[automerger skipped] Merge "DO NOT MERGE Track TZDB 2022a rev. 2 changes. [O-MR1 CTS]" into oreo-mr1-cts-dev am: c44612b872 -s ours am: fb7b265b8a -s ours am: 3ab951b642 -s ours am: 0fa08ed68b -s ours am: 4ad86ea4ed -s ours am: 2bfb332fe6 -s ours am: 0b4cf135e7 -s ours

am skip reason: subject contains skip directive

Original change: https://android-review.googlesource.com/c/platform/libcore/+/2194739

Change-Id: I97395a6fd8d37ce05b89b841d1c8d01d8f7ebd96
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index d205aeb..b599141 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,47 @@
 
 package {
     default_visibility: ["//visibility:private"],
+    default_applicable_licenses: ["libcore_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'filegroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// http://go/android-license-faq
+license {
+    name: "libcore_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+        "SPDX-license-identifier-GPL",
+        "SPDX-license-identifier-GPL-2.0",
+        "SPDX-license-identifier-LGPL",
+        "SPDX-license-identifier-MIT",
+        "SPDX-license-identifier-OpenSSL",
+        "SPDX-license-identifier-Unicode-DFS",
+        "SPDX-license-identifier-W3C",
+        "legacy_unencumbered",
+    ],
+    license_text: [
+        "LICENSE",
+        "NOTICE",
+    ],
 }
 
 build = [
@@ -21,18 +62,16 @@
     "NativeCode.bp",
 ]
 
-genrule {
-    name: "notices-for-framework-stubs",
-    visibility: [
-        "//frameworks/base",
-    ],
+java_genrule {
+    name: "notices-for-stubs-jar",
+    tools: ["soong_zip"],
     tool_files: [
         "NOTICE",
         "ojluni/NOTICE",
     ],
-    cmd: "cp -f $(location NOTICE) $(genDir)/NOTICES/libcore-NOTICE && cp -f $(location ojluni/NOTICE) $(genDir)/NOTICES/ojluni-NOTICE",
-    out: [
-        "NOTICES/libcore-NOTICE",
-        "NOTICES/ojluni-NOTICE",
-    ],
+    cmd: "mkdir $(genDir)/NOTICES && " +
+        "cp -f $(location NOTICE) $(genDir)/NOTICES/libcore-NOTICE && " +
+        "cp -f $(location ojluni/NOTICE) $(genDir)/NOTICES/ojluni-NOTICE && " +
+        "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)/NOTICES",
+    out: ["notices-for-stubs.jar"],
 }
diff --git a/JavaLibrary.bp b/JavaLibrary.bp
index 8b0ab3b..dcbff3b 100644
--- a/JavaLibrary.bp
+++ b/JavaLibrary.bp
@@ -146,6 +146,7 @@
             "-Xep:ConstantOverflow:WARN", // Known constant overflow in SplittableRandom
         ],
     },
+    min_sdk_version: "31",
 }
 
 //
@@ -163,12 +164,18 @@
         // Use the source code for the I18N module intra core API as using the
         // compiled version does not work due to limitations in either soong or the javac
         // toolchain. See http://b/142056316 for more details.
-        ":i18n-module-intra-core-api-stubs-source",
+        ":i18n.module.intra.core.api{.public.stubs.source}",
         ":core_oj_java_files",
         ":core_libart_java_files",
+        // framework-api-annotations contain API annotations, e.g. @SystemApi.
+        ":framework-api-annotations",
         ":openjdk_lambda_stub_files",
         ":openjdk_generated_annotation_stub_files",
         ":app-compat-annotations-source",
+
+        // Use the okhttp source too to allow libcore code to reference it
+        // directly.
+        ":okhttp_impl_files",
     ],
 
     sdk_version: "none",
@@ -187,11 +194,18 @@
         "compat-changeid-annotation-processor",
         "unsupportedappusage-annotation-processor",
     ],
+    libs: [
+        "conscrypt.module.intra.core.api",
+    ],
 }
 
 platform_compat_config {
     name: "libcore-platform-compat-config",
     src: ":core-all",
+    visibility: [
+        "//art/build/apex",
+        "//art/build/sdk",
+    ],
 }
 
 // A system modules definition for use by core library targets only. It only
@@ -200,13 +214,15 @@
 // It does not contain other parts of core library like conscrypt, bouncycastle,
 // etc. This system_modules definition is used to bootstrap compilation for
 // other parts of the core library like core-oj, core-libart, conscrypt,
-// bouncycastle, etc.
+// bouncycastle, etc. It is also used to compile Libcore tests, as well as ART
+// Java tests (run-tests).
 java_system_modules {
     name: "core-all-system-modules",
 
     // Visibility is deliberately restricted to a small set of build modules that
     // the core library team control.
     visibility: [
+        "//art/test:__subpackages__",
         "//external/apache-harmony:__subpackages__",
         "//external/apache-xml",
         "//external/okhttp",
@@ -221,11 +237,13 @@
     name: "core-oj",
     visibility: [
         "//art/build/apex",
+        "//art/build/sdk",
         "//external/wycheproof",
         "//libcore/benchmarks",
+        "//packages/modules/ArtPrebuilt",
     ],
     apex_available: [
-        "com.android.art.release",
+        "com.android.art",
         "com.android.art.debug",
     ],
     defaults: ["libcore_java_defaults"],
@@ -258,6 +276,9 @@
 
     notice: "ojluni/NOTICE",
 
+    hiddenapi_additional_annotations: [
+        "core-oj-hiddenapi-annotations",
+    ],
 }
 
 // Contains parts of core library not associated with OpenJDK. Contains not
@@ -267,13 +288,13 @@
     name: "core-libart",
     visibility: [
         "//art/build/apex",
-        "//external/robolectric-shadows",
+        "//art/build/sdk",
         "//external/wycheproof",
         "//libcore/benchmarks",
-        "//frameworks/layoutlib",
+        "//packages/modules/ArtPrebuilt",
     ],
     apex_available: [
-        "com.android.art.release",
+        "com.android.art",
         "com.android.art.debug",
     ],
     defaults: ["libcore_java_defaults"],
@@ -307,19 +328,30 @@
                 "tz_version_host_tzdata_apex",
             ],
         },
+        darwin: {
+            enabled: false,
+        },
     },
 }
 
+// Java library for use on host, e.g. by robolectric or layoutlib.
+java_library {
+    name: "core-libart-for-host",
+    visibility: [
+        "//art/build/sdk",
+        "//external/robolectric-shadows",
+        "//frameworks/layoutlib",
+    ],
+    static_libs: [
+        "core-libart",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+}
+
 // Provided solely to contribute information about which hidden parts of the
 // core-oj API are used by apps.
 //
-// The build system determines that this library provides hiddenapi information
-// for the core-oj bootjar because its name is of the form <x>-hiddenapi, where
-// <x> is the name of a boot jar. That triggers the generation of a flags.csv
-// file which encapsulates information extracted from the UnsupportedAppUsage
-// annotations in the dex. The information from that file is then encoded into
-// the core-oj file.
-//
 // Usually, e.g. for core-libart, the UnsupportedAppUsage annotations are
 // added to the source that is compiled directly into the bootjar and the build
 // system extracts the information about UnsupportedAppUsage directly from
@@ -331,7 +363,7 @@
 // difficult to pull down changes from upstream.
 //
 java_library {
-    name: "core-oj-hiddenapi",
+    name: "core-oj-hiddenapi-annotations",
     // Do not allow this to be accessed from outside this directory.
     visibility: ["//visibility:private"],
     defaults: ["libcore_java_defaults"],
@@ -345,40 +377,6 @@
     plugins: ["unsupportedappusage-annotation-processor"],
 }
 
-//
-// Guaranteed unstripped versions of core-oj and core-libart.
-//
-// The build system may or may not strip the core-oj and core-libart jars,
-// but these will not be stripped. See b/24535627.
-//
-
-java_library {
-    name: "core-oj-testdex",
-    installable: true,
-    static_libs: ["core-oj"],
-    sdk_version: "none",
-    system_modules: "core-all-system-modules",
-    dxflags: ["--core-library"],
-    dex_preopt: {
-        enabled: false,
-    },
-    java_version: "1.9",
-    notice: "ojluni/NOTICE",
-}
-
-java_library {
-    name: "core-libart-testdex",
-    installable: true,
-    static_libs: ["core-libart"],
-    sdk_version: "none",
-    system_modules: "core-all-system-modules",
-    dxflags: ["--core-library"],
-    dex_preopt: {
-        enabled: false,
-    },
-    notice: "ojluni/NOTICE",
-}
-
 java_defaults {
     name: "core_lambda_stubs_defaults",
     defaults: ["libcore_java_defaults"],
@@ -453,12 +451,13 @@
     name: "core-test-rules",
     visibility: [
         "//art/build/sdk",
+        "//cts/tests/tests/util",
         "//external/conscrypt",
         "//external/conscrypt/apex/tests",
         "//frameworks/base/location/tests/locationtests",
         "//frameworks/base/core/tests/coretests",
         "//frameworks/base/wifi/tests",
-        "//cts/tests/tests/util",
+        "//packages/modules/Wifi/framework/tests",
     ],
     hostdex: true,
     srcs: [
@@ -509,6 +508,7 @@
         "//libcore/benchmarks",
         "//packages/apps/KeyChain/tests",
         "//system/timezone/distro/core",
+        "//packages/modules/Connectivity/tests:__subpackages__",
     ],
     hostdex: true,
     srcs: ["support/src/test/java/**/*.java"],
@@ -526,6 +526,7 @@
 java_test {
     name: "jsr166-tests",
     visibility: [
+        "//art/build/sdk",
         "//cts/tests/libcore/jsr166",
     ],
     srcs: ["jsr166-tests/src/test/java/**/*.java"],
@@ -552,10 +553,19 @@
     tools: ["smali"],
 }
 
+filegroup {
+    name: "core-ojtests-javax-resources",
+    // Set path to keep the resources and .class files in the same directory in the jar file.
+    path: "ojluni/src",
+    srcs: ["ojluni/src/test/javax/**/*"],
+    exclude_srcs: ["ojluni/src/test/javax/**/*.java"],
+}
+
 // Builds the core-tests library.
 java_test {
     name: "core-tests",
     visibility: [
+        "//art/build/sdk",
         "//cts/tests/libcore/luni",
     ],
     defaults: ["libcore_java_defaults"],
@@ -585,6 +595,7 @@
     ],
     exclude_java_resource_dirs: [
         "ojluni/src/test/java",
+        "ojluni/src/test/javax",
         "ojluni/src/test/resources",
     ],
 
@@ -615,7 +626,6 @@
         "nist-pkix-tests",
         "slf4j-jdk14",
         "sqlite-jdbc",
-        "tzdata-testing",
         "truth-prebuilt-jar",
     ],
 
@@ -640,11 +650,9 @@
         "harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/*.java",
         "luni/src/test/java/libcore/javax/crypto/**/*.java",
         "luni/src/test/java/libcore/javax/net/ssl/**/*.java",
+        "luni/src/test/java/libcore/libcore/util/SerializationTester.java",
         "luni/src/test/java/org/apache/harmony/crypto/**/*.java",
     ],
-    exclude_srcs: [
-        "luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/SealedObjectTest.java",
-    ],
 
     java_resource_dirs: [
         "luni/src/test/java",
@@ -671,12 +679,17 @@
 
     srcs: [
         "ojluni/src/test/java/**/*.java",
+        "ojluni/src/test/javax/**/*.java",
     ],
     java_resource_dirs: [
         "ojluni/src/test/java",
         "ojluni/src/test/resources",
     ],
 
+    java_resources: [
+        ":core-ojtests-javax-resources",
+    ],
+
     sdk_version: "none",
     system_modules: "core-all-system-modules",
     libs: [
@@ -684,7 +697,10 @@
         "bouncycastle",
     ],
 
-    static_libs: ["testng"],
+    static_libs: [
+        "junit",
+        "testng",
+    ],
 
     // ojluni/src/test/java/util/stream/{bootlib,boottest}
     // contains tests that are in packages from java.base;
@@ -708,11 +724,13 @@
 java_test {
     name: "core-ojtests-public",
     visibility: [
+        "//art/build/sdk",
         "//cts/tests/libcore/ojluni",
     ],
     defaults: ["libcore_java_defaults"],
     srcs: [
         "ojluni/src/test/java/**/*.java",
+        "ojluni/src/test/javax/**/*.java",
     ],
     // Filter out the following:
     // 1.) DeserializeMethodTest and SerializedLambdaTest, because they depends on stub classes
@@ -731,10 +749,15 @@
         "ojluni/src/test/dist",
     ],
 
+    java_resources: [
+        ":core-ojtests-javax-resources",
+    ],
+
     sdk_version: "none",
     system_modules: "core-all-system-modules",
     libs: [
         "bouncycastle",
+        "junit",
         "okhttp",
         "testng",
     ],
@@ -744,6 +767,9 @@
 // available to metalava. Used for nullability annotations in OpenJDK source.
 droiddoc_exported_dir {
     name: "ojluni-annotated-sdk-stubs",
+    visibility: [
+        "//libcore:__subpackages__",
+    ],
     path: "ojluni/annotations/sdk",
 }
 
@@ -811,7 +837,7 @@
     system_modules: "none",
 }
 
-// A special set of system modules needed to build art.module.public.api.stubs
+// A special set of system modules needed to build art.module.public.api
 // that contain nullability annotations when targeting java language level 1.9
 // and above.
 java_system_modules {
@@ -840,47 +866,85 @@
     // replacement (with $1, $2 backreferences to the regex groups)
     "'$$1https://docs.oracle.com/javase/8/docs/$$2\">' "
 
-// Generates stubs for the parts of the public SDK API provided by the ART module.
-//
-// Only for use by art.module.public.api.stubs target below and the building of the
-// public API.
-droidstubs {
-    name: "art-module-public-api-stubs-source",
+java_library {
+    name: "framework-api-annotations-lib",
+    srcs: [":framework-api-annotations"],
+    sdk_version: "none",
+    patch_module: "java.base",
+    system_modules: "core-all-system-modules",
+    installable: false,
     visibility: [
+        "//visibility:private",
+    ],
+}
+
+// Define the public SDK API provided by the ART module.
+java_sdk_library {
+    name: "art.module.public.api",
+    visibility: [
+        "//art/build/sdk",
+        "//libcore/mmodules/core_platform_api",
         "//frameworks/base",
+        "//frameworks/base/api",
     ],
     srcs: [
         ":core_oj_api_files",
         ":core_libart_api_files",
+
+        // Some source files in :core_oj_api_files and :openjdk_mmodule_extra_files are
+        // annotated by applying annotations to the .annotated.java stubs files in
+        // ojluni/annotated/mmodules and rather than in the original source. See the comments
+        // in openjdk_java_files.bp for more details.
+        ":openjdk_mmodule_extra_files",
+        ":okhttp_api_files",
+
     ],
-    java_version: "1.9",
-    installable: false,
-    sdk_version: "none",
-    system_modules: "java-current-stubs-system-modules",
     libs: [
+        // Put framework-api-annotations into libs to avoid exposing the definition of framework's
+        // annotations from libcore (wrong place) instead of framework (correct place).
+        "framework-api-annotations-lib",
         // Provide access to I18N constants that are used to initialize
         // constants in the public API. i.e. to allow the value of the
         // java.text.CollectionElementIterator.NULLORDER to be initialized from
         // android.icu.text.CollationElementIterator.NULLORDER.
         "i18n.module.intra.core.api.stubs",
     ],
+    stub_only_static_libs: ["notices-for-stubs-jar"],
+    java_version: "1.9",
 
-    args: rewrite_openjdk_doc_args,
+    // Make dex jars for the stubs available for use by hiddenapi processing.
+    compile_dex: true,
 
-    create_doc_stubs: true,
+    public: {
+        enabled: true,
+    },
+    system: {
+        enabled: true,
+    },
+    module_lib: {
+        enabled: true,
+    },
 
+    api_only: true,
+    droiddoc_options: [
+        rewrite_openjdk_doc_args,
+        "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* ",
+        "--hide-annotation libcore.api.Hide",
+    ],
+
+    merge_inclusion_annotations_dirs: ["ojluni-annotated-mmodule-stubs"],
     // Emit nullability annotations from the source to the stub files.
     annotations_enabled: true,
 
     merge_annotations_dirs: [
+        "metalava-manual",
         "ojluni-annotated-sdk-stubs",
     ],
-}
 
-// A stubs target containing the parts of the public SDK API provided by the ART module.
-java_library {
-    name: "art.module.public.api.stubs",
-    srcs: [":art-module-public-api-stubs-source"],
+    doctag_files: [
+        ":known-oj-tags",
+    ],
+
     errorprone: {
         javacflags: [
             "-Xep:MissingOverride:OFF",
@@ -889,6 +953,13 @@
     patch_module: "java.base",
     sdk_version: "none",
     system_modules: "java-current-stubs-system-modules",
+    // The base name for the artifacts that are automatically published to the
+    // dist and which end up in one of the sub-directories of prebuilts/sdk.
+    // As long as this matches the name of the artifacts in prebuilts/sdk then
+    // the API will be checked for compatibility against the latest released
+    // version of the API.
+    dist_stem: "art",
+    dist_group: "android",
 }
 
 // Used when compiling higher-level code against art.module.public.api.stubs.
@@ -927,6 +998,22 @@
     ],
 }
 
+// Used when compiling higher-level code against art.module.public.api.stubs.module_lib.
+//
+// This is only intended for use within core libraries and must not be used
+// from outside.
+java_system_modules {
+    name: "art-module-lib-api-stubs-system-modules",
+    visibility: [
+        "//art/build/sdk",
+        "//external/conscrypt",
+        "//external/icu/android_icu4j",
+    ],
+    libs: [
+        "art.module.public.api.stubs.module_lib",
+    ],
+}
+
 // A stubs target containing the parts of the public SDK API provided by the
 // core library.
 //
@@ -986,6 +1073,43 @@
     ],
 }
 
+// A stubs target containing the parts of the public SDK & @SystemApi(MODULE_LIBRARIES) API
+// provided by the core library.
+//
+// Don't use this directly, use "sdk_version: module_current".
+java_library {
+    name: "core.module_lib.stubs",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+
+        // Replace the following with the module-lib correspondence when Conscrypt or i18N module
+        // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
+        // @SystemApi(MODULE_LIBRARIES).
+        "conscrypt.module.public.api.stubs",
+        "i18n.module.public.api.stubs",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    visibility: ["//visibility:private"],
+}
+
+// Used when compiling higher-level code with sdk_version "module_current"
+java_system_modules {
+    name: "core-module-lib-stubs-system-modules",
+    libs: [
+        "core.module_lib.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+    visibility: ["//visibility:public"],
+}
+
 // Target for validating nullability annotations for correctness and
 // completeness. To check that there are no nullability errors:
 //   m art-module-public-api-stubs-nullability-validation
@@ -1014,18 +1138,6 @@
     check_nullability_warnings: "nullability_warnings.txt",
 }
 
-// A host library containing time zone related classes. Used for
-// host-side tools and tests that have to deal with Android
-// time zone data.
-java_library_host {
-    name: "timezone-host",
-    visibility: [
-        "//art/build/sdk",
-        "//system/timezone/distro/core",
-    ],
-    srcs: [":timezone_host_files"],
-}
-
 // A special set of system modules for building the following library for use
 // in the art-module-public-api-system-modules.
 java_system_modules {
@@ -1057,6 +1169,8 @@
 java_library {
     name: "art.module.api.annotations",
     visibility: [
+        "//art/build/sdk",
+        "//external/icu/android_icu4j",
         "//frameworks/base",
     ],
     host_supported: true,
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..6d8601b
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: RESTRICTED
+}
diff --git a/NativeCode.bp b/NativeCode.bp
index 9619420..a7f18ea 100644
--- a/NativeCode.bp
+++ b/NativeCode.bp
@@ -20,6 +20,7 @@
 
 cc_defaults {
     name: "core_native_default_flags",
+    defaults: ["art_module_source_build_defaults"],
     host_supported: true,
     cflags: [
         "-Wall",
@@ -33,25 +34,28 @@
             enabled: false,
         },
     },
+    min_sdk_version: "S",
 }
 
 cc_defaults {
     name: "core_native_default_libs",
-
+    header_libs: ["jni_headers"],
     shared_libs: [
         "libbase",
         "liblog",
         "libnativehelper",
     ],
+    static_libs: ["libnativehelper_compat_libc++"],
 }
 
 cc_library_shared {
     name: "libjavacore",
     visibility: [
         "//art/build/apex",
+        "//art/runtime",
     ],
     apex_available: [
-        "com.android.art.release",
+        "com.android.art",
         "com.android.art.debug",
     ],
     defaults: [
@@ -60,47 +64,29 @@
     ],
     srcs: [
         ":luni_native_srcs",
-        "dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp",
     ],
     shared_libs: [
         "libandroidio",
-        "libbase",
         "libcrypto",
+        "libicu",
         "libexpat",
-        "libicuuc",
-        "libicui18n",
-        "libnativehelper",
         "libz",
     ],
     static_libs: [
-        "libandroidicuinit",
         "libziparchive",
     ],
-    target: {
-        android: {
-            cflags: [
-                // -DANDROID_LINK_SHARED_ICU4C to enable access to the full ICU4C.
-                // See external/icu/android_icu4c/include/uconfig_local.h
-                // for more information.
-                "-DANDROID_LINK_SHARED_ICU4C",
-            ],
-        },
-    },
 }
 
 cc_library_shared {
     name: "libandroidio",
     visibility: [
         "//art/build/apex",
+        "//art/build/sdk",
         "//external/conscrypt",
     ],
     apex_available: [
-        "com.android.art.release",
+        "com.android.art",
         "com.android.art.debug",
-        // TODO(b/147813447) remove this. This is currently due to the 'runtime_libs'
-        // dependency from libjavacrypto in the conscrypt APEX.
-        "com.android.conscrypt",
-        "test_com.android.conscrypt",
     ],
     defaults: [
         "core_native_default_flags",
@@ -141,11 +127,12 @@
     shared_libs: [
         "libandroidio",
         "libcrypto",
-        "libicuuc",
-        "libnativehelper",
+        "libicu",
         "libz",
     ],
-    static_libs: ["libfdlibm"],
+    static_libs: [
+        "libfdlibm",
+    ],
 
     target: {
         linux_glibc: {
@@ -157,12 +144,6 @@
             ],
         },
         android: {
-            cflags: [
-                // -DANDROID_LINK_SHARED_ICU4C to enable access to the full ICU4C.
-                // See external/icu/android_icu4c/include/uconfig_local.h
-                // for more information.
-                "-DANDROID_LINK_SHARED_ICU4C",
-            ],
             shared_libs: [
                 "libdl_android",
             ],
@@ -178,7 +159,7 @@
         "//art/build/apex",
     ],
     apex_available: [
-        "com.android.art.release",
+        "com.android.art",
         "com.android.art.debug",
     ],
     defaults: ["libopenjdk_native_defaults"],
@@ -219,14 +200,10 @@
         "luni/src/test/native/libcore_java_nio_BufferTest.cpp",
         "luni/src/test/native/libcore_libcore_util_NativeAllocationRegistryTest.cpp",
     ],
-    target: {
-        android: {
-            shared_libs: ["libnativehelper_compat_libc++"],
-        },
-        host: {
-            shared_libs: ["libnativehelper"],
-        },
-    },
+    shared_libs: [
+        "liblog",
+        "libnativehelper",
+    ],
 
     strip: {
         keep_symbols: true,
@@ -238,12 +215,26 @@
     name: "libjavacore-unit-tests",
     defaults: ["core_native_default_flags"],
 
+    // TODO(b/172480617): Fix this source dependency from
+    // platform_testing/build/tasks/tests/native_test_list.mk.
+    enabled: true,
+
     // Add -fno-builtin so that the compiler doesn't attempt to inline
     // memcpy calls that are not really aligned.
     cflags: ["-fno-builtin"],
-    srcs: ["luni/src/test/native/libcore_io_Memory_test.cpp"],
+    srcs: [
+        "luni/src/test/native/libcore_io_Memory_test.cpp",
+        // libcore_io_Memory_test.cpp includes libcore_io_Memory.cpp which
+        // depends on JniConstants.cpp (but these are not used in the tests).
+        "luni/src/main/native/JniConstants.cpp",
+    ],
 
-    shared_libs: ["libnativehelper"],
+    shared_libs: [
+        "liblog",
+        "libnativehelper",
+    ],
+
+    static_libs: ["libnativehelper_compat_libc++"],
 }
 
 // Set of benchmarks for libjavacore functions.
@@ -251,8 +242,20 @@
     name: "libjavacore-benchmarks",
     defaults: ["core_native_default_flags"],
 
-    srcs: ["luni/src/benchmark/native/libcore_io_Memory_bench.cpp"],
+    // TODO(b/172480617): Fix this source dependency from
+    // platform_testing/build/tasks/tests/native_metric_test_list.mk.
+    enabled: true,
+
+    srcs: [
+        "luni/src/benchmark/native/libcore_io_Memory_bench.cpp",
+        // libcore_io_Memory_bench.cpp includes libcore_io_Memory.cpp which
+        // depends on JniConstants.cpp (but these are not used in the benchmark).
+        "luni/src/main/native/JniConstants.cpp",
+    ],
     test_suites: ["device-tests"],
 
-    shared_libs: ["libnativehelper"],
+    shared_libs: [
+        "liblog",
+        "libnativehelper",
+    ],
 }
diff --git a/OWNERS b/OWNERS
index 174fb87..56d718b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -6,17 +6,16 @@
 # People who can approve changes for submission; don't send review emails to them
 # unless you know what you're doing.
 dauletz@google.com
-narayan@google.com
+mingaleev@google.com
 nfuller@google.com
 nikitai@google.com
 paulduffin@google.com
-peteg@google.com
 prb@google.com
-tobiast@google.com
+rpl@google.com
 vichang@google.com
 
 # Don't send these folks any review emails for this project.
+calin@google.com
+narayan@google.com
 ngeoffray@google.com
-sehr@google.com
 vmarko@google.com
-dbrazdil@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index f8c149d..f399594 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,7 +1,90 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
-      "name": "CtsSaxTestCases"
+      "name": "CtsLibcoreTestCases",
+      "options": [
+        {
+          "exclude-filter": "com.android.org.conscrypt.java.security.AlgorithmParameterGeneratorTestDH"
+        },
+        {
+          "exclude-filter": "com.android.org.conscrypt.java.security.KeyPairGeneratorTest"
+        },
+        {
+          "exclude-filter": "com.android.org.conscrypt.java.security.SignatureTest"
+        },
+        {
+          "exclude-filter": "com.android.org.conscrypt.javax.crypto.CipherBasicsTest"
+        },
+        {
+          "exclude-filter": "com.android.org.conscrypt.javax.net.ssl.KeyManagerFactoryTest"
+        },
+        {
+          "exclude-filter": "com.android.org.conscrypt.javax.net.ssl.SSLSocketVersionCompatibilityTest"
+        },
+        {
+          "exclude-filter": "libcore.java.lang.OldRuntimeTest"
+        },
+        {
+          "exclude-filter": "libcore.java.lang.OldThreadTest"
+        },
+        {
+          "exclude-filter": "libcore.java.lang.ref.FinalizeTest"
+        },
+        {
+          "exclude-filter": "libcore.java.math.BigIntegerTest"
+        },
+        {
+          "exclude-filter": "libcore.java.net.ConcurrentCloseTest"
+        },
+        {
+          "exclude-filter": "libcore.java.net.OldSocketTest"
+        },
+        {
+          "exclude-filter": "libcore.java.net.URLConnectionTest"
+        },
+        {
+          "exclude-filter": "libcore.java.nio.channels.DatagramChannelMulticastTest"
+        },
+        {
+          "exclude-filter": "libcore.libcore.net.NetworkSecurityPolicyTest"
+        },
+        {
+          "exclude-filter": "libcore.libcore.util.NativeAllocationRegistryTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.logging.tests.java.util.logging.SocketHandlerTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.lang.ObjectTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.lang.ProcessManagerTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.math.OldBigIntegerTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.net.InetAddressThreadTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.nio.channels.DatagramChannelTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.util.ScannerTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.util.TimerTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.java.util.WeakHashMapTest"
+        },
+        {
+          "exclude-filter": "org.apache.harmony.tests.javax.net.ssl.SSLSessionTest"
+        },
+        {
+          "exclude-filter": "tests.java.sql.StressTest"
+        }
+      ]
     }
   ]
-}
+}
\ No newline at end of file
diff --git a/apex/Android.bp b/apex/Android.bp
deleted file mode 100644
index 51a7335..0000000
--- a/apex/Android.bp
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// i18n APEX is intended as a home for ICU as of July 2019.
-// Various parts and dependencies of ICU would be gradually moved into this APEX,
-// and the below build rules should reflect the latest status.
-// TODO(b/138434658): Move the apex declaration back to external/icu
-// The apex is declared in libcore/ to workaround new dependency from external/icu
-// to system/sepolicy in a downstream branch.
-apex {
-    name: "com.android.i18n",
-    defaults: ["com.android.i18n-defaults"],
-    certificate: ":com.android.i18n.certificate",
-}
-
-apex_defaults {
-    name: "com.android.i18n-defaults",
-    compile_multilib: "both",
-    manifest: "manifest.json",
-    prebuilts: ["apex_icu.dat"],
-    key: "com.android.i18n.key",
-}
-
-apex_key {
-    name: "com.android.i18n.key",
-    public_key: "com.android.i18n.avbpubkey",
-    private_key: "com.android.i18n.pem",
-}
-
-android_app_certificate {
-    name: "com.android.i18n.certificate",
-    certificate: "com.android.i18n",
-}
diff --git a/apex/com.android.i18n.avbpubkey b/apex/com.android.i18n.avbpubkey
deleted file mode 100644
index f1c97a0..0000000
--- a/apex/com.android.i18n.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/apex/com.android.i18n.pem b/apex/com.android.i18n.pem
deleted file mode 100644
index 7676453..0000000
--- a/apex/com.android.i18n.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKwIBAAKCAgEA89VXuQrlqB0L731Es2ud65k5XkyGn0FjQfynPYRPz3nkpNaF
-3LbJ6JXIGKUQMY2pVQFNl2wUtylTJohNv8SWu/hPBMOmHu2zGvvfErKN7QD25y12
-D9jws3VlPhCWQsEZSjloYKI1MvMPcvtLgHoSuMl6JlT8mngC+uR8WLGtDKZYGICf
-I2rhQ5ugzDp00tGJJV9HKAXHx7zA9X+29HbMte57ibOrO4Xs7zLankV3rVEc0ZcD
-phA1RYh6lg9KYfsq/zbEbCRGPLTlqKa3zDPrnXTKInR//5fW7tWgqqRyXGumM8pA
-B1Huba878WnMwcWWYLjxRMQ6hOOCnGY7167JoAnX+NRmUZWXrrSiaCr6o61ingb6
-6BvQTN0f1q2lXIf1bOPRkeNkxBB7q3YFa4XaF9ZpQs7V4vlrvE/uLWDmhTQ6snML
-zWYbD+/nQ6u5BIdh+1rNk5RWp53IoNTGyJQnTpSkA2u+rtDuyKG4av6Vx2cPJ5Mu
-8vAALAyEOqlZr0CeJR0h3m6XVt0PpDxNUvQU53tlzIKD2BuELWeRSJ79DkYf+Soh
-vc0upDbdsy1r4C/Df5UpKnOKCmrYmJ1tdiigVeDH3y0B0ilD2dUqubqlc5Tw0a13
-PkpWPdJV7QpTyb3GDWa4DRpYRxCGDylCsS2rsXbsiMGqJFL3G0w06ahTmFUCAwEA
-AQKCAgEA4nVbQsXHI8rOYgADBWxGwCCPsm/6fABbslZ38N9ozHYWD64ZpzKw9W3e
-6FytXIiIIyXRrXe5CZ+81UW0iA2KPUvR/8fCCmmTddVFifUBcYP6zBxh0TgX3WSD
-wg/frmHvHguRUGZ2aDpsN8sChXFa3/pnkyBNdx1NDz2T60AhS2VW3nLe2iMS0hrB
-Tcxg4cevy2DhEl/D+1LoF1olTojjeXjjjbGrr92jf0jI2EQGcZaq4FGUFvBouYqW
-57WNzNBcT6nK5fEOtqT/wxIMFACIrLViYnu2wWiBO1J3JOUUPZfRRbpqmyHSAlbE
-omMzwyfCkVRS6B4jh3ZRwPYGUDvKjyNLeXXEr7Q6sAiwhvgz8Z5DIyzZr9SkExuQ
-5jfjfYCNsFaA/QmA34hV5KsPLfLFbW3hvDc6ApOn3yvOkIP3Cb2JM/mfyHh7cRpz
-y/UTep2e3uNj7fDkdrqp2oj/EOSKa0KSJCpqkgUwEFFCEvxeYeUgwlM+axSZNXEx
-ocRgQpui/2tVH8rgny3ayYDknleVaWtzxg7QLH7vV90ztjh4XjmZgl4R6N6vO1IT
-G2XN05vySoE+8ZdQVz3Ko3w3NLon4gwyXv8K+PmHBUsIoY37OPpZoj24n/Uajcuh
-crr5zSGlXhC+OzNk1tJ1NosHF70b/sYwdSIRpxEzA7QggSi6iAUCggEBAP5yHpkr
-BZB/C95zT4xd8ugYAw/5rFQXgwYkf1CYUp/lEXBUJGnimIwo5Gr9tGAAxTwXTTJH
-TI25EhAFu/WEh4h31SkL2b3S6jqupCqYwZdrl5TAY/35FBpGZN+ZirTHPP6ZobxX
-8zhHTl+7mKN/neA6sbLwH4WDZa+LUHScTnVSjzyL2PorPdsolFjOLILQE6LZjinB
-5bX972wjK4Q72gfdkia/Csq8Wo2/k53A2ur0fFojRHHI1M9Fthe6OI4RX/RwyHih
-R9S68xrz2bcYQ8XpoaaNpLY/FXev8WcBicjApN+eQQCK2ZGA9+UxgQRld317Wvqf
-zAyKp5idtRJUbb8CggEBAPVSoNyrghZFfaWGDe3pCnKkeZhqNsLbI5YrQohX6Quy
-BhRCP5t5wyNuMQX5rRZqeb3VgbW/06faVwZZ+bwA0KT7RYy0zgJhfD6bZ7WmzQMc
-HmAVqvN8iZIIjpMeN/174yYdqq0ShUYmra151ReL/CWs32kBWNKT6l487J7DAoYR
-ZHtec9nbs2ZFqea+PNAP8VAcsmsemwXe1w63RxDgdZccJmMj0TWmBj47RstMWTLE
-CrCGfXW9qmvs/Itzbuy589DS0AXZfTW+U0TD4RnCcjV7JDyEZASA3V7lMiyKXDTJ
-6+CUcgzOWzdTfAeVcG0olXXNCdCePX8uiqkwSCaXpusCggEBAI4tHkPf8kAHfY5T
-SIPaizx9DlkC3fQvHxtzkWBrfN+zk8b8fUxdPXgz8U6HbR6nz44ARzZs+K3IV/tz
-+M77uu/aZdWFtamIDTG1HC5hJOuDRzPrPPRRFZaI9xyqIwNYwRBSsDkZu+IalgSQ
-Qn45dPIyWdDus+5auZsZcV93Z6/O7hKa4icHuoyXZC0rJ1wBALficLmMitrihcIa
-9Nnyx6XVfTEBVvppvP+vqMBhXvIiosmLI1ehLKiU/2bKu4dG1iM0UhB1rjmELQtG
-bsUMXfJc1eLHCt566XfzbCRui5sNahM5zoCLFX9kXSBIRRs7x0TqhK++Uro/T97L
-YL5ZRukCggEBAOaEpB93EZ/34F7/HmumBWlAX/n2JErpPAFJ2RTg9l1FBS1YKwjf
-W5wZWPtyZ1Ce8JKO43lzLWGWaxvOxDoC0guVCP90jffyvprd0JACkrYPYAONmLt/
-FI4ieEaJqLcKCKGyUsSamJ0Yjy5pQvEDWwXT8YJr/5iv4RR4TyfHusFb6n16fYYD
-SgoZ/9KQg/hGYsySipzZf3X+tTpgweh74kMB8phJ+bZdsZQcgyNZNJ/dUuYZGh7f
-ABq174DiESNkgFSDI3G7skoj8360SPq5mjPi6GPtS0ZoCJu45nKv+ICqFHlNQ/YA
-mfnc+rjtlV0dO4QcDNL5PnQZubXNZp7M9c0CggEBAIrfxdeELSqeVld3nhHFty0b
-ZdmFJWRj6k9zjoOYe8LPkAiue0Zm10sCiLFiRgTH6msnfBloYJ2iiDgSSnIlUvaH
-HFxoLWtv2az2B3g6gfJ5KbdE8mqfz0hGMOUBPFBarC4HrPslR28Cwn8mD5CvN4a/
-oAz3kOlaGokVF6Ikwd606JBFTVKSwr2niTJUwGXgn85tViCWqUm4v24BntbZZ9Rs
-k6ffBUl/k02zw/U405I3qTjCAnetNY3wHmjvdqkCcoWTAmG04IfGlqVE3NMjjLQD
-h9Xx2Nr5SV6djWbmYfT0Ox4Ha/IPuCCAZbBv6Or2wQSOQwb5fUgFcu3NrXU1rQQ=
------END RSA PRIVATE KEY-----
diff --git a/apex/com.android.i18n.pk8 b/apex/com.android.i18n.pk8
deleted file mode 100644
index 100ed14..0000000
--- a/apex/com.android.i18n.pk8
+++ /dev/null
Binary files differ
diff --git a/apex/com.android.i18n.x509.pem b/apex/com.android.i18n.x509.pem
deleted file mode 100644
index fcf1a53..0000000
--- a/apex/com.android.i18n.x509.pem
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFUTCCAzmgAwIBAgIUHufr9X4LD87MyuJ/8AaMvsIhXjUwDQYJKoZIhvcNAQEL
-BQAwNzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmcm9uaWExEzARBgNVBAoM
-Ckdvb2dsZSBMTEMwIBcNMTkwNzA1MTU0ODM2WhgPNDc1NzA1MzExNTQ4MzZaMDcx
-CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZnJvbmlhMRMwEQYDVQQKDApHb29n
-bGUgTExDMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsUMFBc1QNyhj
-gU47F2O3kUrZhATA6Gz644sXvrqUg03retG3jsIEFmBXgbXngvF2yq9sRiGm4D5j
-9Sf5fguDqy9C/Q8MiUEAqQo1ygy9MubMfdGVLb2dcFoKXwyig8KzSS03+73IiUnF
-iCy5GzPO6ltga4nsbXUE6egexE3WGLQ0Lu7gCLNZn1Zd20p4BlTUl64CEu9zt6NN
-D6okd7IGceipBbpK9WDMW6ygXJ5sTJohZYSrSOPkoQFVilWFd5VryBxboTmxhSyo
-4dvNavnNCKOhsCCi8QIHOlXMDWxN970giv60FzoXoD2QwD5o+3I6SIIutEnGmh/d
-SECcK9yK+YECqw2XhvoBR4NH4RltaWKZsy62YPSYZXJXZ4H1P+9L9Q5dwLlI2Itt
-IWtYY88T8P2w8e2KzYewkE/XM4kRUw8pNWod1Hok2WuvZxdbPJ0+Hhvo1FVTwV1K
-kdtUuVPut6fpA2hEfGmwdKpK18Qe5ZLBZ1r1lSrjlfct+BWelADaZRNNyJey0u/s
-DgZGbiJG6tJqDESN229/eMTRt9gDncIx7S37RFgOv8jlUul5miKH3QP6neotofWw
-EWEKFjaDNhqU83rdpk48Br/HTiXA6pvaGlN0RQyBVB7uavvTYKTp8cGU11hkIx8p
-Vkp0EOCs9ajlt1+GyMCrPOk8A10rrOcCAwEAAaNTMFEwHQYDVR0OBBYEFMXzY0Gf
-eAdY8RjzwPnOou8VgjD3MB8GA1UdIwQYMBaAFMXzY0GfeAdY8RjzwPnOou8VgjD3
-MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBABrAExrvMkg26gQg
-rUh170Ms8T5jvIpWyIb7fSLjsA0c2q+zZiIUViO1t2eNQtdExGcmEOBF1+LqAIbT
-zA8nz9ZHweGZi01VodPaGKuz2VWWMzQaENW2RQGaxO9g8nCHGrbwchE7IbNOT1FD
-jzVYWcCKydr4niJWjSy9ey8eNDv/Iqto64/nPI7PxtBVfH7G8hgamyVtwWhhciTH
-nlg6X0EAxq5VwZSK4G8n+gsozPQHRjvBrSzt0A187vh/g6LQPLDZjPnzbZVtfFtM
-pv+hImxyDN3DZYrna2aGxWBIXEFjH61xueBGWaCo8TwvR8NIHM/i4s/zyhUxGUYL
-307i940cEF3rYJ/urzZzXJlxiu8p0Y12i/t+vXRqXM8d65QSMLqDDBHqZ3mPkcBp
-U7hWVi5HuyWGQF4FMK8sdemJITJpg1xztybsvRJ9kDeWxPNQllC4dREaKVhI/3WM
-eP7bpMmZxngoHbAB2f11lQf1A0fWFkng/Gntqnw0Dd6J+++NuuoFUjZLAWzUpoje
-J6J9CuxBjng0n94ZN+E8E5zsAtaO9WvhSQwTvNbHZmERxvtSm6mIH1XEBTi5tOHZ
-PBAAJuzZrFFZWevI44vgpCrF4lO3F9qE+sKn+2sARGuAjGCNV+jJCD+7tySjf7js
-2uaWgWOl7EexciIHD9FbZ3ANsiRU
------END CERTIFICATE-----
diff --git a/apex/manifest.json b/apex/manifest.json
deleted file mode 100644
index 4d1b2b6..0000000
--- a/apex/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "com.android.i18n",
-  "version": 1
-}
\ No newline at end of file
diff --git a/api/current.txt b/api/current.txt
new file mode 100755
index 0000000..5276996
--- /dev/null
+++ b/api/current.txt
@@ -0,0 +1,20388 @@
+// Signature format: 2.0
+package android.system {
+
+  public final class ErrnoException extends java.lang.Exception {
+    ctor public ErrnoException(String, int);
+    ctor public ErrnoException(String, int, Throwable);
+    method @NonNull public java.io.IOException rethrowAsIOException() throws java.io.IOException;
+    method @NonNull public java.net.SocketException rethrowAsSocketException() throws java.net.SocketException;
+    field public final int errno;
+  }
+
+  public class Int64Ref {
+    ctor public Int64Ref(long);
+    field public long value;
+  }
+
+  public final class Os {
+    method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+    method public static boolean access(String, int) throws android.system.ErrnoException;
+    method public static void bind(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static void bind(@NonNull java.io.FileDescriptor, @NonNull java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+    method public static void chmod(String, int) throws android.system.ErrnoException;
+    method public static void chown(String, int, int) throws android.system.ErrnoException;
+    method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static void connect(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static void connect(@NonNull java.io.FileDescriptor, @NonNull java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+    method public static java.io.FileDescriptor dup(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static java.io.FileDescriptor dup2(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static String[] environ();
+    method public static void execv(String, String[]) throws android.system.ErrnoException;
+    method public static void execve(String, String[], String[]) throws android.system.ErrnoException;
+    method public static void fchmod(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static void fchown(java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method public static int fcntlInt(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method public static void fdatasync(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static android.system.StructStat fstat(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static android.system.StructStatVfs fstatvfs(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static void fsync(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static void ftruncate(java.io.FileDescriptor, long) throws android.system.ErrnoException;
+    method public static String gai_strerror(int);
+    method public static int getegid();
+    method public static String getenv(String);
+    method public static int geteuid();
+    method public static int getgid();
+    method public static java.net.SocketAddress getpeername(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static int getpid();
+    method public static int getppid();
+    method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method @NonNull public static android.system.StructTimeval getsockoptTimeval(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method public static int gettid();
+    method public static int getuid();
+    method public static byte[] getxattr(String, String) throws android.system.ErrnoException;
+    method public static String if_indextoname(int);
+    method public static int if_nametoindex(String);
+    method public static java.net.InetAddress inet_pton(int, String);
+    method public static boolean isatty(java.io.FileDescriptor);
+    method public static void kill(int, int) throws android.system.ErrnoException;
+    method public static void lchown(String, int, int) throws android.system.ErrnoException;
+    method public static void link(String, String) throws android.system.ErrnoException;
+    method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static String[] listxattr(String) throws android.system.ErrnoException;
+    method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException;
+    method public static android.system.StructStat lstat(String) throws android.system.ErrnoException;
+    method @NonNull public static java.io.FileDescriptor memfd_create(@NonNull String, int) throws android.system.ErrnoException;
+    method public static void mincore(long, long, byte[]) throws android.system.ErrnoException;
+    method public static void mkdir(String, int) throws android.system.ErrnoException;
+    method public static void mkfifo(String, int) throws android.system.ErrnoException;
+    method public static void mlock(long, long) throws android.system.ErrnoException;
+    method public static long mmap(long, long, int, int, java.io.FileDescriptor, long) throws android.system.ErrnoException;
+    method public static void msync(long, long, int) throws android.system.ErrnoException;
+    method public static void munlock(long, long) throws android.system.ErrnoException;
+    method public static void munmap(long, long) throws android.system.ErrnoException;
+    method public static java.io.FileDescriptor open(String, int, int) throws android.system.ErrnoException;
+    method public static java.io.FileDescriptor[] pipe() throws android.system.ErrnoException;
+    method public static int poll(android.system.StructPollfd[], int) throws android.system.ErrnoException;
+    method public static void posix_fallocate(java.io.FileDescriptor, long, long) throws android.system.ErrnoException;
+    method public static int prctl(int, long, long, long, long) throws android.system.ErrnoException;
+    method public static int pread(java.io.FileDescriptor, java.nio.ByteBuffer, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int pread(java.io.FileDescriptor, byte[], int, int, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int pwrite(java.io.FileDescriptor, java.nio.ByteBuffer, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int pwrite(java.io.FileDescriptor, byte[], int, int, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int read(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int read(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static String readlink(String) throws android.system.ErrnoException;
+    method public static int readv(java.io.FileDescriptor, Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+    method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+    method public static int recvmsg(@NonNull java.io.FileDescriptor, @NonNull android.system.StructMsghdr, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static void remove(String) throws android.system.ErrnoException;
+    method public static void removexattr(String, String) throws android.system.ErrnoException;
+    method public static void rename(String, String) throws android.system.ErrnoException;
+    method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
+    method public static int sendmsg(@NonNull java.io.FileDescriptor, @NonNull android.system.StructMsghdr, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+    method public static int sendto(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int, int, @Nullable java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+    method @Deprecated public static void setegid(int) throws android.system.ErrnoException;
+    method public static void setenv(String, String, boolean) throws android.system.ErrnoException;
+    method @Deprecated public static void seteuid(int) throws android.system.ErrnoException;
+    method @Deprecated public static void setgid(int) throws android.system.ErrnoException;
+    method public static int setsid() throws android.system.ErrnoException;
+    method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException;
+    method public static void setsockoptTimeval(@NonNull java.io.FileDescriptor, int, int, @NonNull android.system.StructTimeval) throws android.system.ErrnoException;
+    method @Deprecated public static void setuid(int) throws android.system.ErrnoException;
+    method public static void setxattr(String, String, byte[], int) throws android.system.ErrnoException;
+    method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException;
+    method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static android.system.StructStat stat(String) throws android.system.ErrnoException;
+    method public static android.system.StructStatVfs statvfs(String) throws android.system.ErrnoException;
+    method public static String strerror(int);
+    method public static String strsignal(int);
+    method public static void symlink(String, String) throws android.system.ErrnoException;
+    method public static long sysconf(int);
+    method public static void tcdrain(java.io.FileDescriptor) throws android.system.ErrnoException;
+    method public static void tcsendbreak(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method public static int umask(int);
+    method public static android.system.StructUtsname uname();
+    method public static void unsetenv(String) throws android.system.ErrnoException;
+    method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
+    method public static int writev(java.io.FileDescriptor, Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
+  }
+
+  public final class OsConstants {
+    method public static boolean S_ISBLK(int);
+    method public static boolean S_ISCHR(int);
+    method public static boolean S_ISDIR(int);
+    method public static boolean S_ISFIFO(int);
+    method public static boolean S_ISLNK(int);
+    method public static boolean S_ISREG(int);
+    method public static boolean S_ISSOCK(int);
+    method public static boolean WCOREDUMP(int);
+    method public static int WEXITSTATUS(int);
+    method public static boolean WIFEXITED(int);
+    method public static boolean WIFSIGNALED(int);
+    method public static boolean WIFSTOPPED(int);
+    method public static int WSTOPSIG(int);
+    method public static int WTERMSIG(int);
+    method public static String errnoName(int);
+    method public static String gaiName(int);
+    field public static final int AF_INET;
+    field public static final int AF_INET6;
+    field public static final int AF_NETLINK;
+    field public static final int AF_PACKET;
+    field public static final int AF_UNIX;
+    field public static final int AF_UNSPEC;
+    field public static final int AF_VSOCK;
+    field public static final int AI_ADDRCONFIG;
+    field public static final int AI_ALL;
+    field public static final int AI_CANONNAME;
+    field public static final int AI_NUMERICHOST;
+    field public static final int AI_NUMERICSERV;
+    field public static final int AI_PASSIVE;
+    field public static final int AI_V4MAPPED;
+    field public static final int ARPHRD_ETHER;
+    field public static final int CAP_AUDIT_CONTROL;
+    field public static final int CAP_AUDIT_WRITE;
+    field public static final int CAP_BLOCK_SUSPEND;
+    field public static final int CAP_CHOWN;
+    field public static final int CAP_DAC_OVERRIDE;
+    field public static final int CAP_DAC_READ_SEARCH;
+    field public static final int CAP_FOWNER;
+    field public static final int CAP_FSETID;
+    field public static final int CAP_IPC_LOCK;
+    field public static final int CAP_IPC_OWNER;
+    field public static final int CAP_KILL;
+    field public static final int CAP_LAST_CAP;
+    field public static final int CAP_LEASE;
+    field public static final int CAP_LINUX_IMMUTABLE;
+    field public static final int CAP_MAC_ADMIN;
+    field public static final int CAP_MAC_OVERRIDE;
+    field public static final int CAP_MKNOD;
+    field public static final int CAP_NET_ADMIN;
+    field public static final int CAP_NET_BIND_SERVICE;
+    field public static final int CAP_NET_BROADCAST;
+    field public static final int CAP_NET_RAW;
+    field public static final int CAP_SETFCAP;
+    field public static final int CAP_SETGID;
+    field public static final int CAP_SETPCAP;
+    field public static final int CAP_SETUID;
+    field public static final int CAP_SYSLOG;
+    field public static final int CAP_SYS_ADMIN;
+    field public static final int CAP_SYS_BOOT;
+    field public static final int CAP_SYS_CHROOT;
+    field public static final int CAP_SYS_MODULE;
+    field public static final int CAP_SYS_NICE;
+    field public static final int CAP_SYS_PACCT;
+    field public static final int CAP_SYS_PTRACE;
+    field public static final int CAP_SYS_RAWIO;
+    field public static final int CAP_SYS_RESOURCE;
+    field public static final int CAP_SYS_TIME;
+    field public static final int CAP_SYS_TTY_CONFIG;
+    field public static final int CAP_WAKE_ALARM;
+    field public static final int E2BIG;
+    field public static final int EACCES;
+    field public static final int EADDRINUSE;
+    field public static final int EADDRNOTAVAIL;
+    field public static final int EAFNOSUPPORT;
+    field public static final int EAGAIN;
+    field public static final int EAI_AGAIN;
+    field public static final int EAI_BADFLAGS;
+    field public static final int EAI_FAIL;
+    field public static final int EAI_FAMILY;
+    field public static final int EAI_MEMORY;
+    field public static final int EAI_NODATA;
+    field public static final int EAI_NONAME;
+    field public static final int EAI_OVERFLOW;
+    field public static final int EAI_SERVICE;
+    field public static final int EAI_SOCKTYPE;
+    field public static final int EAI_SYSTEM;
+    field public static final int EALREADY;
+    field public static final int EBADF;
+    field public static final int EBADMSG;
+    field public static final int EBUSY;
+    field public static final int ECANCELED;
+    field public static final int ECHILD;
+    field public static final int ECONNABORTED;
+    field public static final int ECONNREFUSED;
+    field public static final int ECONNRESET;
+    field public static final int EDEADLK;
+    field public static final int EDESTADDRREQ;
+    field public static final int EDOM;
+    field public static final int EDQUOT;
+    field public static final int EEXIST;
+    field public static final int EFAULT;
+    field public static final int EFBIG;
+    field public static final int EHOSTUNREACH;
+    field public static final int EIDRM;
+    field public static final int EILSEQ;
+    field public static final int EINPROGRESS;
+    field public static final int EINTR;
+    field public static final int EINVAL;
+    field public static final int EIO;
+    field public static final int EISCONN;
+    field public static final int EISDIR;
+    field public static final int ELOOP;
+    field public static final int EMFILE;
+    field public static final int EMLINK;
+    field public static final int EMSGSIZE;
+    field public static final int EMULTIHOP;
+    field public static final int ENAMETOOLONG;
+    field public static final int ENETDOWN;
+    field public static final int ENETRESET;
+    field public static final int ENETUNREACH;
+    field public static final int ENFILE;
+    field public static final int ENOBUFS;
+    field public static final int ENODATA;
+    field public static final int ENODEV;
+    field public static final int ENOENT;
+    field public static final int ENOEXEC;
+    field public static final int ENOLCK;
+    field public static final int ENOLINK;
+    field public static final int ENOMEM;
+    field public static final int ENOMSG;
+    field public static final int ENONET;
+    field public static final int ENOPROTOOPT;
+    field public static final int ENOSPC;
+    field public static final int ENOSR;
+    field public static final int ENOSTR;
+    field public static final int ENOSYS;
+    field public static final int ENOTCONN;
+    field public static final int ENOTDIR;
+    field public static final int ENOTEMPTY;
+    field public static final int ENOTSOCK;
+    field public static final int ENOTSUP;
+    field public static final int ENOTTY;
+    field public static final int ENXIO;
+    field public static final int EOPNOTSUPP;
+    field public static final int EOVERFLOW;
+    field public static final int EPERM;
+    field public static final int EPIPE;
+    field public static final int EPROTO;
+    field public static final int EPROTONOSUPPORT;
+    field public static final int EPROTOTYPE;
+    field public static final int ERANGE;
+    field public static final int EROFS;
+    field public static final int ESPIPE;
+    field public static final int ESRCH;
+    field public static final int ESTALE;
+    field public static final int ETH_P_ALL;
+    field public static final int ETH_P_ARP;
+    field public static final int ETH_P_IP;
+    field public static final int ETH_P_IPV6;
+    field public static final int ETIME;
+    field public static final int ETIMEDOUT;
+    field public static final int ETXTBSY;
+    field public static final int EXDEV;
+    field public static final int EXIT_FAILURE;
+    field public static final int EXIT_SUCCESS;
+    field public static final int FD_CLOEXEC;
+    field public static final int FIONREAD;
+    field public static final int F_DUPFD;
+    field public static final int F_DUPFD_CLOEXEC;
+    field public static final int F_GETFD;
+    field public static final int F_GETFL;
+    field public static final int F_GETLK;
+    field public static final int F_GETLK64;
+    field public static final int F_GETOWN;
+    field public static final int F_OK;
+    field public static final int F_RDLCK;
+    field public static final int F_SETFD;
+    field public static final int F_SETFL;
+    field public static final int F_SETLK;
+    field public static final int F_SETLK64;
+    field public static final int F_SETLKW;
+    field public static final int F_SETLKW64;
+    field public static final int F_SETOWN;
+    field public static final int F_UNLCK;
+    field public static final int F_WRLCK;
+    field public static final int ICMP6_ECHO_REPLY;
+    field public static final int ICMP6_ECHO_REQUEST;
+    field public static final int ICMP_ECHO;
+    field public static final int ICMP_ECHOREPLY;
+    field public static final int IFA_F_DADFAILED;
+    field public static final int IFA_F_DEPRECATED;
+    field public static final int IFA_F_HOMEADDRESS;
+    field public static final int IFA_F_NODAD;
+    field public static final int IFA_F_OPTIMISTIC;
+    field public static final int IFA_F_PERMANENT;
+    field public static final int IFA_F_SECONDARY;
+    field public static final int IFA_F_TEMPORARY;
+    field public static final int IFA_F_TENTATIVE;
+    field public static final int IFF_ALLMULTI;
+    field public static final int IFF_AUTOMEDIA;
+    field public static final int IFF_BROADCAST;
+    field public static final int IFF_DEBUG;
+    field public static final int IFF_DYNAMIC;
+    field public static final int IFF_LOOPBACK;
+    field public static final int IFF_MASTER;
+    field public static final int IFF_MULTICAST;
+    field public static final int IFF_NOARP;
+    field public static final int IFF_NOTRAILERS;
+    field public static final int IFF_POINTOPOINT;
+    field public static final int IFF_PORTSEL;
+    field public static final int IFF_PROMISC;
+    field public static final int IFF_RUNNING;
+    field public static final int IFF_SLAVE;
+    field public static final int IFF_UP;
+    field public static final int IPPROTO_ICMP;
+    field public static final int IPPROTO_ICMPV6;
+    field public static final int IPPROTO_IP;
+    field public static final int IPPROTO_IPV6;
+    field public static final int IPPROTO_RAW;
+    field public static final int IPPROTO_TCP;
+    field public static final int IPPROTO_UDP;
+    field public static final int IPV6_CHECKSUM;
+    field public static final int IPV6_MULTICAST_HOPS;
+    field public static final int IPV6_MULTICAST_IF;
+    field public static final int IPV6_MULTICAST_LOOP;
+    field public static final int IPV6_RECVDSTOPTS;
+    field public static final int IPV6_RECVHOPLIMIT;
+    field public static final int IPV6_RECVHOPOPTS;
+    field public static final int IPV6_RECVPKTINFO;
+    field public static final int IPV6_RECVRTHDR;
+    field public static final int IPV6_RECVTCLASS;
+    field public static final int IPV6_TCLASS;
+    field public static final int IPV6_UNICAST_HOPS;
+    field public static final int IPV6_V6ONLY;
+    field public static final int IP_MULTICAST_IF;
+    field public static final int IP_MULTICAST_LOOP;
+    field public static final int IP_MULTICAST_TTL;
+    field public static final int IP_TOS;
+    field public static final int IP_TTL;
+    field public static final int MAP_ANONYMOUS;
+    field public static final int MAP_FIXED;
+    field public static final int MAP_PRIVATE;
+    field public static final int MAP_SHARED;
+    field public static final int MCAST_BLOCK_SOURCE;
+    field public static final int MCAST_JOIN_GROUP;
+    field public static final int MCAST_JOIN_SOURCE_GROUP;
+    field public static final int MCAST_LEAVE_GROUP;
+    field public static final int MCAST_LEAVE_SOURCE_GROUP;
+    field public static final int MCAST_UNBLOCK_SOURCE;
+    field public static final int MCL_CURRENT;
+    field public static final int MCL_FUTURE;
+    field public static final int MFD_CLOEXEC;
+    field public static final int MSG_CTRUNC;
+    field public static final int MSG_DONTROUTE;
+    field public static final int MSG_EOR;
+    field public static final int MSG_OOB;
+    field public static final int MSG_PEEK;
+    field public static final int MSG_TRUNC;
+    field public static final int MSG_WAITALL;
+    field public static final int MS_ASYNC;
+    field public static final int MS_INVALIDATE;
+    field public static final int MS_SYNC;
+    field public static final int NETLINK_INET_DIAG;
+    field public static final int NETLINK_NETFILTER;
+    field public static final int NETLINK_ROUTE;
+    field public static final int NI_DGRAM;
+    field public static final int NI_NAMEREQD;
+    field public static final int NI_NOFQDN;
+    field public static final int NI_NUMERICHOST;
+    field public static final int NI_NUMERICSERV;
+    field public static final int O_ACCMODE;
+    field public static final int O_APPEND;
+    field public static final int O_CLOEXEC;
+    field public static final int O_CREAT;
+    field public static final int O_DSYNC;
+    field public static final int O_EXCL;
+    field public static final int O_NOCTTY;
+    field public static final int O_NOFOLLOW;
+    field public static final int O_NONBLOCK;
+    field public static final int O_RDONLY;
+    field public static final int O_RDWR;
+    field public static final int O_SYNC;
+    field public static final int O_TRUNC;
+    field public static final int O_WRONLY;
+    field public static final int POLLERR;
+    field public static final int POLLHUP;
+    field public static final int POLLIN;
+    field public static final int POLLNVAL;
+    field public static final int POLLOUT;
+    field public static final int POLLPRI;
+    field public static final int POLLRDBAND;
+    field public static final int POLLRDNORM;
+    field public static final int POLLWRBAND;
+    field public static final int POLLWRNORM;
+    field public static final int PROT_EXEC;
+    field public static final int PROT_NONE;
+    field public static final int PROT_READ;
+    field public static final int PROT_WRITE;
+    field public static final int PR_GET_DUMPABLE;
+    field public static final int PR_SET_DUMPABLE;
+    field public static final int PR_SET_NO_NEW_PRIVS;
+    field public static final int RTMGRP_NEIGH;
+    field public static final int RT_SCOPE_HOST;
+    field public static final int RT_SCOPE_LINK;
+    field public static final int RT_SCOPE_NOWHERE;
+    field public static final int RT_SCOPE_SITE;
+    field public static final int RT_SCOPE_UNIVERSE;
+    field public static final int R_OK;
+    field public static final int SEEK_CUR;
+    field public static final int SEEK_END;
+    field public static final int SEEK_SET;
+    field public static final int SHUT_RD;
+    field public static final int SHUT_RDWR;
+    field public static final int SHUT_WR;
+    field public static final int SIGABRT;
+    field public static final int SIGALRM;
+    field public static final int SIGBUS;
+    field public static final int SIGCHLD;
+    field public static final int SIGCONT;
+    field public static final int SIGFPE;
+    field public static final int SIGHUP;
+    field public static final int SIGILL;
+    field public static final int SIGINT;
+    field public static final int SIGIO;
+    field public static final int SIGKILL;
+    field public static final int SIGPIPE;
+    field public static final int SIGPROF;
+    field public static final int SIGPWR;
+    field public static final int SIGQUIT;
+    field public static final int SIGRTMAX;
+    field public static final int SIGRTMIN;
+    field public static final int SIGSEGV;
+    field public static final int SIGSTKFLT;
+    field public static final int SIGSTOP;
+    field public static final int SIGSYS;
+    field public static final int SIGTERM;
+    field public static final int SIGTRAP;
+    field public static final int SIGTSTP;
+    field public static final int SIGTTIN;
+    field public static final int SIGTTOU;
+    field public static final int SIGURG;
+    field public static final int SIGUSR1;
+    field public static final int SIGUSR2;
+    field public static final int SIGVTALRM;
+    field public static final int SIGWINCH;
+    field public static final int SIGXCPU;
+    field public static final int SIGXFSZ;
+    field public static final int SIOCGIFADDR;
+    field public static final int SIOCGIFBRDADDR;
+    field public static final int SIOCGIFDSTADDR;
+    field public static final int SIOCGIFNETMASK;
+    field public static final int SOCK_CLOEXEC;
+    field public static final int SOCK_DGRAM;
+    field public static final int SOCK_NONBLOCK;
+    field public static final int SOCK_RAW;
+    field public static final int SOCK_SEQPACKET;
+    field public static final int SOCK_STREAM;
+    field public static final int SOL_SOCKET;
+    field public static final int SOL_UDP;
+    field public static final int SO_BINDTODEVICE;
+    field public static final int SO_BROADCAST;
+    field public static final int SO_DEBUG;
+    field public static final int SO_DONTROUTE;
+    field public static final int SO_ERROR;
+    field public static final int SO_KEEPALIVE;
+    field public static final int SO_LINGER;
+    field public static final int SO_OOBINLINE;
+    field public static final int SO_PASSCRED;
+    field public static final int SO_PEERCRED;
+    field public static final int SO_RCVBUF;
+    field public static final int SO_RCVLOWAT;
+    field public static final int SO_RCVTIMEO;
+    field public static final int SO_REUSEADDR;
+    field public static final int SO_SNDBUF;
+    field public static final int SO_SNDLOWAT;
+    field public static final int SO_SNDTIMEO;
+    field public static final int SO_TYPE;
+    field public static final int STDERR_FILENO;
+    field public static final int STDIN_FILENO;
+    field public static final int STDOUT_FILENO;
+    field public static final int ST_MANDLOCK;
+    field public static final int ST_NOATIME;
+    field public static final int ST_NODEV;
+    field public static final int ST_NODIRATIME;
+    field public static final int ST_NOEXEC;
+    field public static final int ST_NOSUID;
+    field public static final int ST_RDONLY;
+    field public static final int ST_RELATIME;
+    field public static final int ST_SYNCHRONOUS;
+    field public static final int S_IFBLK;
+    field public static final int S_IFCHR;
+    field public static final int S_IFDIR;
+    field public static final int S_IFIFO;
+    field public static final int S_IFLNK;
+    field public static final int S_IFMT;
+    field public static final int S_IFREG;
+    field public static final int S_IFSOCK;
+    field public static final int S_IRGRP;
+    field public static final int S_IROTH;
+    field public static final int S_IRUSR;
+    field public static final int S_IRWXG;
+    field public static final int S_IRWXO;
+    field public static final int S_IRWXU;
+    field public static final int S_ISGID;
+    field public static final int S_ISUID;
+    field public static final int S_ISVTX;
+    field public static final int S_IWGRP;
+    field public static final int S_IWOTH;
+    field public static final int S_IWUSR;
+    field public static final int S_IXGRP;
+    field public static final int S_IXOTH;
+    field public static final int S_IXUSR;
+    field public static final int TCP_NODELAY;
+    field public static final int TCP_USER_TIMEOUT;
+    field public static final int UDP_GRO;
+    field public static final int UDP_SEGMENT;
+    field public static final int VMADDR_CID_ANY;
+    field public static final int VMADDR_CID_HOST;
+    field public static final int VMADDR_CID_LOCAL;
+    field public static final int VMADDR_PORT_ANY;
+    field public static final int WCONTINUED;
+    field public static final int WEXITED;
+    field public static final int WNOHANG;
+    field public static final int WNOWAIT;
+    field public static final int WSTOPPED;
+    field public static final int WUNTRACED;
+    field public static final int W_OK;
+    field public static final int X_OK;
+    field public static final int _SC_2_CHAR_TERM;
+    field public static final int _SC_2_C_BIND;
+    field public static final int _SC_2_C_DEV;
+    field public static final int _SC_2_C_VERSION;
+    field public static final int _SC_2_FORT_DEV;
+    field public static final int _SC_2_FORT_RUN;
+    field public static final int _SC_2_LOCALEDEF;
+    field public static final int _SC_2_SW_DEV;
+    field public static final int _SC_2_UPE;
+    field public static final int _SC_2_VERSION;
+    field public static final int _SC_AIO_LISTIO_MAX;
+    field public static final int _SC_AIO_MAX;
+    field public static final int _SC_AIO_PRIO_DELTA_MAX;
+    field public static final int _SC_ARG_MAX;
+    field public static final int _SC_ASYNCHRONOUS_IO;
+    field public static final int _SC_ATEXIT_MAX;
+    field public static final int _SC_AVPHYS_PAGES;
+    field public static final int _SC_BC_BASE_MAX;
+    field public static final int _SC_BC_DIM_MAX;
+    field public static final int _SC_BC_SCALE_MAX;
+    field public static final int _SC_BC_STRING_MAX;
+    field public static final int _SC_CHILD_MAX;
+    field public static final int _SC_CLK_TCK;
+    field public static final int _SC_COLL_WEIGHTS_MAX;
+    field public static final int _SC_DELAYTIMER_MAX;
+    field public static final int _SC_EXPR_NEST_MAX;
+    field public static final int _SC_FSYNC;
+    field public static final int _SC_GETGR_R_SIZE_MAX;
+    field public static final int _SC_GETPW_R_SIZE_MAX;
+    field public static final int _SC_IOV_MAX;
+    field public static final int _SC_JOB_CONTROL;
+    field public static final int _SC_LINE_MAX;
+    field public static final int _SC_LOGIN_NAME_MAX;
+    field public static final int _SC_MAPPED_FILES;
+    field public static final int _SC_MEMLOCK;
+    field public static final int _SC_MEMLOCK_RANGE;
+    field public static final int _SC_MEMORY_PROTECTION;
+    field public static final int _SC_MESSAGE_PASSING;
+    field public static final int _SC_MQ_OPEN_MAX;
+    field public static final int _SC_MQ_PRIO_MAX;
+    field public static final int _SC_NGROUPS_MAX;
+    field public static final int _SC_NPROCESSORS_CONF;
+    field public static final int _SC_NPROCESSORS_ONLN;
+    field public static final int _SC_OPEN_MAX;
+    field public static final int _SC_PAGESIZE;
+    field public static final int _SC_PAGE_SIZE;
+    field public static final int _SC_PASS_MAX;
+    field public static final int _SC_PHYS_PAGES;
+    field public static final int _SC_PRIORITIZED_IO;
+    field public static final int _SC_PRIORITY_SCHEDULING;
+    field public static final int _SC_REALTIME_SIGNALS;
+    field public static final int _SC_RE_DUP_MAX;
+    field public static final int _SC_RTSIG_MAX;
+    field public static final int _SC_SAVED_IDS;
+    field public static final int _SC_SEMAPHORES;
+    field public static final int _SC_SEM_NSEMS_MAX;
+    field public static final int _SC_SEM_VALUE_MAX;
+    field public static final int _SC_SHARED_MEMORY_OBJECTS;
+    field public static final int _SC_SIGQUEUE_MAX;
+    field public static final int _SC_STREAM_MAX;
+    field public static final int _SC_SYNCHRONIZED_IO;
+    field public static final int _SC_THREADS;
+    field public static final int _SC_THREAD_ATTR_STACKADDR;
+    field public static final int _SC_THREAD_ATTR_STACKSIZE;
+    field public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS;
+    field public static final int _SC_THREAD_KEYS_MAX;
+    field public static final int _SC_THREAD_PRIORITY_SCHEDULING;
+    field public static final int _SC_THREAD_PRIO_INHERIT;
+    field public static final int _SC_THREAD_PRIO_PROTECT;
+    field public static final int _SC_THREAD_SAFE_FUNCTIONS;
+    field public static final int _SC_THREAD_STACK_MIN;
+    field public static final int _SC_THREAD_THREADS_MAX;
+    field public static final int _SC_TIMERS;
+    field public static final int _SC_TIMER_MAX;
+    field public static final int _SC_TTY_NAME_MAX;
+    field public static final int _SC_TZNAME_MAX;
+    field public static final int _SC_VERSION;
+    field public static final int _SC_XBS5_ILP32_OFF32;
+    field public static final int _SC_XBS5_ILP32_OFFBIG;
+    field public static final int _SC_XBS5_LP64_OFF64;
+    field public static final int _SC_XBS5_LPBIG_OFFBIG;
+    field public static final int _SC_XOPEN_CRYPT;
+    field public static final int _SC_XOPEN_ENH_I18N;
+    field public static final int _SC_XOPEN_LEGACY;
+    field public static final int _SC_XOPEN_REALTIME;
+    field public static final int _SC_XOPEN_REALTIME_THREADS;
+    field public static final int _SC_XOPEN_SHM;
+    field public static final int _SC_XOPEN_UNIX;
+    field public static final int _SC_XOPEN_VERSION;
+    field public static final int _SC_XOPEN_XCU_VERSION;
+  }
+
+  public final class StructCmsghdr {
+    ctor public StructCmsghdr(int, int, short);
+    ctor public StructCmsghdr(int, int, @NonNull byte[]);
+    field @NonNull public final byte[] cmsg_data;
+    field public final int cmsg_level;
+    field public final int cmsg_type;
+  }
+
+  public final class StructMsghdr {
+    ctor public StructMsghdr(@Nullable java.net.SocketAddress, @NonNull java.nio.ByteBuffer[], @Nullable android.system.StructCmsghdr[], int);
+    field @Nullable public android.system.StructCmsghdr[] msg_control;
+    field public int msg_flags;
+    field @NonNull public final java.nio.ByteBuffer[] msg_iov;
+    field @Nullable public java.net.SocketAddress msg_name;
+  }
+
+  public final class StructPollfd {
+    ctor public StructPollfd();
+    field public short events;
+    field public java.io.FileDescriptor fd;
+    field public short revents;
+    field public Object userData;
+  }
+
+  public final class StructStat {
+    ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+    ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long);
+    field public final android.system.StructTimespec st_atim;
+    field public final long st_atime;
+    field public final long st_blksize;
+    field public final long st_blocks;
+    field public final android.system.StructTimespec st_ctim;
+    field public final long st_ctime;
+    field public final long st_dev;
+    field public final int st_gid;
+    field public final long st_ino;
+    field public final int st_mode;
+    field public final android.system.StructTimespec st_mtim;
+    field public final long st_mtime;
+    field public final long st_nlink;
+    field public final long st_rdev;
+    field public final long st_size;
+    field public final int st_uid;
+  }
+
+  public final class StructStatVfs {
+    ctor public StructStatVfs(long, long, long, long, long, long, long, long, long, long, long);
+    field public final long f_bavail;
+    field public final long f_bfree;
+    field public final long f_blocks;
+    field public final long f_bsize;
+    field public final long f_favail;
+    field public final long f_ffree;
+    field public final long f_files;
+    field public final long f_flag;
+    field public final long f_frsize;
+    field public final long f_fsid;
+    field public final long f_namemax;
+  }
+
+  public final class StructTimespec implements java.lang.Comparable<android.system.StructTimespec> {
+    ctor public StructTimespec(long, long);
+    method public int compareTo(android.system.StructTimespec);
+    field public final long tv_nsec;
+    field public final long tv_sec;
+  }
+
+  public final class StructTimeval {
+    method @NonNull public static android.system.StructTimeval fromMillis(long);
+    method public long toMillis();
+    field public final long tv_sec;
+    field public final long tv_usec;
+  }
+
+  public final class StructUtsname {
+    ctor public StructUtsname(String, String, String, String, String);
+    field public final String machine;
+    field public final String nodename;
+    field public final String release;
+    field public final String sysname;
+    field public final String version;
+  }
+
+}
+
+package dalvik.annotation {
+
+  @Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface TestTarget {
+    method @Deprecated public abstract String conceptName() default "";
+    method @Deprecated public abstract Class<?>[] methodArgs() default {};
+    method @Deprecated public abstract String methodName() default "";
+  }
+
+  @Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE}) public @interface TestTargetClass {
+    method @Deprecated public abstract Class<?> value();
+  }
+
+}
+
+package dalvik.bytecode {
+
+  public final class OpcodeInfo {
+    field public static final int MAXIMUM_PACKED_VALUE;
+    field public static final int MAXIMUM_VALUE;
+  }
+
+  public interface Opcodes {
+    field public static final int OP_ADD_DOUBLE = 171; // 0xab
+    field public static final int OP_ADD_DOUBLE_2ADDR = 203; // 0xcb
+    field public static final int OP_ADD_FLOAT = 166; // 0xa6
+    field public static final int OP_ADD_FLOAT_2ADDR = 198; // 0xc6
+    field public static final int OP_ADD_INT = 144; // 0x90
+    field public static final int OP_ADD_INT_2ADDR = 176; // 0xb0
+    field public static final int OP_ADD_INT_LIT16 = 208; // 0xd0
+    field public static final int OP_ADD_INT_LIT8 = 216; // 0xd8
+    field public static final int OP_ADD_LONG = 155; // 0x9b
+    field public static final int OP_ADD_LONG_2ADDR = 187; // 0xbb
+    field public static final int OP_AGET = 68; // 0x44
+    field public static final int OP_AGET_BOOLEAN = 71; // 0x47
+    field public static final int OP_AGET_BYTE = 72; // 0x48
+    field public static final int OP_AGET_CHAR = 73; // 0x49
+    field public static final int OP_AGET_OBJECT = 70; // 0x46
+    field public static final int OP_AGET_SHORT = 74; // 0x4a
+    field public static final int OP_AGET_WIDE = 69; // 0x45
+    field public static final int OP_AND_INT = 149; // 0x95
+    field public static final int OP_AND_INT_2ADDR = 181; // 0xb5
+    field public static final int OP_AND_INT_LIT16 = 213; // 0xd5
+    field public static final int OP_AND_INT_LIT8 = 221; // 0xdd
+    field public static final int OP_AND_LONG = 160; // 0xa0
+    field public static final int OP_AND_LONG_2ADDR = 192; // 0xc0
+    field public static final int OP_APUT = 75; // 0x4b
+    field public static final int OP_APUT_BOOLEAN = 78; // 0x4e
+    field public static final int OP_APUT_BYTE = 79; // 0x4f
+    field public static final int OP_APUT_CHAR = 80; // 0x50
+    field public static final int OP_APUT_OBJECT = 77; // 0x4d
+    field public static final int OP_APUT_SHORT = 81; // 0x51
+    field public static final int OP_APUT_WIDE = 76; // 0x4c
+    field public static final int OP_ARRAY_LENGTH = 33; // 0x21
+    field @Deprecated public static final int OP_BREAKPOINT = 236; // 0xec
+    field public static final int OP_CHECK_CAST = 31; // 0x1f
+    field public static final int OP_CHECK_CAST_JUMBO = 511; // 0x1ff
+    field public static final int OP_CMPG_DOUBLE = 48; // 0x30
+    field public static final int OP_CMPG_FLOAT = 46; // 0x2e
+    field public static final int OP_CMPL_DOUBLE = 47; // 0x2f
+    field public static final int OP_CMPL_FLOAT = 45; // 0x2d
+    field public static final int OP_CMP_LONG = 49; // 0x31
+    field public static final int OP_CONST = 20; // 0x14
+    field public static final int OP_CONST_16 = 19; // 0x13
+    field public static final int OP_CONST_4 = 18; // 0x12
+    field public static final int OP_CONST_CLASS = 28; // 0x1c
+    field public static final int OP_CONST_CLASS_JUMBO = 255; // 0xff
+    field public static final int OP_CONST_HIGH16 = 21; // 0x15
+    field public static final int OP_CONST_METHOD_HANDLE = 254; // 0xfe
+    field public static final int OP_CONST_METHOD_TYPE = 255; // 0xff
+    field public static final int OP_CONST_STRING = 26; // 0x1a
+    field public static final int OP_CONST_STRING_JUMBO = 27; // 0x1b
+    field public static final int OP_CONST_WIDE = 24; // 0x18
+    field public static final int OP_CONST_WIDE_16 = 22; // 0x16
+    field public static final int OP_CONST_WIDE_32 = 23; // 0x17
+    field public static final int OP_CONST_WIDE_HIGH16 = 25; // 0x19
+    field public static final int OP_DIV_DOUBLE = 174; // 0xae
+    field public static final int OP_DIV_DOUBLE_2ADDR = 206; // 0xce
+    field public static final int OP_DIV_FLOAT = 169; // 0xa9
+    field public static final int OP_DIV_FLOAT_2ADDR = 201; // 0xc9
+    field public static final int OP_DIV_INT = 147; // 0x93
+    field public static final int OP_DIV_INT_2ADDR = 179; // 0xb3
+    field public static final int OP_DIV_INT_LIT16 = 211; // 0xd3
+    field public static final int OP_DIV_INT_LIT8 = 219; // 0xdb
+    field public static final int OP_DIV_LONG = 158; // 0x9e
+    field public static final int OP_DIV_LONG_2ADDR = 190; // 0xbe
+    field public static final int OP_DOUBLE_TO_FLOAT = 140; // 0x8c
+    field public static final int OP_DOUBLE_TO_INT = 138; // 0x8a
+    field public static final int OP_DOUBLE_TO_LONG = 139; // 0x8b
+    field @Deprecated public static final int OP_EXECUTE_INLINE = 238; // 0xee
+    field @Deprecated public static final int OP_EXECUTE_INLINE_RANGE = 239; // 0xef
+    field public static final int OP_FILLED_NEW_ARRAY = 36; // 0x24
+    field public static final int OP_FILLED_NEW_ARRAY_JUMBO = 1535; // 0x5ff
+    field public static final int OP_FILLED_NEW_ARRAY_RANGE = 37; // 0x25
+    field public static final int OP_FILL_ARRAY_DATA = 38; // 0x26
+    field public static final int OP_FLOAT_TO_DOUBLE = 137; // 0x89
+    field public static final int OP_FLOAT_TO_INT = 135; // 0x87
+    field public static final int OP_FLOAT_TO_LONG = 136; // 0x88
+    field public static final int OP_GOTO = 40; // 0x28
+    field public static final int OP_GOTO_16 = 41; // 0x29
+    field public static final int OP_GOTO_32 = 42; // 0x2a
+    field public static final int OP_IF_EQ = 50; // 0x32
+    field public static final int OP_IF_EQZ = 56; // 0x38
+    field public static final int OP_IF_GE = 53; // 0x35
+    field public static final int OP_IF_GEZ = 59; // 0x3b
+    field public static final int OP_IF_GT = 54; // 0x36
+    field public static final int OP_IF_GTZ = 60; // 0x3c
+    field public static final int OP_IF_LE = 55; // 0x37
+    field public static final int OP_IF_LEZ = 61; // 0x3d
+    field public static final int OP_IF_LT = 52; // 0x34
+    field public static final int OP_IF_LTZ = 58; // 0x3a
+    field public static final int OP_IF_NE = 51; // 0x33
+    field public static final int OP_IF_NEZ = 57; // 0x39
+    field public static final int OP_IGET = 82; // 0x52
+    field public static final int OP_IGET_BOOLEAN = 85; // 0x55
+    field public static final int OP_IGET_BOOLEAN_JUMBO = 2559; // 0x9ff
+    field public static final int OP_IGET_BYTE = 86; // 0x56
+    field public static final int OP_IGET_BYTE_JUMBO = 2815; // 0xaff
+    field public static final int OP_IGET_CHAR = 87; // 0x57
+    field public static final int OP_IGET_CHAR_JUMBO = 3071; // 0xbff
+    field public static final int OP_IGET_JUMBO = 1791; // 0x6ff
+    field public static final int OP_IGET_OBJECT = 84; // 0x54
+    field public static final int OP_IGET_OBJECT_JUMBO = 2303; // 0x8ff
+    field @Deprecated public static final int OP_IGET_OBJECT_QUICK = 244; // 0xf4
+    field @Deprecated public static final int OP_IGET_QUICK = 242; // 0xf2
+    field public static final int OP_IGET_SHORT = 88; // 0x58
+    field public static final int OP_IGET_SHORT_JUMBO = 3327; // 0xcff
+    field public static final int OP_IGET_WIDE = 83; // 0x53
+    field public static final int OP_IGET_WIDE_JUMBO = 2047; // 0x7ff
+    field @Deprecated public static final int OP_IGET_WIDE_QUICK = 243; // 0xf3
+    field @Deprecated public static final int OP_IGET_WIDE_VOLATILE = 232; // 0xe8
+    field public static final int OP_INSTANCE_OF = 32; // 0x20
+    field public static final int OP_INSTANCE_OF_JUMBO = 767; // 0x2ff
+    field public static final int OP_INT_TO_BYTE = 141; // 0x8d
+    field public static final int OP_INT_TO_CHAR = 142; // 0x8e
+    field public static final int OP_INT_TO_DOUBLE = 131; // 0x83
+    field public static final int OP_INT_TO_FLOAT = 130; // 0x82
+    field public static final int OP_INT_TO_LONG = 129; // 0x81
+    field public static final int OP_INT_TO_SHORT = 143; // 0x8f
+    field public static final int OP_INVOKE_CUSTOM = 252; // 0xfc
+    field public static final int OP_INVOKE_CUSTOM_RANGE = 253; // 0xfd
+    field public static final int OP_INVOKE_DIRECT = 112; // 0x70
+    field @Deprecated public static final int OP_INVOKE_DIRECT_EMPTY = 240; // 0xf0
+    field public static final int OP_INVOKE_DIRECT_JUMBO = 9471; // 0x24ff
+    field public static final int OP_INVOKE_DIRECT_RANGE = 118; // 0x76
+    field public static final int OP_INVOKE_INTERFACE = 114; // 0x72
+    field public static final int OP_INVOKE_INTERFACE_JUMBO = 9983; // 0x26ff
+    field public static final int OP_INVOKE_INTERFACE_RANGE = 120; // 0x78
+    field public static final int OP_INVOKE_POLYMORPHIC = 250; // 0xfa
+    field public static final int OP_INVOKE_POLYMORPHIC_RANGE = 251; // 0xfb
+    field public static final int OP_INVOKE_STATIC = 113; // 0x71
+    field public static final int OP_INVOKE_STATIC_JUMBO = 9727; // 0x25ff
+    field public static final int OP_INVOKE_STATIC_RANGE = 119; // 0x77
+    field public static final int OP_INVOKE_SUPER = 111; // 0x6f
+    field public static final int OP_INVOKE_SUPER_JUMBO = 9215; // 0x23ff
+    field @Deprecated public static final int OP_INVOKE_SUPER_QUICK = 250; // 0xfa
+    field @Deprecated public static final int OP_INVOKE_SUPER_QUICK_RANGE = 251; // 0xfb
+    field public static final int OP_INVOKE_SUPER_RANGE = 117; // 0x75
+    field public static final int OP_INVOKE_VIRTUAL = 110; // 0x6e
+    field public static final int OP_INVOKE_VIRTUAL_JUMBO = 8959; // 0x22ff
+    field @Deprecated public static final int OP_INVOKE_VIRTUAL_QUICK = 248; // 0xf8
+    field @Deprecated public static final int OP_INVOKE_VIRTUAL_QUICK_RANGE = 249; // 0xf9
+    field public static final int OP_INVOKE_VIRTUAL_RANGE = 116; // 0x74
+    field public static final int OP_IPUT = 89; // 0x59
+    field public static final int OP_IPUT_BOOLEAN = 92; // 0x5c
+    field public static final int OP_IPUT_BOOLEAN_JUMBO = 4351; // 0x10ff
+    field public static final int OP_IPUT_BYTE = 93; // 0x5d
+    field public static final int OP_IPUT_BYTE_JUMBO = 4607; // 0x11ff
+    field public static final int OP_IPUT_CHAR = 94; // 0x5e
+    field public static final int OP_IPUT_CHAR_JUMBO = 4863; // 0x12ff
+    field public static final int OP_IPUT_JUMBO = 3583; // 0xdff
+    field public static final int OP_IPUT_OBJECT = 91; // 0x5b
+    field public static final int OP_IPUT_OBJECT_JUMBO = 4095; // 0xfff
+    field @Deprecated public static final int OP_IPUT_OBJECT_QUICK = 247; // 0xf7
+    field @Deprecated public static final int OP_IPUT_QUICK = 245; // 0xf5
+    field public static final int OP_IPUT_SHORT = 95; // 0x5f
+    field public static final int OP_IPUT_SHORT_JUMBO = 5119; // 0x13ff
+    field public static final int OP_IPUT_WIDE = 90; // 0x5a
+    field public static final int OP_IPUT_WIDE_JUMBO = 3839; // 0xeff
+    field @Deprecated public static final int OP_IPUT_WIDE_QUICK = 246; // 0xf6
+    field @Deprecated public static final int OP_IPUT_WIDE_VOLATILE = 233; // 0xe9
+    field public static final int OP_LONG_TO_DOUBLE = 134; // 0x86
+    field public static final int OP_LONG_TO_FLOAT = 133; // 0x85
+    field public static final int OP_LONG_TO_INT = 132; // 0x84
+    field public static final int OP_MONITOR_ENTER = 29; // 0x1d
+    field public static final int OP_MONITOR_EXIT = 30; // 0x1e
+    field public static final int OP_MOVE = 1; // 0x1
+    field public static final int OP_MOVE_16 = 3; // 0x3
+    field public static final int OP_MOVE_EXCEPTION = 13; // 0xd
+    field public static final int OP_MOVE_FROM16 = 2; // 0x2
+    field public static final int OP_MOVE_OBJECT = 7; // 0x7
+    field public static final int OP_MOVE_OBJECT_16 = 9; // 0x9
+    field public static final int OP_MOVE_OBJECT_FROM16 = 8; // 0x8
+    field public static final int OP_MOVE_RESULT = 10; // 0xa
+    field public static final int OP_MOVE_RESULT_OBJECT = 12; // 0xc
+    field public static final int OP_MOVE_RESULT_WIDE = 11; // 0xb
+    field public static final int OP_MOVE_WIDE = 4; // 0x4
+    field public static final int OP_MOVE_WIDE_16 = 6; // 0x6
+    field public static final int OP_MOVE_WIDE_FROM16 = 5; // 0x5
+    field public static final int OP_MUL_DOUBLE = 173; // 0xad
+    field public static final int OP_MUL_DOUBLE_2ADDR = 205; // 0xcd
+    field public static final int OP_MUL_FLOAT = 168; // 0xa8
+    field public static final int OP_MUL_FLOAT_2ADDR = 200; // 0xc8
+    field public static final int OP_MUL_INT = 146; // 0x92
+    field public static final int OP_MUL_INT_2ADDR = 178; // 0xb2
+    field public static final int OP_MUL_INT_LIT16 = 210; // 0xd2
+    field public static final int OP_MUL_INT_LIT8 = 218; // 0xda
+    field public static final int OP_MUL_LONG = 157; // 0x9d
+    field public static final int OP_MUL_LONG_2ADDR = 189; // 0xbd
+    field public static final int OP_NEG_DOUBLE = 128; // 0x80
+    field public static final int OP_NEG_FLOAT = 127; // 0x7f
+    field public static final int OP_NEG_INT = 123; // 0x7b
+    field public static final int OP_NEG_LONG = 125; // 0x7d
+    field public static final int OP_NEW_ARRAY = 35; // 0x23
+    field public static final int OP_NEW_ARRAY_JUMBO = 1279; // 0x4ff
+    field public static final int OP_NEW_INSTANCE = 34; // 0x22
+    field public static final int OP_NEW_INSTANCE_JUMBO = 1023; // 0x3ff
+    field public static final int OP_NOP = 0; // 0x0
+    field public static final int OP_NOT_INT = 124; // 0x7c
+    field public static final int OP_NOT_LONG = 126; // 0x7e
+    field public static final int OP_OR_INT = 150; // 0x96
+    field public static final int OP_OR_INT_2ADDR = 182; // 0xb6
+    field public static final int OP_OR_INT_LIT16 = 214; // 0xd6
+    field public static final int OP_OR_INT_LIT8 = 222; // 0xde
+    field public static final int OP_OR_LONG = 161; // 0xa1
+    field public static final int OP_OR_LONG_2ADDR = 193; // 0xc1
+    field public static final int OP_PACKED_SWITCH = 43; // 0x2b
+    field public static final int OP_REM_DOUBLE = 175; // 0xaf
+    field public static final int OP_REM_DOUBLE_2ADDR = 207; // 0xcf
+    field public static final int OP_REM_FLOAT = 170; // 0xaa
+    field public static final int OP_REM_FLOAT_2ADDR = 202; // 0xca
+    field public static final int OP_REM_INT = 148; // 0x94
+    field public static final int OP_REM_INT_2ADDR = 180; // 0xb4
+    field public static final int OP_REM_INT_LIT16 = 212; // 0xd4
+    field public static final int OP_REM_INT_LIT8 = 220; // 0xdc
+    field public static final int OP_REM_LONG = 159; // 0x9f
+    field public static final int OP_REM_LONG_2ADDR = 191; // 0xbf
+    field public static final int OP_RETURN = 15; // 0xf
+    field public static final int OP_RETURN_OBJECT = 17; // 0x11
+    field public static final int OP_RETURN_VOID = 14; // 0xe
+    field public static final int OP_RETURN_WIDE = 16; // 0x10
+    field public static final int OP_RSUB_INT = 209; // 0xd1
+    field public static final int OP_RSUB_INT_LIT8 = 217; // 0xd9
+    field public static final int OP_SGET = 96; // 0x60
+    field public static final int OP_SGET_BOOLEAN = 99; // 0x63
+    field public static final int OP_SGET_BOOLEAN_JUMBO = 6143; // 0x17ff
+    field public static final int OP_SGET_BYTE = 100; // 0x64
+    field public static final int OP_SGET_BYTE_JUMBO = 6399; // 0x18ff
+    field public static final int OP_SGET_CHAR = 101; // 0x65
+    field public static final int OP_SGET_CHAR_JUMBO = 6655; // 0x19ff
+    field public static final int OP_SGET_JUMBO = 5375; // 0x14ff
+    field public static final int OP_SGET_OBJECT = 98; // 0x62
+    field public static final int OP_SGET_OBJECT_JUMBO = 5887; // 0x16ff
+    field public static final int OP_SGET_SHORT = 102; // 0x66
+    field public static final int OP_SGET_SHORT_JUMBO = 6911; // 0x1aff
+    field public static final int OP_SGET_WIDE = 97; // 0x61
+    field public static final int OP_SGET_WIDE_JUMBO = 5631; // 0x15ff
+    field @Deprecated public static final int OP_SGET_WIDE_VOLATILE = 234; // 0xea
+    field public static final int OP_SHL_INT = 152; // 0x98
+    field public static final int OP_SHL_INT_2ADDR = 184; // 0xb8
+    field public static final int OP_SHL_INT_LIT8 = 224; // 0xe0
+    field public static final int OP_SHL_LONG = 163; // 0xa3
+    field public static final int OP_SHL_LONG_2ADDR = 195; // 0xc3
+    field public static final int OP_SHR_INT = 153; // 0x99
+    field public static final int OP_SHR_INT_2ADDR = 185; // 0xb9
+    field public static final int OP_SHR_INT_LIT8 = 225; // 0xe1
+    field public static final int OP_SHR_LONG = 164; // 0xa4
+    field public static final int OP_SHR_LONG_2ADDR = 196; // 0xc4
+    field public static final int OP_SPARSE_SWITCH = 44; // 0x2c
+    field public static final int OP_SPUT = 103; // 0x67
+    field public static final int OP_SPUT_BOOLEAN = 106; // 0x6a
+    field public static final int OP_SPUT_BOOLEAN_JUMBO = 7935; // 0x1eff
+    field public static final int OP_SPUT_BYTE = 107; // 0x6b
+    field public static final int OP_SPUT_BYTE_JUMBO = 8191; // 0x1fff
+    field public static final int OP_SPUT_CHAR = 108; // 0x6c
+    field public static final int OP_SPUT_CHAR_JUMBO = 8447; // 0x20ff
+    field public static final int OP_SPUT_JUMBO = 7167; // 0x1bff
+    field public static final int OP_SPUT_OBJECT = 105; // 0x69
+    field public static final int OP_SPUT_OBJECT_JUMBO = 7679; // 0x1dff
+    field public static final int OP_SPUT_SHORT = 109; // 0x6d
+    field public static final int OP_SPUT_SHORT_JUMBO = 8703; // 0x21ff
+    field public static final int OP_SPUT_WIDE = 104; // 0x68
+    field public static final int OP_SPUT_WIDE_JUMBO = 7423; // 0x1cff
+    field @Deprecated public static final int OP_SPUT_WIDE_VOLATILE = 235; // 0xeb
+    field public static final int OP_SUB_DOUBLE = 172; // 0xac
+    field public static final int OP_SUB_DOUBLE_2ADDR = 204; // 0xcc
+    field public static final int OP_SUB_FLOAT = 167; // 0xa7
+    field public static final int OP_SUB_FLOAT_2ADDR = 199; // 0xc7
+    field public static final int OP_SUB_INT = 145; // 0x91
+    field public static final int OP_SUB_INT_2ADDR = 177; // 0xb1
+    field public static final int OP_SUB_LONG = 156; // 0x9c
+    field public static final int OP_SUB_LONG_2ADDR = 188; // 0xbc
+    field public static final int OP_THROW = 39; // 0x27
+    field @Deprecated public static final int OP_THROW_VERIFICATION_ERROR = 237; // 0xed
+    field public static final int OP_USHR_INT = 154; // 0x9a
+    field public static final int OP_USHR_INT_2ADDR = 186; // 0xba
+    field public static final int OP_USHR_INT_LIT8 = 226; // 0xe2
+    field public static final int OP_USHR_LONG = 165; // 0xa5
+    field public static final int OP_USHR_LONG_2ADDR = 197; // 0xc5
+    field public static final int OP_XOR_INT = 151; // 0x97
+    field public static final int OP_XOR_INT_2ADDR = 183; // 0xb7
+    field public static final int OP_XOR_INT_LIT16 = 215; // 0xd7
+    field public static final int OP_XOR_INT_LIT8 = 223; // 0xdf
+    field public static final int OP_XOR_LONG = 162; // 0xa2
+    field public static final int OP_XOR_LONG_2ADDR = 194; // 0xc2
+  }
+
+}
+
+package dalvik.system {
+
+  public class BaseDexClassLoader extends java.lang.ClassLoader {
+    ctor public BaseDexClassLoader(String, java.io.File, String, ClassLoader);
+    method public String findLibrary(String);
+    method protected java.util.Enumeration<java.net.URL> findResources(String);
+  }
+
+  public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
+    ctor public DelegateLastClassLoader(String, ClassLoader);
+    ctor public DelegateLastClassLoader(String, String, ClassLoader);
+    ctor public DelegateLastClassLoader(@NonNull String, @Nullable String, @Nullable ClassLoader, boolean);
+  }
+
+  public class DexClassLoader extends dalvik.system.BaseDexClassLoader {
+    ctor public DexClassLoader(String, String, String, ClassLoader);
+  }
+
+  @Deprecated public final class DexFile {
+    ctor @Deprecated public DexFile(java.io.File) throws java.io.IOException;
+    ctor @Deprecated public DexFile(String) throws java.io.IOException;
+    method @Deprecated public void close() throws java.io.IOException;
+    method @Deprecated public java.util.Enumeration<java.lang.String> entries();
+    method @Deprecated public String getName();
+    method @Deprecated public static boolean isDexOptNeeded(String) throws java.io.FileNotFoundException, java.io.IOException;
+    method @Deprecated public Class loadClass(String, ClassLoader);
+    method @Deprecated public static dalvik.system.DexFile loadDex(String, String, int) throws java.io.IOException;
+  }
+
+  public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
+    ctor public InMemoryDexClassLoader(@NonNull java.nio.ByteBuffer[], @Nullable String, @Nullable ClassLoader);
+    ctor public InMemoryDexClassLoader(@NonNull java.nio.ByteBuffer[], @Nullable ClassLoader);
+    ctor public InMemoryDexClassLoader(@NonNull java.nio.ByteBuffer, @Nullable ClassLoader);
+  }
+
+  public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
+    ctor public PathClassLoader(String, ClassLoader);
+    ctor public PathClassLoader(String, String, ClassLoader);
+  }
+
+}
+
+package java.awt.font {
+
+  public final class NumericShaper implements java.io.Serializable {
+    method public static java.awt.font.NumericShaper getContextualShaper(int);
+    method public static java.awt.font.NumericShaper getContextualShaper(java.util.Set<java.awt.font.NumericShaper.Range>);
+    method public static java.awt.font.NumericShaper getContextualShaper(int, int);
+    method public static java.awt.font.NumericShaper getContextualShaper(java.util.Set<java.awt.font.NumericShaper.Range>, java.awt.font.NumericShaper.Range);
+    method public java.util.Set<java.awt.font.NumericShaper.Range> getRangeSet();
+    method public int getRanges();
+    method public static java.awt.font.NumericShaper getShaper(int);
+    method public static java.awt.font.NumericShaper getShaper(java.awt.font.NumericShaper.Range);
+    method public boolean isContextual();
+    method public void shape(char[], int, int);
+    method public void shape(char[], int, int, int);
+    method public void shape(char[], int, int, java.awt.font.NumericShaper.Range);
+    field public static final int ALL_RANGES = 524287; // 0x7ffff
+    field public static final int ARABIC = 2; // 0x2
+    field public static final int BENGALI = 16; // 0x10
+    field public static final int DEVANAGARI = 8; // 0x8
+    field public static final int EASTERN_ARABIC = 4; // 0x4
+    field public static final int ETHIOPIC = 65536; // 0x10000
+    field public static final int EUROPEAN = 1; // 0x1
+    field public static final int GUJARATI = 64; // 0x40
+    field public static final int GURMUKHI = 32; // 0x20
+    field public static final int KANNADA = 1024; // 0x400
+    field public static final int KHMER = 131072; // 0x20000
+    field public static final int LAO = 8192; // 0x2000
+    field public static final int MALAYALAM = 2048; // 0x800
+    field public static final int MONGOLIAN = 262144; // 0x40000
+    field public static final int MYANMAR = 32768; // 0x8000
+    field public static final int ORIYA = 128; // 0x80
+    field public static final int TAMIL = 256; // 0x100
+    field public static final int TELUGU = 512; // 0x200
+    field public static final int THAI = 4096; // 0x1000
+    field public static final int TIBETAN = 16384; // 0x4000
+  }
+
+  public enum NumericShaper.Range {
+    enum_constant public static final java.awt.font.NumericShaper.Range ARABIC;
+    enum_constant public static final java.awt.font.NumericShaper.Range BALINESE;
+    enum_constant public static final java.awt.font.NumericShaper.Range BENGALI;
+    enum_constant public static final java.awt.font.NumericShaper.Range CHAM;
+    enum_constant public static final java.awt.font.NumericShaper.Range DEVANAGARI;
+    enum_constant public static final java.awt.font.NumericShaper.Range EASTERN_ARABIC;
+    enum_constant public static final java.awt.font.NumericShaper.Range ETHIOPIC;
+    enum_constant public static final java.awt.font.NumericShaper.Range EUROPEAN;
+    enum_constant public static final java.awt.font.NumericShaper.Range GUJARATI;
+    enum_constant public static final java.awt.font.NumericShaper.Range GURMUKHI;
+    enum_constant public static final java.awt.font.NumericShaper.Range JAVANESE;
+    enum_constant public static final java.awt.font.NumericShaper.Range KANNADA;
+    enum_constant public static final java.awt.font.NumericShaper.Range KAYAH_LI;
+    enum_constant public static final java.awt.font.NumericShaper.Range KHMER;
+    enum_constant public static final java.awt.font.NumericShaper.Range LAO;
+    enum_constant public static final java.awt.font.NumericShaper.Range LEPCHA;
+    enum_constant public static final java.awt.font.NumericShaper.Range LIMBU;
+    enum_constant public static final java.awt.font.NumericShaper.Range MALAYALAM;
+    enum_constant public static final java.awt.font.NumericShaper.Range MEETEI_MAYEK;
+    enum_constant public static final java.awt.font.NumericShaper.Range MONGOLIAN;
+    enum_constant public static final java.awt.font.NumericShaper.Range MYANMAR;
+    enum_constant public static final java.awt.font.NumericShaper.Range MYANMAR_SHAN;
+    enum_constant public static final java.awt.font.NumericShaper.Range NEW_TAI_LUE;
+    enum_constant public static final java.awt.font.NumericShaper.Range NKO;
+    enum_constant public static final java.awt.font.NumericShaper.Range OL_CHIKI;
+    enum_constant public static final java.awt.font.NumericShaper.Range ORIYA;
+    enum_constant public static final java.awt.font.NumericShaper.Range SAURASHTRA;
+    enum_constant public static final java.awt.font.NumericShaper.Range SUNDANESE;
+    enum_constant public static final java.awt.font.NumericShaper.Range TAI_THAM_HORA;
+    enum_constant public static final java.awt.font.NumericShaper.Range TAI_THAM_THAM;
+    enum_constant public static final java.awt.font.NumericShaper.Range TAMIL;
+    enum_constant public static final java.awt.font.NumericShaper.Range TELUGU;
+    enum_constant public static final java.awt.font.NumericShaper.Range THAI;
+    enum_constant public static final java.awt.font.NumericShaper.Range TIBETAN;
+    enum_constant public static final java.awt.font.NumericShaper.Range VAI;
+  }
+
+  public final class TextAttribute extends java.text.AttributedCharacterIterator.Attribute {
+    ctor protected TextAttribute(String);
+    field public static final java.awt.font.TextAttribute BACKGROUND;
+    field public static final java.awt.font.TextAttribute BIDI_EMBEDDING;
+    field public static final java.awt.font.TextAttribute CHAR_REPLACEMENT;
+    field public static final java.awt.font.TextAttribute FAMILY;
+    field public static final java.awt.font.TextAttribute FONT;
+    field public static final java.awt.font.TextAttribute FOREGROUND;
+    field public static final java.awt.font.TextAttribute INPUT_METHOD_HIGHLIGHT;
+    field public static final java.awt.font.TextAttribute INPUT_METHOD_UNDERLINE;
+    field public static final java.awt.font.TextAttribute JUSTIFICATION;
+    field public static final Float JUSTIFICATION_FULL;
+    field public static final Float JUSTIFICATION_NONE;
+    field public static final java.awt.font.TextAttribute KERNING;
+    field public static final Integer KERNING_ON;
+    field public static final java.awt.font.TextAttribute LIGATURES;
+    field public static final Integer LIGATURES_ON;
+    field public static final java.awt.font.TextAttribute NUMERIC_SHAPING;
+    field public static final java.awt.font.TextAttribute POSTURE;
+    field public static final Float POSTURE_OBLIQUE;
+    field public static final Float POSTURE_REGULAR;
+    field public static final java.awt.font.TextAttribute RUN_DIRECTION;
+    field public static final Boolean RUN_DIRECTION_LTR;
+    field public static final Boolean RUN_DIRECTION_RTL;
+    field public static final java.awt.font.TextAttribute SIZE;
+    field public static final java.awt.font.TextAttribute STRIKETHROUGH;
+    field public static final Boolean STRIKETHROUGH_ON;
+    field public static final java.awt.font.TextAttribute SUPERSCRIPT;
+    field public static final Integer SUPERSCRIPT_SUB;
+    field public static final Integer SUPERSCRIPT_SUPER;
+    field public static final java.awt.font.TextAttribute SWAP_COLORS;
+    field public static final Boolean SWAP_COLORS_ON;
+    field public static final java.awt.font.TextAttribute TRACKING;
+    field public static final Float TRACKING_LOOSE;
+    field public static final Float TRACKING_TIGHT;
+    field public static final java.awt.font.TextAttribute TRANSFORM;
+    field public static final java.awt.font.TextAttribute UNDERLINE;
+    field public static final Integer UNDERLINE_LOW_DASHED;
+    field public static final Integer UNDERLINE_LOW_DOTTED;
+    field public static final Integer UNDERLINE_LOW_GRAY;
+    field public static final Integer UNDERLINE_LOW_ONE_PIXEL;
+    field public static final Integer UNDERLINE_LOW_TWO_PIXEL;
+    field public static final Integer UNDERLINE_ON;
+    field public static final java.awt.font.TextAttribute WEIGHT;
+    field public static final Float WEIGHT_BOLD;
+    field public static final Float WEIGHT_DEMIBOLD;
+    field public static final Float WEIGHT_DEMILIGHT;
+    field public static final Float WEIGHT_EXTRABOLD;
+    field public static final Float WEIGHT_EXTRA_LIGHT;
+    field public static final Float WEIGHT_HEAVY;
+    field public static final Float WEIGHT_LIGHT;
+    field public static final Float WEIGHT_MEDIUM;
+    field public static final Float WEIGHT_REGULAR;
+    field public static final Float WEIGHT_SEMIBOLD;
+    field public static final Float WEIGHT_ULTRABOLD;
+    field public static final java.awt.font.TextAttribute WIDTH;
+    field public static final Float WIDTH_CONDENSED;
+    field public static final Float WIDTH_EXTENDED;
+    field public static final Float WIDTH_REGULAR;
+    field public static final Float WIDTH_SEMI_CONDENSED;
+    field public static final Float WIDTH_SEMI_EXTENDED;
+  }
+
+}
+
+package java.beans {
+
+  public class IndexedPropertyChangeEvent extends java.beans.PropertyChangeEvent {
+    ctor public IndexedPropertyChangeEvent(Object, String, Object, Object, int);
+    method public int getIndex();
+  }
+
+  public class PropertyChangeEvent extends java.util.EventObject {
+    ctor public PropertyChangeEvent(Object, String, Object, Object);
+    method public Object getNewValue();
+    method public Object getOldValue();
+    method public Object getPropagationId();
+    method public String getPropertyName();
+    method public void setPropagationId(Object);
+  }
+
+  public interface PropertyChangeListener extends java.util.EventListener {
+    method public void propertyChange(java.beans.PropertyChangeEvent);
+  }
+
+  public class PropertyChangeListenerProxy extends java.util.EventListenerProxy<java.beans.PropertyChangeListener> implements java.beans.PropertyChangeListener {
+    ctor public PropertyChangeListenerProxy(String, java.beans.PropertyChangeListener);
+    method public String getPropertyName();
+    method public void propertyChange(java.beans.PropertyChangeEvent);
+  }
+
+  public class PropertyChangeSupport implements java.io.Serializable {
+    ctor public PropertyChangeSupport(Object);
+    method public void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public void addPropertyChangeListener(String, java.beans.PropertyChangeListener);
+    method public void fireIndexedPropertyChange(String, int, Object, Object);
+    method public void fireIndexedPropertyChange(String, int, int, int);
+    method public void fireIndexedPropertyChange(String, int, boolean, boolean);
+    method public void firePropertyChange(String, Object, Object);
+    method public void firePropertyChange(String, int, int);
+    method public void firePropertyChange(String, boolean, boolean);
+    method public void firePropertyChange(java.beans.PropertyChangeEvent);
+    method public java.beans.PropertyChangeListener[] getPropertyChangeListeners();
+    method public java.beans.PropertyChangeListener[] getPropertyChangeListeners(String);
+    method public boolean hasListeners(String);
+    method public void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public void removePropertyChangeListener(String, java.beans.PropertyChangeListener);
+  }
+
+}
+
+package java.io {
+
+  public class BufferedInputStream extends java.io.FilterInputStream {
+    ctor public BufferedInputStream(java.io.InputStream);
+    ctor public BufferedInputStream(java.io.InputStream, int);
+    field protected volatile byte[] buf;
+    field protected int count;
+    field protected int marklimit;
+    field protected int markpos;
+    field protected int pos;
+  }
+
+  public class BufferedOutputStream extends java.io.FilterOutputStream {
+    ctor public BufferedOutputStream(java.io.OutputStream);
+    ctor public BufferedOutputStream(java.io.OutputStream, int);
+    field protected byte[] buf;
+    field protected int count;
+  }
+
+  public class BufferedReader extends java.io.Reader {
+    ctor public BufferedReader(java.io.Reader, int);
+    ctor public BufferedReader(java.io.Reader);
+    method public void close() throws java.io.IOException;
+    method public java.util.stream.Stream<java.lang.String> lines();
+    method public int read(char[], int, int) throws java.io.IOException;
+    method public String readLine() throws java.io.IOException;
+  }
+
+  public class BufferedWriter extends java.io.Writer {
+    ctor public BufferedWriter(java.io.Writer);
+    ctor public BufferedWriter(java.io.Writer, int);
+    method public void close() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method public void newLine() throws java.io.IOException;
+    method public void write(char[], int, int) throws java.io.IOException;
+  }
+
+  public class ByteArrayInputStream extends java.io.InputStream {
+    ctor public ByteArrayInputStream(byte[]);
+    ctor public ByteArrayInputStream(byte[], int, int);
+    method public int available();
+    method public int read();
+    method public int read(byte[], int, int);
+    method public void reset();
+    method public long skip(long);
+    field protected byte[] buf;
+    field protected int count;
+    field protected int mark;
+    field protected int pos;
+  }
+
+  public class ByteArrayOutputStream extends java.io.OutputStream {
+    ctor public ByteArrayOutputStream();
+    ctor public ByteArrayOutputStream(int);
+    method public void reset();
+    method public int size();
+    method @NonNull public byte[] toByteArray();
+    method @NonNull public String toString(@NonNull String) throws java.io.UnsupportedEncodingException;
+    method @Deprecated @NonNull public String toString(int);
+    method public void write(int);
+    method public void write(@NonNull byte[], int, int);
+    method public void writeTo(@NonNull java.io.OutputStream) throws java.io.IOException;
+    field @NonNull protected byte[] buf;
+    field protected int count;
+  }
+
+  public class CharArrayReader extends java.io.Reader {
+    ctor public CharArrayReader(char[]);
+    ctor public CharArrayReader(char[], int, int);
+    method public void close();
+    method public int read(char[], int, int) throws java.io.IOException;
+    field protected char[] buf;
+    field protected int count;
+    field protected int markedPos;
+    field protected int pos;
+  }
+
+  public class CharArrayWriter extends java.io.Writer {
+    ctor public CharArrayWriter();
+    ctor public CharArrayWriter(int);
+    method public java.io.CharArrayWriter append(CharSequence);
+    method public java.io.CharArrayWriter append(CharSequence, int, int);
+    method public java.io.CharArrayWriter append(char);
+    method public void close();
+    method public void flush();
+    method public void reset();
+    method public int size();
+    method public char[] toCharArray();
+    method public void write(int);
+    method public void write(char[], int, int);
+    method public void write(String, int, int);
+    method public void writeTo(java.io.Writer) throws java.io.IOException;
+    field protected char[] buf;
+    field protected int count;
+  }
+
+  public class CharConversionException extends java.io.IOException {
+    ctor public CharConversionException();
+    ctor public CharConversionException(String);
+  }
+
+  public interface Closeable extends java.lang.AutoCloseable {
+    method public void close() throws java.io.IOException;
+  }
+
+  public final class Console implements java.io.Flushable {
+    method public void flush();
+    method public java.io.Console format(String, java.lang.Object...);
+    method public java.io.Console printf(String, java.lang.Object...);
+    method public String readLine(String, java.lang.Object...);
+    method public String readLine();
+    method public char[] readPassword(String, java.lang.Object...);
+    method public char[] readPassword();
+    method public java.io.Reader reader();
+    method public java.io.PrintWriter writer();
+  }
+
+  public interface DataInput {
+    method public boolean readBoolean() throws java.io.IOException;
+    method public byte readByte() throws java.io.IOException;
+    method public char readChar() throws java.io.IOException;
+    method public double readDouble() throws java.io.IOException;
+    method public float readFloat() throws java.io.IOException;
+    method public void readFully(byte[]) throws java.io.IOException;
+    method public void readFully(byte[], int, int) throws java.io.IOException;
+    method public int readInt() throws java.io.IOException;
+    method public String readLine() throws java.io.IOException;
+    method public long readLong() throws java.io.IOException;
+    method public short readShort() throws java.io.IOException;
+    method public String readUTF() throws java.io.IOException;
+    method public int readUnsignedByte() throws java.io.IOException;
+    method public int readUnsignedShort() throws java.io.IOException;
+    method public int skipBytes(int) throws java.io.IOException;
+  }
+
+  public class DataInputStream extends java.io.FilterInputStream implements java.io.DataInput {
+    ctor public DataInputStream(java.io.InputStream);
+    method public final int read(byte[]) throws java.io.IOException;
+    method public final int read(byte[], int, int) throws java.io.IOException;
+    method public final boolean readBoolean() throws java.io.IOException;
+    method public final byte readByte() throws java.io.IOException;
+    method public final char readChar() throws java.io.IOException;
+    method public final double readDouble() throws java.io.IOException;
+    method public final float readFloat() throws java.io.IOException;
+    method public final void readFully(byte[]) throws java.io.IOException;
+    method public final void readFully(byte[], int, int) throws java.io.IOException;
+    method public final int readInt() throws java.io.IOException;
+    method @Deprecated public final String readLine() throws java.io.IOException;
+    method public final long readLong() throws java.io.IOException;
+    method public final short readShort() throws java.io.IOException;
+    method public final String readUTF() throws java.io.IOException;
+    method public static final String readUTF(java.io.DataInput) throws java.io.IOException;
+    method public final int readUnsignedByte() throws java.io.IOException;
+    method public final int readUnsignedShort() throws java.io.IOException;
+    method public final int skipBytes(int) throws java.io.IOException;
+  }
+
+  public interface DataOutput {
+    method public void write(int) throws java.io.IOException;
+    method public void write(byte[]) throws java.io.IOException;
+    method public void write(byte[], int, int) throws java.io.IOException;
+    method public void writeBoolean(boolean) throws java.io.IOException;
+    method public void writeByte(int) throws java.io.IOException;
+    method public void writeBytes(String) throws java.io.IOException;
+    method public void writeChar(int) throws java.io.IOException;
+    method public void writeChars(String) throws java.io.IOException;
+    method public void writeDouble(double) throws java.io.IOException;
+    method public void writeFloat(float) throws java.io.IOException;
+    method public void writeInt(int) throws java.io.IOException;
+    method public void writeLong(long) throws java.io.IOException;
+    method public void writeShort(int) throws java.io.IOException;
+    method public void writeUTF(String) throws java.io.IOException;
+  }
+
+  public class DataOutputStream extends java.io.FilterOutputStream implements java.io.DataOutput {
+    ctor public DataOutputStream(java.io.OutputStream);
+    method public final int size();
+    method public final void writeBoolean(boolean) throws java.io.IOException;
+    method public final void writeByte(int) throws java.io.IOException;
+    method public final void writeBytes(String) throws java.io.IOException;
+    method public final void writeChar(int) throws java.io.IOException;
+    method public final void writeChars(String) throws java.io.IOException;
+    method public final void writeDouble(double) throws java.io.IOException;
+    method public final void writeFloat(float) throws java.io.IOException;
+    method public final void writeInt(int) throws java.io.IOException;
+    method public final void writeLong(long) throws java.io.IOException;
+    method public final void writeShort(int) throws java.io.IOException;
+    method public final void writeUTF(String) throws java.io.IOException;
+    field protected int written;
+  }
+
+  public class EOFException extends java.io.IOException {
+    ctor public EOFException();
+    ctor public EOFException(String);
+  }
+
+  public interface Externalizable extends java.io.Serializable {
+    method public void readExternal(java.io.ObjectInput) throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public void writeExternal(java.io.ObjectOutput) throws java.io.IOException;
+  }
+
+  public class File implements java.lang.Comparable<java.io.File> java.io.Serializable {
+    ctor public File(@NonNull String);
+    ctor public File(@Nullable String, @NonNull String);
+    ctor public File(@Nullable java.io.File, @NonNull String);
+    ctor public File(@NonNull java.net.URI);
+    method public boolean canExecute();
+    method public boolean canRead();
+    method public boolean canWrite();
+    method public int compareTo(@NonNull java.io.File);
+    method public boolean createNewFile() throws java.io.IOException;
+    method @NonNull public static java.io.File createTempFile(@NonNull String, @Nullable String, @Nullable java.io.File) throws java.io.IOException;
+    method @NonNull public static java.io.File createTempFile(@NonNull String, @Nullable String) throws java.io.IOException;
+    method public boolean delete();
+    method public void deleteOnExit();
+    method public boolean exists();
+    method @NonNull public java.io.File getAbsoluteFile();
+    method @NonNull public String getAbsolutePath();
+    method @NonNull public java.io.File getCanonicalFile() throws java.io.IOException;
+    method @NonNull public String getCanonicalPath() throws java.io.IOException;
+    method public long getFreeSpace();
+    method @NonNull public String getName();
+    method @Nullable public String getParent();
+    method @Nullable public java.io.File getParentFile();
+    method @NonNull public String getPath();
+    method public long getTotalSpace();
+    method public long getUsableSpace();
+    method public boolean isAbsolute();
+    method public boolean isDirectory();
+    method public boolean isFile();
+    method public boolean isHidden();
+    method public long lastModified();
+    method public long length();
+    method @Nullable public String[] list();
+    method @Nullable public String[] list(@Nullable java.io.FilenameFilter);
+    method @Nullable public java.io.File[] listFiles();
+    method @Nullable public java.io.File[] listFiles(@Nullable java.io.FilenameFilter);
+    method @Nullable public java.io.File[] listFiles(@Nullable java.io.FileFilter);
+    method @NonNull public static java.io.File[] listRoots();
+    method public boolean mkdir();
+    method public boolean mkdirs();
+    method public boolean renameTo(@NonNull java.io.File);
+    method public boolean setExecutable(boolean, boolean);
+    method public boolean setExecutable(boolean);
+    method public boolean setLastModified(long);
+    method public boolean setReadOnly();
+    method public boolean setReadable(boolean, boolean);
+    method public boolean setReadable(boolean);
+    method public boolean setWritable(boolean, boolean);
+    method public boolean setWritable(boolean);
+    method @NonNull public java.nio.file.Path toPath();
+    method @NonNull public java.net.URI toURI();
+    method @Deprecated @NonNull public java.net.URL toURL() throws java.net.MalformedURLException;
+    field @NonNull public static final String pathSeparator;
+    field public static final char pathSeparatorChar;
+    field @NonNull public static final String separator;
+    field public static final char separatorChar;
+  }
+
+  public final class FileDescriptor {
+    ctor public FileDescriptor();
+    method public void sync() throws java.io.SyncFailedException;
+    method public boolean valid();
+    field public static final java.io.FileDescriptor err;
+    field public static final java.io.FileDescriptor in;
+    field public static final java.io.FileDescriptor out;
+  }
+
+  @java.lang.FunctionalInterface public interface FileFilter {
+    method public boolean accept(java.io.File);
+  }
+
+  public class FileInputStream extends java.io.InputStream {
+    ctor public FileInputStream(String) throws java.io.FileNotFoundException;
+    ctor public FileInputStream(java.io.File) throws java.io.FileNotFoundException;
+    ctor public FileInputStream(java.io.FileDescriptor);
+    method protected void finalize() throws java.io.IOException;
+    method public java.nio.channels.FileChannel getChannel();
+    method public final java.io.FileDescriptor getFD() throws java.io.IOException;
+    method public int read() throws java.io.IOException;
+  }
+
+  public class FileNotFoundException extends java.io.IOException {
+    ctor public FileNotFoundException();
+    ctor public FileNotFoundException(String);
+  }
+
+  public class FileOutputStream extends java.io.OutputStream {
+    ctor public FileOutputStream(String) throws java.io.FileNotFoundException;
+    ctor public FileOutputStream(String, boolean) throws java.io.FileNotFoundException;
+    ctor public FileOutputStream(java.io.File) throws java.io.FileNotFoundException;
+    ctor public FileOutputStream(java.io.File, boolean) throws java.io.FileNotFoundException;
+    ctor public FileOutputStream(java.io.FileDescriptor);
+    method protected void finalize() throws java.io.IOException;
+    method public java.nio.channels.FileChannel getChannel();
+    method public final java.io.FileDescriptor getFD() throws java.io.IOException;
+    method public void write(int) throws java.io.IOException;
+  }
+
+  public final class FilePermission extends java.security.Permission implements java.io.Serializable {
+    ctor public FilePermission(String, String);
+    method public String getActions();
+    method public boolean implies(java.security.Permission);
+  }
+
+  public class FileReader extends java.io.InputStreamReader {
+    ctor public FileReader(String) throws java.io.FileNotFoundException;
+    ctor public FileReader(java.io.File) throws java.io.FileNotFoundException;
+    ctor public FileReader(java.io.FileDescriptor);
+  }
+
+  public class FileWriter extends java.io.OutputStreamWriter {
+    ctor public FileWriter(String) throws java.io.IOException;
+    ctor public FileWriter(String, boolean) throws java.io.IOException;
+    ctor public FileWriter(java.io.File) throws java.io.IOException;
+    ctor public FileWriter(java.io.File, boolean) throws java.io.IOException;
+    ctor public FileWriter(java.io.FileDescriptor);
+  }
+
+  @java.lang.FunctionalInterface public interface FilenameFilter {
+    method public boolean accept(java.io.File, String);
+  }
+
+  public class FilterInputStream extends java.io.InputStream {
+    ctor protected FilterInputStream(java.io.InputStream);
+    method public int read() throws java.io.IOException;
+    field protected volatile java.io.InputStream in;
+  }
+
+  public class FilterOutputStream extends java.io.OutputStream {
+    ctor public FilterOutputStream(java.io.OutputStream);
+    method public void write(int) throws java.io.IOException;
+    field protected java.io.OutputStream out;
+  }
+
+  public abstract class FilterReader extends java.io.Reader {
+    ctor protected FilterReader(java.io.Reader);
+    method public void close() throws java.io.IOException;
+    method public int read(char[], int, int) throws java.io.IOException;
+    field protected java.io.Reader in;
+  }
+
+  public abstract class FilterWriter extends java.io.Writer {
+    ctor protected FilterWriter(java.io.Writer);
+    method public void close() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method public void write(char[], int, int) throws java.io.IOException;
+    field protected java.io.Writer out;
+  }
+
+  public interface Flushable {
+    method public void flush() throws java.io.IOException;
+  }
+
+  public class IOError extends java.lang.Error {
+    ctor public IOError(Throwable);
+  }
+
+  public class IOException extends java.lang.Exception {
+    ctor public IOException();
+    ctor public IOException(String);
+    ctor public IOException(String, Throwable);
+    ctor public IOException(Throwable);
+  }
+
+  public abstract class InputStream implements java.io.Closeable {
+    ctor public InputStream();
+    method public int available() throws java.io.IOException;
+    method public void close() throws java.io.IOException;
+    method public void mark(int);
+    method public boolean markSupported();
+    method public abstract int read() throws java.io.IOException;
+    method public int read(byte[]) throws java.io.IOException;
+    method public int read(byte[], int, int) throws java.io.IOException;
+    method public void reset() throws java.io.IOException;
+    method public long skip(long) throws java.io.IOException;
+  }
+
+  public class InputStreamReader extends java.io.Reader {
+    ctor public InputStreamReader(java.io.InputStream);
+    ctor public InputStreamReader(java.io.InputStream, String) throws java.io.UnsupportedEncodingException;
+    ctor public InputStreamReader(java.io.InputStream, java.nio.charset.Charset);
+    ctor public InputStreamReader(java.io.InputStream, java.nio.charset.CharsetDecoder);
+    method public void close() throws java.io.IOException;
+    method public String getEncoding();
+    method public int read(char[], int, int) throws java.io.IOException;
+  }
+
+  public class InterruptedIOException extends java.io.IOException {
+    ctor public InterruptedIOException();
+    ctor public InterruptedIOException(String);
+    field public int bytesTransferred;
+  }
+
+  public class InvalidClassException extends java.io.ObjectStreamException {
+    ctor public InvalidClassException(String);
+    ctor public InvalidClassException(String, String);
+    field public String classname;
+  }
+
+  public class InvalidObjectException extends java.io.ObjectStreamException {
+    ctor public InvalidObjectException(String);
+  }
+
+  @Deprecated public class LineNumberInputStream extends java.io.FilterInputStream {
+    ctor @Deprecated public LineNumberInputStream(java.io.InputStream);
+    method @Deprecated public int getLineNumber();
+    method @Deprecated public void setLineNumber(int);
+  }
+
+  public class LineNumberReader extends java.io.BufferedReader {
+    ctor public LineNumberReader(java.io.Reader);
+    ctor public LineNumberReader(java.io.Reader, int);
+    method public int getLineNumber();
+    method public void setLineNumber(int);
+  }
+
+  public class NotActiveException extends java.io.ObjectStreamException {
+    ctor public NotActiveException(String);
+    ctor public NotActiveException();
+  }
+
+  public class NotSerializableException extends java.io.ObjectStreamException {
+    ctor public NotSerializableException(String);
+    ctor public NotSerializableException();
+  }
+
+  public interface ObjectInput extends java.io.DataInput java.lang.AutoCloseable {
+    method public int available() throws java.io.IOException;
+    method public void close() throws java.io.IOException;
+    method public int read() throws java.io.IOException;
+    method public int read(byte[]) throws java.io.IOException;
+    method public int read(byte[], int, int) throws java.io.IOException;
+    method public Object readObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public long skip(long) throws java.io.IOException;
+  }
+
+  public class ObjectInputStream extends java.io.InputStream implements java.io.ObjectInput java.io.ObjectStreamConstants {
+    ctor public ObjectInputStream(java.io.InputStream) throws java.io.IOException;
+    ctor protected ObjectInputStream() throws java.io.IOException, java.lang.SecurityException;
+    method public void defaultReadObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method protected boolean enableResolveObject(boolean) throws java.lang.SecurityException;
+    method public int read() throws java.io.IOException;
+    method public boolean readBoolean() throws java.io.IOException;
+    method public byte readByte() throws java.io.IOException;
+    method public char readChar() throws java.io.IOException;
+    method protected java.io.ObjectStreamClass readClassDescriptor() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public double readDouble() throws java.io.IOException;
+    method public java.io.ObjectInputStream.GetField readFields() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public float readFloat() throws java.io.IOException;
+    method public void readFully(byte[]) throws java.io.IOException;
+    method public void readFully(byte[], int, int) throws java.io.IOException;
+    method public int readInt() throws java.io.IOException;
+    method @Deprecated public String readLine() throws java.io.IOException;
+    method public long readLong() throws java.io.IOException;
+    method public final Object readObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method protected Object readObjectOverride() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public short readShort() throws java.io.IOException;
+    method protected void readStreamHeader() throws java.io.IOException, java.io.StreamCorruptedException;
+    method public String readUTF() throws java.io.IOException;
+    method public Object readUnshared() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public int readUnsignedByte() throws java.io.IOException;
+    method public int readUnsignedShort() throws java.io.IOException;
+    method public void registerValidation(java.io.ObjectInputValidation, int) throws java.io.InvalidObjectException, java.io.NotActiveException;
+    method protected Class<?> resolveClass(java.io.ObjectStreamClass) throws java.lang.ClassNotFoundException, java.io.IOException;
+    method protected Object resolveObject(Object) throws java.io.IOException;
+    method protected Class<?> resolveProxyClass(String[]) throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public int skipBytes(int) throws java.io.IOException;
+  }
+
+  public abstract static class ObjectInputStream.GetField {
+    ctor public ObjectInputStream.GetField();
+    method public abstract boolean defaulted(String) throws java.io.IOException;
+    method public abstract boolean get(String, boolean) throws java.io.IOException;
+    method public abstract byte get(String, byte) throws java.io.IOException;
+    method public abstract char get(String, char) throws java.io.IOException;
+    method public abstract short get(String, short) throws java.io.IOException;
+    method public abstract int get(String, int) throws java.io.IOException;
+    method public abstract long get(String, long) throws java.io.IOException;
+    method public abstract float get(String, float) throws java.io.IOException;
+    method public abstract double get(String, double) throws java.io.IOException;
+    method public abstract Object get(String, Object) throws java.io.IOException;
+    method public abstract java.io.ObjectStreamClass getObjectStreamClass();
+  }
+
+  public interface ObjectInputValidation {
+    method public void validateObject() throws java.io.InvalidObjectException;
+  }
+
+  public interface ObjectOutput extends java.io.DataOutput java.lang.AutoCloseable {
+    method public void close() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method public void writeObject(Object) throws java.io.IOException;
+  }
+
+  public class ObjectOutputStream extends java.io.OutputStream implements java.io.ObjectOutput java.io.ObjectStreamConstants {
+    ctor public ObjectOutputStream(java.io.OutputStream) throws java.io.IOException;
+    ctor protected ObjectOutputStream() throws java.io.IOException, java.lang.SecurityException;
+    method protected void annotateClass(Class<?>) throws java.io.IOException;
+    method protected void annotateProxyClass(Class<?>) throws java.io.IOException;
+    method public void defaultWriteObject() throws java.io.IOException;
+    method protected void drain() throws java.io.IOException;
+    method protected boolean enableReplaceObject(boolean) throws java.lang.SecurityException;
+    method public java.io.ObjectOutputStream.PutField putFields() throws java.io.IOException;
+    method protected Object replaceObject(Object) throws java.io.IOException;
+    method public void reset() throws java.io.IOException;
+    method public void useProtocolVersion(int) throws java.io.IOException;
+    method public void write(int) throws java.io.IOException;
+    method public void writeBoolean(boolean) throws java.io.IOException;
+    method public void writeByte(int) throws java.io.IOException;
+    method public void writeBytes(String) throws java.io.IOException;
+    method public void writeChar(int) throws java.io.IOException;
+    method public void writeChars(String) throws java.io.IOException;
+    method protected void writeClassDescriptor(java.io.ObjectStreamClass) throws java.io.IOException;
+    method public void writeDouble(double) throws java.io.IOException;
+    method public void writeFields() throws java.io.IOException;
+    method public void writeFloat(float) throws java.io.IOException;
+    method public void writeInt(int) throws java.io.IOException;
+    method public void writeLong(long) throws java.io.IOException;
+    method public final void writeObject(Object) throws java.io.IOException;
+    method protected void writeObjectOverride(Object) throws java.io.IOException;
+    method public void writeShort(int) throws java.io.IOException;
+    method protected void writeStreamHeader() throws java.io.IOException;
+    method public void writeUTF(String) throws java.io.IOException;
+    method public void writeUnshared(Object) throws java.io.IOException;
+  }
+
+  public abstract static class ObjectOutputStream.PutField {
+    ctor public ObjectOutputStream.PutField();
+    method public abstract void put(String, boolean);
+    method public abstract void put(String, byte);
+    method public abstract void put(String, char);
+    method public abstract void put(String, short);
+    method public abstract void put(String, int);
+    method public abstract void put(String, long);
+    method public abstract void put(String, float);
+    method public abstract void put(String, double);
+    method public abstract void put(String, Object);
+    method @Deprecated public abstract void write(java.io.ObjectOutput) throws java.io.IOException;
+  }
+
+  public class ObjectStreamClass implements java.io.Serializable {
+    method public Class<?> forClass();
+    method public java.io.ObjectStreamField getField(String);
+    method public java.io.ObjectStreamField[] getFields();
+    method public String getName();
+    method public long getSerialVersionUID();
+    method public static java.io.ObjectStreamClass lookup(Class<?>);
+    method public static java.io.ObjectStreamClass lookupAny(Class<?>);
+    field public static final java.io.ObjectStreamField[] NO_FIELDS;
+  }
+
+  public interface ObjectStreamConstants {
+    field public static final int PROTOCOL_VERSION_1 = 1; // 0x1
+    field public static final int PROTOCOL_VERSION_2 = 2; // 0x2
+    field public static final byte SC_BLOCK_DATA = 8; // 0x8
+    field public static final byte SC_ENUM = 16; // 0x10
+    field public static final byte SC_EXTERNALIZABLE = 4; // 0x4
+    field public static final byte SC_SERIALIZABLE = 2; // 0x2
+    field public static final byte SC_WRITE_METHOD = 1; // 0x1
+    field public static final short STREAM_MAGIC = -21267; // 0xffffaced
+    field public static final short STREAM_VERSION = 5; // 0x5
+    field public static final java.io.SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION;
+    field public static final java.io.SerializablePermission SUBSTITUTION_PERMISSION;
+    field public static final byte TC_ARRAY = 117; // 0x75
+    field public static final byte TC_BASE = 112; // 0x70
+    field public static final byte TC_BLOCKDATA = 119; // 0x77
+    field public static final byte TC_BLOCKDATALONG = 122; // 0x7a
+    field public static final byte TC_CLASS = 118; // 0x76
+    field public static final byte TC_CLASSDESC = 114; // 0x72
+    field public static final byte TC_ENDBLOCKDATA = 120; // 0x78
+    field public static final byte TC_ENUM = 126; // 0x7e
+    field public static final byte TC_EXCEPTION = 123; // 0x7b
+    field public static final byte TC_LONGSTRING = 124; // 0x7c
+    field public static final byte TC_MAX = 126; // 0x7e
+    field public static final byte TC_NULL = 112; // 0x70
+    field public static final byte TC_OBJECT = 115; // 0x73
+    field public static final byte TC_PROXYCLASSDESC = 125; // 0x7d
+    field public static final byte TC_REFERENCE = 113; // 0x71
+    field public static final byte TC_RESET = 121; // 0x79
+    field public static final byte TC_STRING = 116; // 0x74
+    field public static final int baseWireHandle = 8257536; // 0x7e0000
+  }
+
+  public abstract class ObjectStreamException extends java.io.IOException {
+    ctor protected ObjectStreamException(String);
+    ctor protected ObjectStreamException();
+  }
+
+  public class ObjectStreamField implements java.lang.Comparable<java.lang.Object> {
+    ctor public ObjectStreamField(String, Class<?>);
+    ctor public ObjectStreamField(String, Class<?>, boolean);
+    method public int compareTo(Object);
+    method public String getName();
+    method public int getOffset();
+    method public Class<?> getType();
+    method public char getTypeCode();
+    method public String getTypeString();
+    method public boolean isPrimitive();
+    method public boolean isUnshared();
+    method protected void setOffset(int);
+  }
+
+  public class OptionalDataException extends java.io.ObjectStreamException {
+    field public boolean eof;
+    field public int length;
+  }
+
+  public abstract class OutputStream implements java.io.Closeable java.io.Flushable {
+    ctor public OutputStream();
+    method public void close() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method public abstract void write(int) throws java.io.IOException;
+    method public void write(byte[]) throws java.io.IOException;
+    method public void write(byte[], int, int) throws java.io.IOException;
+  }
+
+  public class OutputStreamWriter extends java.io.Writer {
+    ctor public OutputStreamWriter(java.io.OutputStream, String) throws java.io.UnsupportedEncodingException;
+    ctor public OutputStreamWriter(java.io.OutputStream);
+    ctor public OutputStreamWriter(java.io.OutputStream, java.nio.charset.Charset);
+    ctor public OutputStreamWriter(java.io.OutputStream, java.nio.charset.CharsetEncoder);
+    method public void close() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method public String getEncoding();
+    method public void write(char[], int, int) throws java.io.IOException;
+  }
+
+  public class PipedInputStream extends java.io.InputStream {
+    ctor public PipedInputStream(java.io.PipedOutputStream) throws java.io.IOException;
+    ctor public PipedInputStream(java.io.PipedOutputStream, int) throws java.io.IOException;
+    ctor public PipedInputStream();
+    ctor public PipedInputStream(int);
+    method public void connect(java.io.PipedOutputStream) throws java.io.IOException;
+    method public int read() throws java.io.IOException;
+    method protected void receive(int) throws java.io.IOException;
+    field protected static final int PIPE_SIZE = 1024; // 0x400
+    field protected byte[] buffer;
+    field protected int in;
+    field protected int out;
+  }
+
+  public class PipedOutputStream extends java.io.OutputStream {
+    ctor public PipedOutputStream(java.io.PipedInputStream) throws java.io.IOException;
+    ctor public PipedOutputStream();
+    method public void connect(java.io.PipedInputStream) throws java.io.IOException;
+    method public void write(int) throws java.io.IOException;
+  }
+
+  public class PipedReader extends java.io.Reader {
+    ctor public PipedReader(java.io.PipedWriter) throws java.io.IOException;
+    ctor public PipedReader(java.io.PipedWriter, int) throws java.io.IOException;
+    ctor public PipedReader();
+    ctor public PipedReader(int);
+    method public void close() throws java.io.IOException;
+    method public void connect(java.io.PipedWriter) throws java.io.IOException;
+    method public int read(char[], int, int) throws java.io.IOException;
+  }
+
+  public class PipedWriter extends java.io.Writer {
+    ctor public PipedWriter(java.io.PipedReader) throws java.io.IOException;
+    ctor public PipedWriter();
+    method public void close() throws java.io.IOException;
+    method public void connect(java.io.PipedReader) throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method public void write(char[], int, int) throws java.io.IOException;
+  }
+
+  public class PrintStream extends java.io.FilterOutputStream implements java.lang.Appendable java.io.Closeable {
+    ctor public PrintStream(java.io.OutputStream);
+    ctor public PrintStream(java.io.OutputStream, boolean);
+    ctor public PrintStream(java.io.OutputStream, boolean, String) throws java.io.UnsupportedEncodingException;
+    ctor public PrintStream(String) throws java.io.FileNotFoundException;
+    ctor public PrintStream(String, String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    ctor public PrintStream(java.io.File) throws java.io.FileNotFoundException;
+    ctor public PrintStream(java.io.File, String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    method public java.io.PrintStream append(CharSequence);
+    method public java.io.PrintStream append(CharSequence, int, int);
+    method public java.io.PrintStream append(char);
+    method public boolean checkError();
+    method protected void clearError();
+    method public void close();
+    method public void flush();
+    method public java.io.PrintStream format(String, java.lang.Object...);
+    method public java.io.PrintStream format(java.util.Locale, String, java.lang.Object...);
+    method public void print(boolean);
+    method public void print(char);
+    method public void print(int);
+    method public void print(long);
+    method public void print(float);
+    method public void print(double);
+    method public void print(char[]);
+    method public void print(String);
+    method public void print(Object);
+    method public java.io.PrintStream printf(String, java.lang.Object...);
+    method public java.io.PrintStream printf(java.util.Locale, String, java.lang.Object...);
+    method public void println();
+    method public void println(boolean);
+    method public void println(char);
+    method public void println(int);
+    method public void println(long);
+    method public void println(float);
+    method public void println(double);
+    method public void println(char[]);
+    method public void println(String);
+    method public void println(Object);
+    method protected void setError();
+    method public void write(int);
+    method public void write(byte[], int, int);
+  }
+
+  public class PrintWriter extends java.io.Writer {
+    ctor public PrintWriter(@NonNull java.io.Writer);
+    ctor public PrintWriter(@NonNull java.io.Writer, boolean);
+    ctor public PrintWriter(@NonNull java.io.OutputStream);
+    ctor public PrintWriter(@NonNull java.io.OutputStream, boolean);
+    ctor public PrintWriter(@NonNull String) throws java.io.FileNotFoundException;
+    ctor public PrintWriter(@NonNull String, @NonNull String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    ctor public PrintWriter(@NonNull java.io.File) throws java.io.FileNotFoundException;
+    ctor public PrintWriter(@NonNull java.io.File, @NonNull String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    method @NonNull public java.io.PrintWriter append(@Nullable CharSequence);
+    method @NonNull public java.io.PrintWriter append(@Nullable CharSequence, int, int);
+    method @NonNull public java.io.PrintWriter append(char);
+    method public boolean checkError();
+    method protected void clearError();
+    method public void close();
+    method public void flush();
+    method @NonNull public java.io.PrintWriter format(@NonNull String, @NonNull java.lang.Object...);
+    method @NonNull public java.io.PrintWriter format(@Nullable java.util.Locale, @NonNull String, @NonNull java.lang.Object...);
+    method public void print(boolean);
+    method public void print(char);
+    method public void print(int);
+    method public void print(long);
+    method public void print(float);
+    method public void print(double);
+    method public void print(char[]);
+    method public void print(@Nullable String);
+    method public void print(@Nullable Object);
+    method @NonNull public java.io.PrintWriter printf(@NonNull String, @NonNull java.lang.Object...);
+    method @NonNull public java.io.PrintWriter printf(@Nullable java.util.Locale, @NonNull String, @NonNull java.lang.Object...);
+    method public void println();
+    method public void println(boolean);
+    method public void println(char);
+    method public void println(int);
+    method public void println(long);
+    method public void println(float);
+    method public void println(double);
+    method public void println(char[]);
+    method public void println(@Nullable String);
+    method public void println(@Nullable Object);
+    method protected void setError();
+    method public void write(int);
+    method public void write(char[], int, int);
+    method public void write(char[]);
+    method public void write(@NonNull String, int, int);
+    method public void write(@NonNull String);
+    field protected java.io.Writer out;
+  }
+
+  public class PushbackInputStream extends java.io.FilterInputStream {
+    ctor public PushbackInputStream(java.io.InputStream, int);
+    ctor public PushbackInputStream(java.io.InputStream);
+    method public void unread(int) throws java.io.IOException;
+    method public void unread(byte[], int, int) throws java.io.IOException;
+    method public void unread(byte[]) throws java.io.IOException;
+    field protected byte[] buf;
+    field protected int pos;
+  }
+
+  public class PushbackReader extends java.io.FilterReader {
+    ctor public PushbackReader(java.io.Reader, int);
+    ctor public PushbackReader(java.io.Reader);
+    method public void unread(int) throws java.io.IOException;
+    method public void unread(char[], int, int) throws java.io.IOException;
+    method public void unread(char[]) throws java.io.IOException;
+  }
+
+  public class RandomAccessFile implements java.io.Closeable java.io.DataInput java.io.DataOutput {
+    ctor public RandomAccessFile(String, String) throws java.io.FileNotFoundException;
+    ctor public RandomAccessFile(java.io.File, String) throws java.io.FileNotFoundException;
+    method public void close() throws java.io.IOException;
+    method public final java.nio.channels.FileChannel getChannel();
+    method public final java.io.FileDescriptor getFD() throws java.io.IOException;
+    method public long getFilePointer() throws java.io.IOException;
+    method public long length() throws java.io.IOException;
+    method public int read() throws java.io.IOException;
+    method public int read(byte[], int, int) throws java.io.IOException;
+    method public int read(byte[]) throws java.io.IOException;
+    method public final boolean readBoolean() throws java.io.IOException;
+    method public final byte readByte() throws java.io.IOException;
+    method public final char readChar() throws java.io.IOException;
+    method public final double readDouble() throws java.io.IOException;
+    method public final float readFloat() throws java.io.IOException;
+    method public final void readFully(byte[]) throws java.io.IOException;
+    method public final void readFully(byte[], int, int) throws java.io.IOException;
+    method public final int readInt() throws java.io.IOException;
+    method public final String readLine() throws java.io.IOException;
+    method public final long readLong() throws java.io.IOException;
+    method public final short readShort() throws java.io.IOException;
+    method public final String readUTF() throws java.io.IOException;
+    method public final int readUnsignedByte() throws java.io.IOException;
+    method public final int readUnsignedShort() throws java.io.IOException;
+    method public void seek(long) throws java.io.IOException;
+    method public void setLength(long) throws java.io.IOException;
+    method public int skipBytes(int) throws java.io.IOException;
+    method public void write(int) throws java.io.IOException;
+    method public void write(byte[]) throws java.io.IOException;
+    method public void write(byte[], int, int) throws java.io.IOException;
+    method public final void writeBoolean(boolean) throws java.io.IOException;
+    method public final void writeByte(int) throws java.io.IOException;
+    method public final void writeBytes(String) throws java.io.IOException;
+    method public final void writeChar(int) throws java.io.IOException;
+    method public final void writeChars(String) throws java.io.IOException;
+    method public final void writeDouble(double) throws java.io.IOException;
+    method public final void writeFloat(float) throws java.io.IOException;
+    method public final void writeInt(int) throws java.io.IOException;
+    method public final void writeLong(long) throws java.io.IOException;
+    method public final void writeShort(int) throws java.io.IOException;
+    method public final void writeUTF(String) throws java.io.IOException;
+  }
+
+  public abstract class Reader implements java.io.Closeable java.lang.Readable {
+    ctor protected Reader();
+    ctor protected Reader(Object);
+    method public void mark(int) throws java.io.IOException;
+    method public boolean markSupported();
+    method public int read(java.nio.CharBuffer) throws java.io.IOException;
+    method public int read() throws java.io.IOException;
+    method public int read(char[]) throws java.io.IOException;
+    method public abstract int read(char[], int, int) throws java.io.IOException;
+    method public boolean ready() throws java.io.IOException;
+    method public void reset() throws java.io.IOException;
+    method public long skip(long) throws java.io.IOException;
+    field protected Object lock;
+  }
+
+  public class SequenceInputStream extends java.io.InputStream {
+    ctor public SequenceInputStream(java.util.Enumeration<? extends java.io.InputStream>);
+    ctor public SequenceInputStream(java.io.InputStream, java.io.InputStream);
+    method public int read() throws java.io.IOException;
+  }
+
+  public interface Serializable {
+  }
+
+  public final class SerializablePermission extends java.security.BasicPermission {
+    ctor public SerializablePermission(String);
+    ctor public SerializablePermission(String, String);
+  }
+
+  public class StreamCorruptedException extends java.io.ObjectStreamException {
+    ctor public StreamCorruptedException(String);
+    ctor public StreamCorruptedException();
+  }
+
+  public class StreamTokenizer {
+    ctor @Deprecated public StreamTokenizer(java.io.InputStream);
+    ctor public StreamTokenizer(java.io.Reader);
+    method public void commentChar(int);
+    method public void eolIsSignificant(boolean);
+    method public int lineno();
+    method public void lowerCaseMode(boolean);
+    method public int nextToken() throws java.io.IOException;
+    method public void ordinaryChar(int);
+    method public void ordinaryChars(int, int);
+    method public void parseNumbers();
+    method public void pushBack();
+    method public void quoteChar(int);
+    method public void resetSyntax();
+    method public void slashSlashComments(boolean);
+    method public void slashStarComments(boolean);
+    method public void whitespaceChars(int, int);
+    method public void wordChars(int, int);
+    field public static final int TT_EOF = -1; // 0xffffffff
+    field public static final int TT_EOL = 10; // 0xa
+    field public static final int TT_NUMBER = -2; // 0xfffffffe
+    field public static final int TT_WORD = -3; // 0xfffffffd
+    field public double nval;
+    field public String sval;
+    field public int ttype;
+  }
+
+  @Deprecated public class StringBufferInputStream extends java.io.InputStream {
+    ctor @Deprecated public StringBufferInputStream(String);
+    method @Deprecated public int available();
+    method @Deprecated public int read();
+    method @Deprecated public int read(byte[], int, int);
+    method @Deprecated public void reset();
+    method @Deprecated public long skip(long);
+    field @Deprecated protected String buffer;
+    field @Deprecated protected int count;
+    field @Deprecated protected int pos;
+  }
+
+  public class StringReader extends java.io.Reader {
+    ctor public StringReader(String);
+    method public void close();
+    method public int read(char[], int, int) throws java.io.IOException;
+  }
+
+  public class StringWriter extends java.io.Writer {
+    ctor public StringWriter();
+    ctor public StringWriter(int);
+    method public java.io.StringWriter append(CharSequence);
+    method public java.io.StringWriter append(CharSequence, int, int);
+    method public java.io.StringWriter append(char);
+    method public void close() throws java.io.IOException;
+    method public void flush();
+    method public StringBuffer getBuffer();
+    method public void write(int);
+    method public void write(char[], int, int);
+    method public void write(String);
+    method public void write(String, int, int);
+  }
+
+  public class SyncFailedException extends java.io.IOException {
+    ctor public SyncFailedException(String);
+  }
+
+  public class UTFDataFormatException extends java.io.IOException {
+    ctor public UTFDataFormatException();
+    ctor public UTFDataFormatException(String);
+  }
+
+  public class UncheckedIOException extends java.lang.RuntimeException {
+    ctor public UncheckedIOException(String, java.io.IOException);
+    ctor public UncheckedIOException(java.io.IOException);
+    method public java.io.IOException getCause();
+  }
+
+  public class UnsupportedEncodingException extends java.io.IOException {
+    ctor public UnsupportedEncodingException();
+    ctor public UnsupportedEncodingException(String);
+  }
+
+  public class WriteAbortedException extends java.io.ObjectStreamException {
+    ctor public WriteAbortedException(String, Exception);
+    field public Exception detail;
+  }
+
+  public abstract class Writer implements java.lang.Appendable java.io.Closeable java.io.Flushable {
+    ctor protected Writer();
+    ctor protected Writer(Object);
+    method public java.io.Writer append(CharSequence) throws java.io.IOException;
+    method public java.io.Writer append(CharSequence, int, int) throws java.io.IOException;
+    method public java.io.Writer append(char) throws java.io.IOException;
+    method public void write(int) throws java.io.IOException;
+    method public void write(char[]) throws java.io.IOException;
+    method public abstract void write(char[], int, int) throws java.io.IOException;
+    method public void write(String) throws java.io.IOException;
+    method public void write(String, int, int) throws java.io.IOException;
+    field protected Object lock;
+  }
+
+}
+
+package java.lang {
+
+  public class AbstractMethodError extends java.lang.IncompatibleClassChangeError {
+    ctor public AbstractMethodError();
+    ctor public AbstractMethodError(String);
+  }
+
+  public interface Appendable {
+    method @NonNull public Appendable append(@Nullable CharSequence) throws java.io.IOException;
+    method @NonNull public Appendable append(@Nullable CharSequence, int, int) throws java.io.IOException;
+    method @NonNull public Appendable append(char) throws java.io.IOException;
+  }
+
+  public class ArithmeticException extends java.lang.RuntimeException {
+    ctor public ArithmeticException();
+    ctor public ArithmeticException(String);
+  }
+
+  public class ArrayIndexOutOfBoundsException extends java.lang.IndexOutOfBoundsException {
+    ctor public ArrayIndexOutOfBoundsException();
+    ctor public ArrayIndexOutOfBoundsException(int);
+    ctor public ArrayIndexOutOfBoundsException(String);
+  }
+
+  public class ArrayStoreException extends java.lang.RuntimeException {
+    ctor public ArrayStoreException();
+    ctor public ArrayStoreException(String);
+  }
+
+  public class AssertionError extends java.lang.Error {
+    ctor public AssertionError();
+    ctor public AssertionError(Object);
+    ctor public AssertionError(boolean);
+    ctor public AssertionError(char);
+    ctor public AssertionError(int);
+    ctor public AssertionError(long);
+    ctor public AssertionError(float);
+    ctor public AssertionError(double);
+    ctor public AssertionError(String, Throwable);
+  }
+
+  public interface AutoCloseable {
+    method public void close() throws java.lang.Exception;
+  }
+
+  public final class Boolean implements java.lang.Comparable<java.lang.Boolean> java.io.Serializable {
+    ctor public Boolean(boolean);
+    ctor public Boolean(@Nullable String);
+    method public boolean booleanValue();
+    method public static int compare(boolean, boolean);
+    method public int compareTo(@NonNull Boolean);
+    method public static boolean getBoolean(@NonNull String);
+    method public static int hashCode(boolean);
+    method public static boolean logicalAnd(boolean, boolean);
+    method public static boolean logicalOr(boolean, boolean);
+    method public static boolean logicalXor(boolean, boolean);
+    method public static boolean parseBoolean(@Nullable String);
+    method @NonNull public static String toString(boolean);
+    method @NonNull public static Boolean valueOf(boolean);
+    method @NonNull public static Boolean valueOf(@Nullable String);
+    field public static final Boolean FALSE;
+    field public static final Boolean TRUE;
+    field public static final Class<java.lang.Boolean> TYPE;
+  }
+
+  public class BootstrapMethodError extends java.lang.LinkageError {
+    ctor public BootstrapMethodError();
+    ctor public BootstrapMethodError(String);
+    ctor public BootstrapMethodError(String, Throwable);
+    ctor public BootstrapMethodError(Throwable);
+  }
+
+  public final class Byte extends java.lang.Number implements java.lang.Comparable<java.lang.Byte> {
+    ctor @Deprecated public Byte(byte);
+    ctor @Deprecated public Byte(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int compare(byte, byte);
+    method public int compareTo(@NonNull Byte);
+    method public static int compareUnsigned(byte, byte);
+    method @NonNull public static Byte decode(@NonNull String) throws java.lang.NumberFormatException;
+    method public double doubleValue();
+    method public float floatValue();
+    method public static int hashCode(byte);
+    method public int intValue();
+    method public long longValue();
+    method public static byte parseByte(@NonNull String, int) throws java.lang.NumberFormatException;
+    method public static byte parseByte(@NonNull String) throws java.lang.NumberFormatException;
+    method @NonNull public static String toString(byte);
+    method public static int toUnsignedInt(byte);
+    method public static long toUnsignedLong(byte);
+    method @NonNull public static Byte valueOf(byte);
+    method @NonNull public static Byte valueOf(@NonNull String, int) throws java.lang.NumberFormatException;
+    method @NonNull public static Byte valueOf(@NonNull String) throws java.lang.NumberFormatException;
+    field public static final int BYTES = 1; // 0x1
+    field public static final byte MAX_VALUE = 127; // 0x7f
+    field public static final byte MIN_VALUE = -128; // 0xffffff80
+    field public static final int SIZE = 8; // 0x8
+    field public static final Class<java.lang.Byte> TYPE;
+  }
+
+  public interface CharSequence {
+    method public char charAt(int);
+    method @NonNull public default java.util.stream.IntStream chars();
+    method @NonNull public default java.util.stream.IntStream codePoints();
+    method public int length();
+    method @NonNull public CharSequence subSequence(int, int);
+    method @NonNull public String toString();
+  }
+
+  public final class Character implements java.lang.Comparable<java.lang.Character> java.io.Serializable {
+    ctor public Character(char);
+    method public static int charCount(int);
+    method public char charValue();
+    method public static int codePointAt(@NonNull CharSequence, int);
+    method public static int codePointAt(char[], int);
+    method public static int codePointAt(char[], int, int);
+    method public static int codePointBefore(@NonNull CharSequence, int);
+    method public static int codePointBefore(char[], int);
+    method public static int codePointBefore(char[], int, int);
+    method public static int codePointCount(@NonNull CharSequence, int, int);
+    method public static int codePointCount(char[], int, int);
+    method public static int compare(char, char);
+    method public int compareTo(@NonNull Character);
+    method public static int digit(char, int);
+    method public static int digit(int, int);
+    method public static char forDigit(int, int);
+    method public static byte getDirectionality(char);
+    method public static byte getDirectionality(int);
+    method @Nullable public static String getName(int);
+    method public static int getNumericValue(char);
+    method public static int getNumericValue(int);
+    method public static int getType(char);
+    method public static int getType(int);
+    method public static int hashCode(char);
+    method public static char highSurrogate(int);
+    method public static boolean isAlphabetic(int);
+    method public static boolean isBmpCodePoint(int);
+    method public static boolean isDefined(char);
+    method public static boolean isDefined(int);
+    method public static boolean isDigit(char);
+    method public static boolean isDigit(int);
+    method public static boolean isHighSurrogate(char);
+    method public static boolean isISOControl(char);
+    method public static boolean isISOControl(int);
+    method public static boolean isIdentifierIgnorable(char);
+    method public static boolean isIdentifierIgnorable(int);
+    method public static boolean isIdeographic(int);
+    method public static boolean isJavaIdentifierPart(char);
+    method public static boolean isJavaIdentifierPart(int);
+    method public static boolean isJavaIdentifierStart(char);
+    method public static boolean isJavaIdentifierStart(int);
+    method @Deprecated public static boolean isJavaLetter(char);
+    method @Deprecated public static boolean isJavaLetterOrDigit(char);
+    method public static boolean isLetter(char);
+    method public static boolean isLetter(int);
+    method public static boolean isLetterOrDigit(char);
+    method public static boolean isLetterOrDigit(int);
+    method public static boolean isLowSurrogate(char);
+    method public static boolean isLowerCase(char);
+    method public static boolean isLowerCase(int);
+    method public static boolean isMirrored(char);
+    method public static boolean isMirrored(int);
+    method @Deprecated public static boolean isSpace(char);
+    method public static boolean isSpaceChar(char);
+    method public static boolean isSpaceChar(int);
+    method public static boolean isSupplementaryCodePoint(int);
+    method public static boolean isSurrogate(char);
+    method public static boolean isSurrogatePair(char, char);
+    method public static boolean isTitleCase(char);
+    method public static boolean isTitleCase(int);
+    method public static boolean isUnicodeIdentifierPart(char);
+    method public static boolean isUnicodeIdentifierPart(int);
+    method public static boolean isUnicodeIdentifierStart(char);
+    method public static boolean isUnicodeIdentifierStart(int);
+    method public static boolean isUpperCase(char);
+    method public static boolean isUpperCase(int);
+    method public static boolean isValidCodePoint(int);
+    method public static boolean isWhitespace(char);
+    method public static boolean isWhitespace(int);
+    method public static char lowSurrogate(int);
+    method public static int offsetByCodePoints(@NonNull CharSequence, int, int);
+    method public static int offsetByCodePoints(char[], int, int, int, int);
+    method public static char reverseBytes(char);
+    method public static int toChars(int, char[], int);
+    method public static char[] toChars(int);
+    method public static int toCodePoint(char, char);
+    method public static char toLowerCase(char);
+    method public static int toLowerCase(int);
+    method @NonNull public static String toString(char);
+    method public static char toTitleCase(char);
+    method public static int toTitleCase(int);
+    method public static char toUpperCase(char);
+    method public static int toUpperCase(int);
+    method @NonNull public static Character valueOf(char);
+    field public static final int BYTES = 2; // 0x2
+    field public static final byte COMBINING_SPACING_MARK = 8; // 0x8
+    field public static final byte CONNECTOR_PUNCTUATION = 23; // 0x17
+    field public static final byte CONTROL = 15; // 0xf
+    field public static final byte CURRENCY_SYMBOL = 26; // 0x1a
+    field public static final byte DASH_PUNCTUATION = 20; // 0x14
+    field public static final byte DECIMAL_DIGIT_NUMBER = 9; // 0x9
+    field public static final byte DIRECTIONALITY_ARABIC_NUMBER = 6; // 0x6
+    field public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 9; // 0x9
+    field public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = 7; // 0x7
+    field public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = 3; // 0x3
+    field public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = 4; // 0x4
+    field public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = 5; // 0x5
+    field public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = 0; // 0x0
+    field public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = 14; // 0xe
+    field public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = 15; // 0xf
+    field public static final byte DIRECTIONALITY_NONSPACING_MARK = 8; // 0x8
+    field public static final byte DIRECTIONALITY_OTHER_NEUTRALS = 13; // 0xd
+    field public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = 10; // 0xa
+    field public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18; // 0x12
+    field public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = 1; // 0x1
+    field public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2; // 0x2
+    field public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = 16; // 0x10
+    field public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = 17; // 0x11
+    field public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = 11; // 0xb
+    field public static final byte DIRECTIONALITY_UNDEFINED = -1; // 0xffffffff
+    field public static final byte DIRECTIONALITY_WHITESPACE = 12; // 0xc
+    field public static final byte ENCLOSING_MARK = 7; // 0x7
+    field public static final byte END_PUNCTUATION = 22; // 0x16
+    field public static final byte FINAL_QUOTE_PUNCTUATION = 30; // 0x1e
+    field public static final byte FORMAT = 16; // 0x10
+    field public static final byte INITIAL_QUOTE_PUNCTUATION = 29; // 0x1d
+    field public static final byte LETTER_NUMBER = 10; // 0xa
+    field public static final byte LINE_SEPARATOR = 13; // 0xd
+    field public static final byte LOWERCASE_LETTER = 2; // 0x2
+    field public static final byte MATH_SYMBOL = 25; // 0x19
+    field public static final int MAX_CODE_POINT = 1114111; // 0x10ffff
+    field public static final char MAX_HIGH_SURROGATE = 56319; // 0xdbff '\udbff'
+    field public static final char MAX_LOW_SURROGATE = 57343; // 0xdfff '\udfff'
+    field public static final int MAX_RADIX = 36; // 0x24
+    field public static final char MAX_SURROGATE = 57343; // 0xdfff '\udfff'
+    field public static final char MAX_VALUE = 65535; // 0xffff '\uffff'
+    field public static final int MIN_CODE_POINT = 0; // 0x0
+    field public static final char MIN_HIGH_SURROGATE = 55296; // 0xd800 '\ud800'
+    field public static final char MIN_LOW_SURROGATE = 56320; // 0xdc00 '\udc00'
+    field public static final int MIN_RADIX = 2; // 0x2
+    field public static final int MIN_SUPPLEMENTARY_CODE_POINT = 65536; // 0x10000
+    field public static final char MIN_SURROGATE = 55296; // 0xd800 '\ud800'
+    field public static final char MIN_VALUE = 0; // 0x0000 '\u0000'
+    field public static final byte MODIFIER_LETTER = 4; // 0x4
+    field public static final byte MODIFIER_SYMBOL = 27; // 0x1b
+    field public static final byte NON_SPACING_MARK = 6; // 0x6
+    field public static final byte OTHER_LETTER = 5; // 0x5
+    field public static final byte OTHER_NUMBER = 11; // 0xb
+    field public static final byte OTHER_PUNCTUATION = 24; // 0x18
+    field public static final byte OTHER_SYMBOL = 28; // 0x1c
+    field public static final byte PARAGRAPH_SEPARATOR = 14; // 0xe
+    field public static final byte PRIVATE_USE = 18; // 0x12
+    field public static final int SIZE = 16; // 0x10
+    field public static final byte SPACE_SEPARATOR = 12; // 0xc
+    field public static final byte START_PUNCTUATION = 21; // 0x15
+    field public static final byte SURROGATE = 19; // 0x13
+    field public static final byte TITLECASE_LETTER = 3; // 0x3
+    field public static final Class<java.lang.Character> TYPE;
+    field public static final byte UNASSIGNED = 0; // 0x0
+    field public static final byte UPPERCASE_LETTER = 1; // 0x1
+  }
+
+  public static class Character.Subset {
+    ctor protected Character.Subset(@NonNull String);
+    method public final boolean equals(@Nullable Object);
+    method public final int hashCode();
+    method @NonNull public final String toString();
+  }
+
+  public static final class Character.UnicodeBlock extends java.lang.Character.Subset {
+    method @NonNull public static java.lang.Character.UnicodeBlock forName(@NonNull String);
+    method @Nullable public static java.lang.Character.UnicodeBlock of(char);
+    method @Nullable public static java.lang.Character.UnicodeBlock of(int);
+    field public static final java.lang.Character.UnicodeBlock AEGEAN_NUMBERS;
+    field public static final java.lang.Character.UnicodeBlock ALCHEMICAL_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock ALPHABETIC_PRESENTATION_FORMS;
+    field public static final java.lang.Character.UnicodeBlock ANCIENT_GREEK_MUSICAL_NOTATION;
+    field public static final java.lang.Character.UnicodeBlock ANCIENT_GREEK_NUMBERS;
+    field public static final java.lang.Character.UnicodeBlock ANCIENT_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock ARABIC;
+    field public static final java.lang.Character.UnicodeBlock ARABIC_EXTENDED_A;
+    field public static final java.lang.Character.UnicodeBlock ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock ARABIC_PRESENTATION_FORMS_A;
+    field public static final java.lang.Character.UnicodeBlock ARABIC_PRESENTATION_FORMS_B;
+    field public static final java.lang.Character.UnicodeBlock ARABIC_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock ARMENIAN;
+    field public static final java.lang.Character.UnicodeBlock ARROWS;
+    field public static final java.lang.Character.UnicodeBlock AVESTAN;
+    field public static final java.lang.Character.UnicodeBlock BALINESE;
+    field public static final java.lang.Character.UnicodeBlock BAMUM;
+    field public static final java.lang.Character.UnicodeBlock BAMUM_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock BASIC_LATIN;
+    field public static final java.lang.Character.UnicodeBlock BATAK;
+    field public static final java.lang.Character.UnicodeBlock BENGALI;
+    field public static final java.lang.Character.UnicodeBlock BLOCK_ELEMENTS;
+    field public static final java.lang.Character.UnicodeBlock BOPOMOFO;
+    field public static final java.lang.Character.UnicodeBlock BOPOMOFO_EXTENDED;
+    field public static final java.lang.Character.UnicodeBlock BOX_DRAWING;
+    field public static final java.lang.Character.UnicodeBlock BRAHMI;
+    field public static final java.lang.Character.UnicodeBlock BRAILLE_PATTERNS;
+    field public static final java.lang.Character.UnicodeBlock BUGINESE;
+    field public static final java.lang.Character.UnicodeBlock BUHID;
+    field public static final java.lang.Character.UnicodeBlock BYZANTINE_MUSICAL_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock CARIAN;
+    field public static final java.lang.Character.UnicodeBlock CHAKMA;
+    field public static final java.lang.Character.UnicodeBlock CHAM;
+    field public static final java.lang.Character.UnicodeBlock CHEROKEE;
+    field public static final java.lang.Character.UnicodeBlock CJK_COMPATIBILITY;
+    field public static final java.lang.Character.UnicodeBlock CJK_COMPATIBILITY_FORMS;
+    field public static final java.lang.Character.UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS;
+    field public static final java.lang.Character.UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock CJK_RADICALS_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock CJK_STROKES;
+    field public static final java.lang.Character.UnicodeBlock CJK_SYMBOLS_AND_PUNCTUATION;
+    field public static final java.lang.Character.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS;
+    field public static final java.lang.Character.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A;
+    field public static final java.lang.Character.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B;
+    field public static final java.lang.Character.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C;
+    field public static final java.lang.Character.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D;
+    field public static final java.lang.Character.UnicodeBlock COMBINING_DIACRITICAL_MARKS;
+    field public static final java.lang.Character.UnicodeBlock COMBINING_DIACRITICAL_MARKS_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock COMBINING_HALF_MARKS;
+    field public static final java.lang.Character.UnicodeBlock COMBINING_MARKS_FOR_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock COMMON_INDIC_NUMBER_FORMS;
+    field public static final java.lang.Character.UnicodeBlock CONTROL_PICTURES;
+    field public static final java.lang.Character.UnicodeBlock COPTIC;
+    field public static final java.lang.Character.UnicodeBlock COUNTING_ROD_NUMERALS;
+    field public static final java.lang.Character.UnicodeBlock CUNEIFORM;
+    field public static final java.lang.Character.UnicodeBlock CUNEIFORM_NUMBERS_AND_PUNCTUATION;
+    field public static final java.lang.Character.UnicodeBlock CURRENCY_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock CYPRIOT_SYLLABARY;
+    field public static final java.lang.Character.UnicodeBlock CYRILLIC;
+    field public static final java.lang.Character.UnicodeBlock CYRILLIC_EXTENDED_A;
+    field public static final java.lang.Character.UnicodeBlock CYRILLIC_EXTENDED_B;
+    field public static final java.lang.Character.UnicodeBlock CYRILLIC_SUPPLEMENTARY;
+    field public static final java.lang.Character.UnicodeBlock DESERET;
+    field public static final java.lang.Character.UnicodeBlock DEVANAGARI;
+    field public static final java.lang.Character.UnicodeBlock DEVANAGARI_EXTENDED;
+    field public static final java.lang.Character.UnicodeBlock DINGBATS;
+    field public static final java.lang.Character.UnicodeBlock DOMINO_TILES;
+    field public static final java.lang.Character.UnicodeBlock EGYPTIAN_HIEROGLYPHS;
+    field public static final java.lang.Character.UnicodeBlock EMOTICONS;
+    field public static final java.lang.Character.UnicodeBlock ENCLOSED_ALPHANUMERICS;
+    field public static final java.lang.Character.UnicodeBlock ENCLOSED_ALPHANUMERIC_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock ENCLOSED_CJK_LETTERS_AND_MONTHS;
+    field public static final java.lang.Character.UnicodeBlock ENCLOSED_IDEOGRAPHIC_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock ETHIOPIC;
+    field public static final java.lang.Character.UnicodeBlock ETHIOPIC_EXTENDED;
+    field public static final java.lang.Character.UnicodeBlock ETHIOPIC_EXTENDED_A;
+    field public static final java.lang.Character.UnicodeBlock ETHIOPIC_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock GENERAL_PUNCTUATION;
+    field public static final java.lang.Character.UnicodeBlock GEOMETRIC_SHAPES;
+    field public static final java.lang.Character.UnicodeBlock GEORGIAN;
+    field public static final java.lang.Character.UnicodeBlock GEORGIAN_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock GLAGOLITIC;
+    field public static final java.lang.Character.UnicodeBlock GOTHIC;
+    field public static final java.lang.Character.UnicodeBlock GREEK;
+    field public static final java.lang.Character.UnicodeBlock GREEK_EXTENDED;
+    field public static final java.lang.Character.UnicodeBlock GUJARATI;
+    field public static final java.lang.Character.UnicodeBlock GURMUKHI;
+    field public static final java.lang.Character.UnicodeBlock HALFWIDTH_AND_FULLWIDTH_FORMS;
+    field public static final java.lang.Character.UnicodeBlock HANGUL_COMPATIBILITY_JAMO;
+    field public static final java.lang.Character.UnicodeBlock HANGUL_JAMO;
+    field public static final java.lang.Character.UnicodeBlock HANGUL_JAMO_EXTENDED_A;
+    field public static final java.lang.Character.UnicodeBlock HANGUL_JAMO_EXTENDED_B;
+    field public static final java.lang.Character.UnicodeBlock HANGUL_SYLLABLES;
+    field public static final java.lang.Character.UnicodeBlock HANUNOO;
+    field public static final java.lang.Character.UnicodeBlock HEBREW;
+    field public static final java.lang.Character.UnicodeBlock HIGH_PRIVATE_USE_SURROGATES;
+    field public static final java.lang.Character.UnicodeBlock HIGH_SURROGATES;
+    field public static final java.lang.Character.UnicodeBlock HIRAGANA;
+    field public static final java.lang.Character.UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS;
+    field public static final java.lang.Character.UnicodeBlock IMPERIAL_ARAMAIC;
+    field public static final java.lang.Character.UnicodeBlock INSCRIPTIONAL_PAHLAVI;
+    field public static final java.lang.Character.UnicodeBlock INSCRIPTIONAL_PARTHIAN;
+    field public static final java.lang.Character.UnicodeBlock IPA_EXTENSIONS;
+    field public static final java.lang.Character.UnicodeBlock JAVANESE;
+    field public static final java.lang.Character.UnicodeBlock KAITHI;
+    field public static final java.lang.Character.UnicodeBlock KANA_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock KANBUN;
+    field public static final java.lang.Character.UnicodeBlock KANGXI_RADICALS;
+    field public static final java.lang.Character.UnicodeBlock KANNADA;
+    field public static final java.lang.Character.UnicodeBlock KATAKANA;
+    field public static final java.lang.Character.UnicodeBlock KATAKANA_PHONETIC_EXTENSIONS;
+    field public static final java.lang.Character.UnicodeBlock KAYAH_LI;
+    field public static final java.lang.Character.UnicodeBlock KHAROSHTHI;
+    field public static final java.lang.Character.UnicodeBlock KHMER;
+    field public static final java.lang.Character.UnicodeBlock KHMER_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock LAO;
+    field public static final java.lang.Character.UnicodeBlock LATIN_1_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock LATIN_EXTENDED_A;
+    field public static final java.lang.Character.UnicodeBlock LATIN_EXTENDED_ADDITIONAL;
+    field public static final java.lang.Character.UnicodeBlock LATIN_EXTENDED_B;
+    field public static final java.lang.Character.UnicodeBlock LATIN_EXTENDED_C;
+    field public static final java.lang.Character.UnicodeBlock LATIN_EXTENDED_D;
+    field public static final java.lang.Character.UnicodeBlock LEPCHA;
+    field public static final java.lang.Character.UnicodeBlock LETTERLIKE_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock LIMBU;
+    field public static final java.lang.Character.UnicodeBlock LINEAR_B_IDEOGRAMS;
+    field public static final java.lang.Character.UnicodeBlock LINEAR_B_SYLLABARY;
+    field public static final java.lang.Character.UnicodeBlock LISU;
+    field public static final java.lang.Character.UnicodeBlock LOW_SURROGATES;
+    field public static final java.lang.Character.UnicodeBlock LYCIAN;
+    field public static final java.lang.Character.UnicodeBlock LYDIAN;
+    field public static final java.lang.Character.UnicodeBlock MAHJONG_TILES;
+    field public static final java.lang.Character.UnicodeBlock MALAYALAM;
+    field public static final java.lang.Character.UnicodeBlock MANDAIC;
+    field public static final java.lang.Character.UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock MATHEMATICAL_OPERATORS;
+    field public static final java.lang.Character.UnicodeBlock MEETEI_MAYEK;
+    field public static final java.lang.Character.UnicodeBlock MEETEI_MAYEK_EXTENSIONS;
+    field public static final java.lang.Character.UnicodeBlock MEROITIC_CURSIVE;
+    field public static final java.lang.Character.UnicodeBlock MEROITIC_HIEROGLYPHS;
+    field public static final java.lang.Character.UnicodeBlock MIAO;
+    field public static final java.lang.Character.UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A;
+    field public static final java.lang.Character.UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B;
+    field public static final java.lang.Character.UnicodeBlock MISCELLANEOUS_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock MISCELLANEOUS_SYMBOLS_AND_ARROWS;
+    field public static final java.lang.Character.UnicodeBlock MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS;
+    field public static final java.lang.Character.UnicodeBlock MISCELLANEOUS_TECHNICAL;
+    field public static final java.lang.Character.UnicodeBlock MODIFIER_TONE_LETTERS;
+    field public static final java.lang.Character.UnicodeBlock MONGOLIAN;
+    field public static final java.lang.Character.UnicodeBlock MUSICAL_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock MYANMAR;
+    field public static final java.lang.Character.UnicodeBlock MYANMAR_EXTENDED_A;
+    field public static final java.lang.Character.UnicodeBlock NEW_TAI_LUE;
+    field public static final java.lang.Character.UnicodeBlock NKO;
+    field public static final java.lang.Character.UnicodeBlock NUMBER_FORMS;
+    field public static final java.lang.Character.UnicodeBlock OGHAM;
+    field public static final java.lang.Character.UnicodeBlock OLD_ITALIC;
+    field public static final java.lang.Character.UnicodeBlock OLD_PERSIAN;
+    field public static final java.lang.Character.UnicodeBlock OLD_SOUTH_ARABIAN;
+    field public static final java.lang.Character.UnicodeBlock OLD_TURKIC;
+    field public static final java.lang.Character.UnicodeBlock OL_CHIKI;
+    field public static final java.lang.Character.UnicodeBlock OPTICAL_CHARACTER_RECOGNITION;
+    field public static final java.lang.Character.UnicodeBlock ORIYA;
+    field public static final java.lang.Character.UnicodeBlock OSMANYA;
+    field public static final java.lang.Character.UnicodeBlock PHAGS_PA;
+    field public static final java.lang.Character.UnicodeBlock PHAISTOS_DISC;
+    field public static final java.lang.Character.UnicodeBlock PHOENICIAN;
+    field public static final java.lang.Character.UnicodeBlock PHONETIC_EXTENSIONS;
+    field public static final java.lang.Character.UnicodeBlock PHONETIC_EXTENSIONS_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock PLAYING_CARDS;
+    field public static final java.lang.Character.UnicodeBlock PRIVATE_USE_AREA;
+    field public static final java.lang.Character.UnicodeBlock REJANG;
+    field public static final java.lang.Character.UnicodeBlock RUMI_NUMERAL_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock RUNIC;
+    field public static final java.lang.Character.UnicodeBlock SAMARITAN;
+    field public static final java.lang.Character.UnicodeBlock SAURASHTRA;
+    field public static final java.lang.Character.UnicodeBlock SHARADA;
+    field public static final java.lang.Character.UnicodeBlock SHAVIAN;
+    field public static final java.lang.Character.UnicodeBlock SINHALA;
+    field public static final java.lang.Character.UnicodeBlock SMALL_FORM_VARIANTS;
+    field public static final java.lang.Character.UnicodeBlock SORA_SOMPENG;
+    field public static final java.lang.Character.UnicodeBlock SPACING_MODIFIER_LETTERS;
+    field public static final java.lang.Character.UnicodeBlock SPECIALS;
+    field public static final java.lang.Character.UnicodeBlock SUNDANESE;
+    field public static final java.lang.Character.UnicodeBlock SUNDANESE_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock SUPERSCRIPTS_AND_SUBSCRIPTS;
+    field public static final java.lang.Character.UnicodeBlock SUPPLEMENTAL_ARROWS_A;
+    field public static final java.lang.Character.UnicodeBlock SUPPLEMENTAL_ARROWS_B;
+    field public static final java.lang.Character.UnicodeBlock SUPPLEMENTAL_MATHEMATICAL_OPERATORS;
+    field public static final java.lang.Character.UnicodeBlock SUPPLEMENTAL_PUNCTUATION;
+    field public static final java.lang.Character.UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_A;
+    field public static final java.lang.Character.UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_B;
+    field @Deprecated public static final java.lang.Character.UnicodeBlock SURROGATES_AREA;
+    field public static final java.lang.Character.UnicodeBlock SYLOTI_NAGRI;
+    field public static final java.lang.Character.UnicodeBlock SYRIAC;
+    field public static final java.lang.Character.UnicodeBlock TAGALOG;
+    field public static final java.lang.Character.UnicodeBlock TAGBANWA;
+    field public static final java.lang.Character.UnicodeBlock TAGS;
+    field public static final java.lang.Character.UnicodeBlock TAI_LE;
+    field public static final java.lang.Character.UnicodeBlock TAI_THAM;
+    field public static final java.lang.Character.UnicodeBlock TAI_VIET;
+    field public static final java.lang.Character.UnicodeBlock TAI_XUAN_JING_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock TAKRI;
+    field public static final java.lang.Character.UnicodeBlock TAMIL;
+    field public static final java.lang.Character.UnicodeBlock TELUGU;
+    field public static final java.lang.Character.UnicodeBlock THAANA;
+    field public static final java.lang.Character.UnicodeBlock THAI;
+    field public static final java.lang.Character.UnicodeBlock TIBETAN;
+    field public static final java.lang.Character.UnicodeBlock TIFINAGH;
+    field public static final java.lang.Character.UnicodeBlock TRANSPORT_AND_MAP_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock UGARITIC;
+    field public static final java.lang.Character.UnicodeBlock UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS;
+    field public static final java.lang.Character.UnicodeBlock UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED;
+    field public static final java.lang.Character.UnicodeBlock VAI;
+    field public static final java.lang.Character.UnicodeBlock VARIATION_SELECTORS;
+    field public static final java.lang.Character.UnicodeBlock VARIATION_SELECTORS_SUPPLEMENT;
+    field public static final java.lang.Character.UnicodeBlock VEDIC_EXTENSIONS;
+    field public static final java.lang.Character.UnicodeBlock VERTICAL_FORMS;
+    field public static final java.lang.Character.UnicodeBlock YIJING_HEXAGRAM_SYMBOLS;
+    field public static final java.lang.Character.UnicodeBlock YI_RADICALS;
+    field public static final java.lang.Character.UnicodeBlock YI_SYLLABLES;
+  }
+
+  public enum Character.UnicodeScript {
+    method @NonNull public static java.lang.Character.UnicodeScript forName(@NonNull String);
+    method @NonNull public static java.lang.Character.UnicodeScript of(int);
+    enum_constant public static final java.lang.Character.UnicodeScript ARABIC;
+    enum_constant public static final java.lang.Character.UnicodeScript ARMENIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript AVESTAN;
+    enum_constant public static final java.lang.Character.UnicodeScript BALINESE;
+    enum_constant public static final java.lang.Character.UnicodeScript BAMUM;
+    enum_constant public static final java.lang.Character.UnicodeScript BATAK;
+    enum_constant public static final java.lang.Character.UnicodeScript BENGALI;
+    enum_constant public static final java.lang.Character.UnicodeScript BOPOMOFO;
+    enum_constant public static final java.lang.Character.UnicodeScript BRAHMI;
+    enum_constant public static final java.lang.Character.UnicodeScript BRAILLE;
+    enum_constant public static final java.lang.Character.UnicodeScript BUGINESE;
+    enum_constant public static final java.lang.Character.UnicodeScript BUHID;
+    enum_constant public static final java.lang.Character.UnicodeScript CANADIAN_ABORIGINAL;
+    enum_constant public static final java.lang.Character.UnicodeScript CARIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript CHAKMA;
+    enum_constant public static final java.lang.Character.UnicodeScript CHAM;
+    enum_constant public static final java.lang.Character.UnicodeScript CHEROKEE;
+    enum_constant public static final java.lang.Character.UnicodeScript COMMON;
+    enum_constant public static final java.lang.Character.UnicodeScript COPTIC;
+    enum_constant public static final java.lang.Character.UnicodeScript CUNEIFORM;
+    enum_constant public static final java.lang.Character.UnicodeScript CYPRIOT;
+    enum_constant public static final java.lang.Character.UnicodeScript CYRILLIC;
+    enum_constant public static final java.lang.Character.UnicodeScript DESERET;
+    enum_constant public static final java.lang.Character.UnicodeScript DEVANAGARI;
+    enum_constant public static final java.lang.Character.UnicodeScript EGYPTIAN_HIEROGLYPHS;
+    enum_constant public static final java.lang.Character.UnicodeScript ETHIOPIC;
+    enum_constant public static final java.lang.Character.UnicodeScript GEORGIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript GLAGOLITIC;
+    enum_constant public static final java.lang.Character.UnicodeScript GOTHIC;
+    enum_constant public static final java.lang.Character.UnicodeScript GREEK;
+    enum_constant public static final java.lang.Character.UnicodeScript GUJARATI;
+    enum_constant public static final java.lang.Character.UnicodeScript GURMUKHI;
+    enum_constant public static final java.lang.Character.UnicodeScript HAN;
+    enum_constant public static final java.lang.Character.UnicodeScript HANGUL;
+    enum_constant public static final java.lang.Character.UnicodeScript HANUNOO;
+    enum_constant public static final java.lang.Character.UnicodeScript HEBREW;
+    enum_constant public static final java.lang.Character.UnicodeScript HIRAGANA;
+    enum_constant public static final java.lang.Character.UnicodeScript IMPERIAL_ARAMAIC;
+    enum_constant public static final java.lang.Character.UnicodeScript INHERITED;
+    enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PAHLAVI;
+    enum_constant public static final java.lang.Character.UnicodeScript INSCRIPTIONAL_PARTHIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript JAVANESE;
+    enum_constant public static final java.lang.Character.UnicodeScript KAITHI;
+    enum_constant public static final java.lang.Character.UnicodeScript KANNADA;
+    enum_constant public static final java.lang.Character.UnicodeScript KATAKANA;
+    enum_constant public static final java.lang.Character.UnicodeScript KAYAH_LI;
+    enum_constant public static final java.lang.Character.UnicodeScript KHAROSHTHI;
+    enum_constant public static final java.lang.Character.UnicodeScript KHMER;
+    enum_constant public static final java.lang.Character.UnicodeScript LAO;
+    enum_constant public static final java.lang.Character.UnicodeScript LATIN;
+    enum_constant public static final java.lang.Character.UnicodeScript LEPCHA;
+    enum_constant public static final java.lang.Character.UnicodeScript LIMBU;
+    enum_constant public static final java.lang.Character.UnicodeScript LINEAR_B;
+    enum_constant public static final java.lang.Character.UnicodeScript LISU;
+    enum_constant public static final java.lang.Character.UnicodeScript LYCIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript LYDIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript MALAYALAM;
+    enum_constant public static final java.lang.Character.UnicodeScript MANDAIC;
+    enum_constant public static final java.lang.Character.UnicodeScript MEETEI_MAYEK;
+    enum_constant public static final java.lang.Character.UnicodeScript MEROITIC_CURSIVE;
+    enum_constant public static final java.lang.Character.UnicodeScript MEROITIC_HIEROGLYPHS;
+    enum_constant public static final java.lang.Character.UnicodeScript MIAO;
+    enum_constant public static final java.lang.Character.UnicodeScript MONGOLIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript MYANMAR;
+    enum_constant public static final java.lang.Character.UnicodeScript NEW_TAI_LUE;
+    enum_constant public static final java.lang.Character.UnicodeScript NKO;
+    enum_constant public static final java.lang.Character.UnicodeScript OGHAM;
+    enum_constant public static final java.lang.Character.UnicodeScript OLD_ITALIC;
+    enum_constant public static final java.lang.Character.UnicodeScript OLD_PERSIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript OLD_SOUTH_ARABIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript OLD_TURKIC;
+    enum_constant public static final java.lang.Character.UnicodeScript OL_CHIKI;
+    enum_constant public static final java.lang.Character.UnicodeScript ORIYA;
+    enum_constant public static final java.lang.Character.UnicodeScript OSMANYA;
+    enum_constant public static final java.lang.Character.UnicodeScript PHAGS_PA;
+    enum_constant public static final java.lang.Character.UnicodeScript PHOENICIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript REJANG;
+    enum_constant public static final java.lang.Character.UnicodeScript RUNIC;
+    enum_constant public static final java.lang.Character.UnicodeScript SAMARITAN;
+    enum_constant public static final java.lang.Character.UnicodeScript SAURASHTRA;
+    enum_constant public static final java.lang.Character.UnicodeScript SHARADA;
+    enum_constant public static final java.lang.Character.UnicodeScript SHAVIAN;
+    enum_constant public static final java.lang.Character.UnicodeScript SINHALA;
+    enum_constant public static final java.lang.Character.UnicodeScript SORA_SOMPENG;
+    enum_constant public static final java.lang.Character.UnicodeScript SUNDANESE;
+    enum_constant public static final java.lang.Character.UnicodeScript SYLOTI_NAGRI;
+    enum_constant public static final java.lang.Character.UnicodeScript SYRIAC;
+    enum_constant public static final java.lang.Character.UnicodeScript TAGALOG;
+    enum_constant public static final java.lang.Character.UnicodeScript TAGBANWA;
+    enum_constant public static final java.lang.Character.UnicodeScript TAI_LE;
+    enum_constant public static final java.lang.Character.UnicodeScript TAI_THAM;
+    enum_constant public static final java.lang.Character.UnicodeScript TAI_VIET;
+    enum_constant public static final java.lang.Character.UnicodeScript TAKRI;
+    enum_constant public static final java.lang.Character.UnicodeScript TAMIL;
+    enum_constant public static final java.lang.Character.UnicodeScript TELUGU;
+    enum_constant public static final java.lang.Character.UnicodeScript THAANA;
+    enum_constant public static final java.lang.Character.UnicodeScript THAI;
+    enum_constant public static final java.lang.Character.UnicodeScript TIBETAN;
+    enum_constant public static final java.lang.Character.UnicodeScript TIFINAGH;
+    enum_constant public static final java.lang.Character.UnicodeScript UGARITIC;
+    enum_constant public static final java.lang.Character.UnicodeScript UNKNOWN;
+    enum_constant public static final java.lang.Character.UnicodeScript VAI;
+    enum_constant public static final java.lang.Character.UnicodeScript YI;
+  }
+
+  public final class Class<T> implements java.lang.reflect.AnnotatedElement java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type {
+    method @NonNull public <U> Class<? extends U> asSubclass(@NonNull Class<U>);
+    method @Nullable public T cast(@Nullable Object);
+    method public boolean desiredAssertionStatus();
+    method @NonNull public static Class<?> forName(@NonNull String) throws java.lang.ClassNotFoundException;
+    method @NonNull public static Class<?> forName(@NonNull String, boolean, @Nullable ClassLoader) throws java.lang.ClassNotFoundException;
+    method @Nullable public <A extends java.lang.annotation.Annotation> A getAnnotation(@NonNull Class<A>);
+    method @NonNull public java.lang.annotation.Annotation[] getAnnotations();
+    method @NonNull public <A extends java.lang.annotation.Annotation> A[] getAnnotationsByType(@NonNull Class<A>);
+    method @Nullable public String getCanonicalName();
+    method @Nullable public ClassLoader getClassLoader();
+    method @NonNull public Class<?>[] getClasses();
+    method @Nullable public Class<?> getComponentType();
+    method @NonNull public java.lang.reflect.Constructor<T> getConstructor(@Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+    method @NonNull public java.lang.reflect.Constructor<?>[] getConstructors() throws java.lang.SecurityException;
+    method @Nullable public <A extends java.lang.annotation.Annotation> A getDeclaredAnnotation(@NonNull Class<A>);
+    method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method @NonNull public Class<?>[] getDeclaredClasses();
+    method @NonNull public java.lang.reflect.Constructor<T> getDeclaredConstructor(@Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+    method @NonNull public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException;
+    method @NonNull public java.lang.reflect.Field getDeclaredField(@NonNull String) throws java.lang.NoSuchFieldException;
+    method @NonNull public java.lang.reflect.Field[] getDeclaredFields();
+    method @NonNull public java.lang.reflect.Method getDeclaredMethod(@NonNull String, @Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+    method @NonNull public java.lang.reflect.Method[] getDeclaredMethods() throws java.lang.SecurityException;
+    method @Nullable public Class<?> getDeclaringClass();
+    method @Nullable public Class<?> getEnclosingClass();
+    method @Nullable public java.lang.reflect.Constructor<?> getEnclosingConstructor();
+    method @Nullable public java.lang.reflect.Method getEnclosingMethod();
+    method @Nullable public T[] getEnumConstants();
+    method @NonNull public java.lang.reflect.Field getField(@NonNull String) throws java.lang.NoSuchFieldException;
+    method @NonNull public java.lang.reflect.Field[] getFields() throws java.lang.SecurityException;
+    method @NonNull public java.lang.reflect.Type[] getGenericInterfaces();
+    method @Nullable public java.lang.reflect.Type getGenericSuperclass();
+    method @NonNull public Class<?>[] getInterfaces();
+    method @NonNull public java.lang.reflect.Method getMethod(@NonNull String, @Nullable Class<?>...) throws java.lang.NoSuchMethodException, java.lang.SecurityException;
+    method @NonNull public java.lang.reflect.Method[] getMethods() throws java.lang.SecurityException;
+    method public int getModifiers();
+    method @NonNull public String getName();
+    method @Nullable public Package getPackage();
+    method @NonNull public String getPackageName();
+    method @Nullable public java.security.ProtectionDomain getProtectionDomain();
+    method @Nullable public java.net.URL getResource(@NonNull String);
+    method @Nullable public java.io.InputStream getResourceAsStream(@NonNull String);
+    method @Nullable public Object[] getSigners();
+    method @NonNull public String getSimpleName();
+    method @Nullable public Class<? super T> getSuperclass();
+    method @NonNull public java.lang.reflect.TypeVariable<java.lang.Class<T>>[] getTypeParameters();
+    method public boolean isAnnotation();
+    method public boolean isAnonymousClass();
+    method public boolean isArray();
+    method public boolean isAssignableFrom(@NonNull Class<?>);
+    method public boolean isEnum();
+    method public boolean isInstance(@Nullable Object);
+    method public boolean isInterface();
+    method public boolean isLocalClass();
+    method public boolean isMemberClass();
+    method public boolean isPrimitive();
+    method public boolean isSynthetic();
+    method @NonNull public T newInstance() throws java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method @NonNull public String toGenericString();
+  }
+
+  public class ClassCastException extends java.lang.RuntimeException {
+    ctor public ClassCastException();
+    ctor public ClassCastException(String);
+  }
+
+  public class ClassCircularityError extends java.lang.LinkageError {
+    ctor public ClassCircularityError();
+    ctor public ClassCircularityError(String);
+  }
+
+  public class ClassFormatError extends java.lang.LinkageError {
+    ctor public ClassFormatError();
+    ctor public ClassFormatError(String);
+  }
+
+  public abstract class ClassLoader {
+    ctor protected ClassLoader(ClassLoader);
+    ctor protected ClassLoader();
+    method public void clearAssertionStatus();
+    method @Deprecated protected final Class<?> defineClass(byte[], int, int) throws java.lang.ClassFormatError;
+    method protected final Class<?> defineClass(String, byte[], int, int) throws java.lang.ClassFormatError;
+    method protected final Class<?> defineClass(String, byte[], int, int, java.security.ProtectionDomain) throws java.lang.ClassFormatError;
+    method protected final Class<?> defineClass(String, java.nio.ByteBuffer, java.security.ProtectionDomain) throws java.lang.ClassFormatError;
+    method protected Package definePackage(String, String, String, String, String, String, String, java.net.URL) throws java.lang.IllegalArgumentException;
+    method protected Class<?> findClass(String) throws java.lang.ClassNotFoundException;
+    method protected String findLibrary(String);
+    method protected final Class<?> findLoadedClass(String);
+    method protected java.net.URL findResource(String);
+    method protected java.util.Enumeration<java.net.URL> findResources(String) throws java.io.IOException;
+    method protected final Class<?> findSystemClass(String) throws java.lang.ClassNotFoundException;
+    method protected Package getPackage(String);
+    method protected Package[] getPackages();
+    method public final ClassLoader getParent();
+    method public java.net.URL getResource(String);
+    method public java.io.InputStream getResourceAsStream(String);
+    method public java.util.Enumeration<java.net.URL> getResources(String) throws java.io.IOException;
+    method public static ClassLoader getSystemClassLoader();
+    method public static java.net.URL getSystemResource(String);
+    method public static java.io.InputStream getSystemResourceAsStream(String);
+    method public static java.util.Enumeration<java.net.URL> getSystemResources(String) throws java.io.IOException;
+    method public Class<?> loadClass(String) throws java.lang.ClassNotFoundException;
+    method protected Class<?> loadClass(String, boolean) throws java.lang.ClassNotFoundException;
+    method protected static boolean registerAsParallelCapable();
+    method protected final void resolveClass(Class<?>);
+    method public void setClassAssertionStatus(String, boolean);
+    method public void setDefaultAssertionStatus(boolean);
+    method public void setPackageAssertionStatus(String, boolean);
+    method protected final void setSigners(Class<?>, Object[]);
+  }
+
+  public class ClassNotFoundException extends java.lang.ReflectiveOperationException {
+    ctor public ClassNotFoundException();
+    ctor public ClassNotFoundException(String);
+    ctor public ClassNotFoundException(String, Throwable);
+    method public Throwable getException();
+  }
+
+  public class CloneNotSupportedException extends java.lang.Exception {
+    ctor public CloneNotSupportedException();
+    ctor public CloneNotSupportedException(String);
+  }
+
+  public interface Cloneable {
+  }
+
+  public interface Comparable<T> {
+    method public int compareTo(T);
+  }
+
+  public final class Compiler {
+    method public static Object command(Object);
+    method public static boolean compileClass(Class<?>);
+    method public static boolean compileClasses(String);
+    method public static void disable();
+    method public static void enable();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.MODULE, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE}) public @interface Deprecated {
+    method public abstract boolean forRemoval() default false;
+    method public abstract String since() default "";
+  }
+
+  public final class Double extends java.lang.Number implements java.lang.Comparable<java.lang.Double> {
+    ctor public Double(double);
+    ctor public Double(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int compare(double, double);
+    method public int compareTo(@NonNull Double);
+    method public static long doubleToLongBits(double);
+    method public static long doubleToRawLongBits(double);
+    method public double doubleValue();
+    method public float floatValue();
+    method public static int hashCode(double);
+    method public int intValue();
+    method public static boolean isFinite(double);
+    method public static boolean isInfinite(double);
+    method public boolean isInfinite();
+    method public static boolean isNaN(double);
+    method public boolean isNaN();
+    method public static double longBitsToDouble(long);
+    method public long longValue();
+    method public static double max(double, double);
+    method public static double min(double, double);
+    method public static double parseDouble(@NonNull String) throws java.lang.NumberFormatException;
+    method public static double sum(double, double);
+    method @NonNull public static String toHexString(double);
+    method @NonNull public static String toString(double);
+    method @NonNull public static Double valueOf(@NonNull String) throws java.lang.NumberFormatException;
+    method @NonNull public static Double valueOf(double);
+    field public static final int BYTES = 8; // 0x8
+    field public static final int MAX_EXPONENT = 1023; // 0x3ff
+    field public static final double MAX_VALUE = 1.7976931348623157E308;
+    field public static final int MIN_EXPONENT = -1022; // 0xfffffc02
+    field public static final double MIN_NORMAL = 2.2250738585072014E-308;
+    field public static final double MIN_VALUE = 4.9E-324;
+    field public static final double NEGATIVE_INFINITY = (-1.0/0.0);
+    field public static final double NaN = (0.0/0.0);
+    field public static final double POSITIVE_INFINITY = (1.0/0.0);
+    field public static final int SIZE = 64; // 0x40
+    field public static final Class<java.lang.Double> TYPE;
+  }
+
+  public abstract class Enum<E extends java.lang.Enum<E>> implements java.lang.Comparable<E> java.io.Serializable {
+    ctor protected Enum(@NonNull String, int);
+    method @NonNull protected final Object clone() throws java.lang.CloneNotSupportedException;
+    method public final int compareTo(E);
+    method public final boolean equals(@Nullable Object);
+    method protected final void finalize();
+    method @NonNull public final Class<E> getDeclaringClass();
+    method public final int hashCode();
+    method @NonNull public final String name();
+    method public final int ordinal();
+    method @NonNull public static <T extends java.lang.Enum<T>> T valueOf(@NonNull Class<T>, @NonNull String);
+  }
+
+  public class EnumConstantNotPresentException extends java.lang.RuntimeException {
+    ctor public EnumConstantNotPresentException(Class<? extends java.lang.Enum>, String);
+    method public String constantName();
+    method public Class<? extends java.lang.Enum> enumType();
+  }
+
+  public class Error extends java.lang.Throwable {
+    ctor public Error();
+    ctor public Error(String);
+    ctor public Error(String, Throwable);
+    ctor public Error(Throwable);
+    ctor protected Error(String, Throwable, boolean, boolean);
+  }
+
+  public class Exception extends java.lang.Throwable {
+    ctor public Exception();
+    ctor public Exception(String);
+    ctor public Exception(String, Throwable);
+    ctor public Exception(Throwable);
+    ctor protected Exception(String, Throwable, boolean, boolean);
+  }
+
+  public class ExceptionInInitializerError extends java.lang.LinkageError {
+    ctor public ExceptionInInitializerError();
+    ctor public ExceptionInInitializerError(Throwable);
+    ctor public ExceptionInInitializerError(String);
+    method public Throwable getException();
+  }
+
+  public final class Float extends java.lang.Number implements java.lang.Comparable<java.lang.Float> {
+    ctor public Float(float);
+    ctor public Float(double);
+    ctor public Float(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int compare(float, float);
+    method public int compareTo(@NonNull Float);
+    method public double doubleValue();
+    method public static int floatToIntBits(float);
+    method public static int floatToRawIntBits(float);
+    method public float floatValue();
+    method public static int hashCode(float);
+    method public static float intBitsToFloat(int);
+    method public int intValue();
+    method public static boolean isFinite(float);
+    method public static boolean isInfinite(float);
+    method public boolean isInfinite();
+    method public static boolean isNaN(float);
+    method public boolean isNaN();
+    method public long longValue();
+    method public static float max(float, float);
+    method public static float min(float, float);
+    method public static float parseFloat(@NonNull String) throws java.lang.NumberFormatException;
+    method public static float sum(float, float);
+    method @NonNull public static String toHexString(float);
+    method @NonNull public static String toString(float);
+    method @NonNull public static Float valueOf(@NonNull String) throws java.lang.NumberFormatException;
+    method @NonNull public static Float valueOf(float);
+    field public static final int BYTES = 4; // 0x4
+    field public static final int MAX_EXPONENT = 127; // 0x7f
+    field public static final float MAX_VALUE = 3.4028235E38f;
+    field public static final int MIN_EXPONENT = -126; // 0xffffff82
+    field public static final float MIN_NORMAL = 1.17549435E-38f;
+    field public static final float MIN_VALUE = 1.4E-45f;
+    field public static final float NEGATIVE_INFINITY = (-1.0f/0.0f);
+    field public static final float NaN = (0.0f/0.0f);
+    field public static final float POSITIVE_INFINITY = (1.0f/0.0f);
+    field public static final int SIZE = 32; // 0x20
+    field public static final Class<java.lang.Float> TYPE;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE) public @interface FunctionalInterface {
+  }
+
+  public class IllegalAccessError extends java.lang.IncompatibleClassChangeError {
+    ctor public IllegalAccessError();
+    ctor public IllegalAccessError(String);
+  }
+
+  public class IllegalAccessException extends java.lang.ReflectiveOperationException {
+    ctor public IllegalAccessException();
+    ctor public IllegalAccessException(String);
+  }
+
+  public class IllegalArgumentException extends java.lang.RuntimeException {
+    ctor public IllegalArgumentException();
+    ctor public IllegalArgumentException(String);
+    ctor public IllegalArgumentException(String, Throwable);
+    ctor public IllegalArgumentException(Throwable);
+  }
+
+  public class IllegalMonitorStateException extends java.lang.RuntimeException {
+    ctor public IllegalMonitorStateException();
+    ctor public IllegalMonitorStateException(String);
+  }
+
+  public class IllegalStateException extends java.lang.RuntimeException {
+    ctor public IllegalStateException();
+    ctor public IllegalStateException(String);
+    ctor public IllegalStateException(String, Throwable);
+    ctor public IllegalStateException(Throwable);
+  }
+
+  public class IllegalThreadStateException extends java.lang.IllegalArgumentException {
+    ctor public IllegalThreadStateException();
+    ctor public IllegalThreadStateException(String);
+  }
+
+  public class IncompatibleClassChangeError extends java.lang.LinkageError {
+    ctor public IncompatibleClassChangeError();
+    ctor public IncompatibleClassChangeError(String);
+  }
+
+  public class IndexOutOfBoundsException extends java.lang.RuntimeException {
+    ctor public IndexOutOfBoundsException();
+    ctor public IndexOutOfBoundsException(String);
+  }
+
+  public class InheritableThreadLocal<T> extends java.lang.ThreadLocal<T> {
+    ctor public InheritableThreadLocal();
+    method protected T childValue(T);
+  }
+
+  public class InstantiationError extends java.lang.IncompatibleClassChangeError {
+    ctor public InstantiationError();
+    ctor public InstantiationError(String);
+  }
+
+  public class InstantiationException extends java.lang.ReflectiveOperationException {
+    ctor public InstantiationException();
+    ctor public InstantiationException(String);
+  }
+
+  public final class Integer extends java.lang.Number implements java.lang.Comparable<java.lang.Integer> {
+    ctor public Integer(int);
+    ctor public Integer(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int bitCount(int);
+    method public static int compare(int, int);
+    method public int compareTo(@NonNull Integer);
+    method public static int compareUnsigned(int, int);
+    method @NonNull public static Integer decode(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int divideUnsigned(int, int);
+    method public double doubleValue();
+    method public float floatValue();
+    method @Nullable public static Integer getInteger(@NonNull String);
+    method @Nullable public static Integer getInteger(@NonNull String, int);
+    method @Nullable public static Integer getInteger(@NonNull String, @Nullable Integer);
+    method public static int hashCode(int);
+    method public static int highestOneBit(int);
+    method public int intValue();
+    method public long longValue();
+    method public static int lowestOneBit(int);
+    method public static int max(int, int);
+    method public static int min(int, int);
+    method public static int numberOfLeadingZeros(int);
+    method public static int numberOfTrailingZeros(int);
+    method public static int parseInt(@NonNull String, int) throws java.lang.NumberFormatException;
+    method public static int parseInt(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int parseUnsignedInt(@NonNull String, int) throws java.lang.NumberFormatException;
+    method public static int parseUnsignedInt(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int remainderUnsigned(int, int);
+    method public static int reverse(int);
+    method public static int reverseBytes(int);
+    method public static int rotateLeft(int, int);
+    method public static int rotateRight(int, int);
+    method public static int signum(int);
+    method public static int sum(int, int);
+    method @NonNull public static String toBinaryString(int);
+    method @NonNull public static String toHexString(int);
+    method @NonNull public static String toOctalString(int);
+    method @NonNull public static String toString(int, int);
+    method @NonNull public static String toString(int);
+    method public static long toUnsignedLong(int);
+    method @NonNull public static String toUnsignedString(int, int);
+    method @NonNull public static String toUnsignedString(int);
+    method @NonNull public static Integer valueOf(@NonNull String, int) throws java.lang.NumberFormatException;
+    method @NonNull public static Integer valueOf(@NonNull String) throws java.lang.NumberFormatException;
+    method @NonNull public static Integer valueOf(int);
+    field public static final int BYTES = 4; // 0x4
+    field public static final int MAX_VALUE = 2147483647; // 0x7fffffff
+    field public static final int MIN_VALUE = -2147483648; // 0x80000000
+    field public static final int SIZE = 32; // 0x20
+    field public static final Class<java.lang.Integer> TYPE;
+  }
+
+  public class InternalError extends java.lang.VirtualMachineError {
+    ctor public InternalError();
+    ctor public InternalError(String);
+    ctor public InternalError(String, Throwable);
+    ctor public InternalError(Throwable);
+  }
+
+  public class InterruptedException extends java.lang.Exception {
+    ctor public InterruptedException();
+    ctor public InterruptedException(String);
+  }
+
+  public interface Iterable<T> {
+    method public default void forEach(@NonNull java.util.function.Consumer<? super T>);
+    method @NonNull public java.util.Iterator<T> iterator();
+    method @NonNull public default java.util.Spliterator<T> spliterator();
+  }
+
+  public class LinkageError extends java.lang.Error {
+    ctor public LinkageError();
+    ctor public LinkageError(String);
+    ctor public LinkageError(String, Throwable);
+  }
+
+  public final class Long extends java.lang.Number implements java.lang.Comparable<java.lang.Long> {
+    ctor public Long(long);
+    ctor public Long(@NonNull String) throws java.lang.NumberFormatException;
+    method public static int bitCount(long);
+    method public static int compare(long, long);
+    method public int compareTo(@NonNull Long);
+    method public static int compareUnsigned(long, long);
+    method @NonNull public static Long decode(@NonNull String) throws java.lang.NumberFormatException;
+    method public static long divideUnsigned(long, long);
+    method public double doubleValue();
+    method public float floatValue();
+    method @Nullable public static Long getLong(@NonNull String);
+    method @Nullable public static Long getLong(@NonNull String, long);
+    method @Nullable public static Long getLong(@NonNull String, @Nullable Long);
+    method public static int hashCode(long);
+    method public static long highestOneBit(long);
+    method public int intValue();
+    method public long longValue();
+    method public static long lowestOneBit(long);
+    method public static long max(long, long);
+    method public static long min(long, long);
+    method public static int numberOfLeadingZeros(long);
+    method public static int numberOfTrailingZeros(long);
+    method public static long parseLong(@NonNull String, int) throws java.lang.NumberFormatException;
+    method public static long parseLong(@NonNull String) throws java.lang.NumberFormatException;
+    method public static long parseUnsignedLong(@NonNull String, int) throws java.lang.NumberFormatException;
+    method public static long parseUnsignedLong(@NonNull String) throws java.lang.NumberFormatException;
+    method public static long remainderUnsigned(long, long);
+    method public static long reverse(long);
+    method public static long reverseBytes(long);
+    method public static long rotateLeft(long, int);
+    method public static long rotateRight(long, int);
+    method public static int signum(long);
+    method public static long sum(long, long);
+    method @NonNull public static String toBinaryString(long);
+    method @NonNull public static String toHexString(long);
+    method @NonNull public static String toOctalString(long);
+    method @NonNull public static String toString(long, int);
+    method @NonNull public static String toString(long);
+    method @NonNull public static String toUnsignedString(long, int);
+    method @NonNull public static String toUnsignedString(long);
+    method @NonNull public static Long valueOf(@NonNull String, int) throws java.lang.NumberFormatException;
+    method @NonNull public static Long valueOf(@NonNull String) throws java.lang.NumberFormatException;
+    method @NonNull public static Long valueOf(long);
+    field public static final int BYTES = 8; // 0x8
+    field public static final long MAX_VALUE = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final long MIN_VALUE = -9223372036854775808L; // 0x8000000000000000L
+    field public static final int SIZE = 64; // 0x40
+    field public static final Class<java.lang.Long> TYPE;
+  }
+
+  public final class Math {
+    method public static double IEEEremainder(double, double);
+    method public static int abs(int);
+    method public static long abs(long);
+    method public static float abs(float);
+    method public static double abs(double);
+    method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
+    method public static double asin(double);
+    method public static double atan(double);
+    method public static double atan2(double, double);
+    method public static double cbrt(double);
+    method public static double ceil(double);
+    method public static double copySign(double, double);
+    method public static float copySign(float, float);
+    method public static double cos(double);
+    method public static double cosh(double);
+    method public static int decrementExact(int);
+    method public static long decrementExact(long);
+    method public static double exp(double);
+    method public static double expm1(double);
+    method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static int floorMod(long, int);
+    method public static long floorMod(long, long);
+    method public static int getExponent(float);
+    method public static int getExponent(double);
+    method public static double hypot(double, double);
+    method public static int incrementExact(int);
+    method public static long incrementExact(long);
+    method public static double log(double);
+    method public static double log10(double);
+    method public static double log1p(double);
+    method public static int max(int, int);
+    method public static long max(long, long);
+    method public static float max(float, float);
+    method public static double max(double, double);
+    method public static int min(int, int);
+    method public static long min(long, long);
+    method public static float min(float, float);
+    method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, int);
+    method public static long multiplyExact(long, long);
+    method public static long multiplyFull(int, int);
+    method public static long multiplyHigh(long, long);
+    method public static int negateExact(int);
+    method public static long negateExact(long);
+    method public static double nextAfter(double, double);
+    method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
+    method public static double nextUp(double);
+    method public static float nextUp(float);
+    method public static double pow(double, double);
+    method public static double random();
+    method public static double rint(double);
+    method public static int round(float);
+    method public static long round(double);
+    method public static double scalb(double, int);
+    method public static float scalb(float, int);
+    method public static double signum(double);
+    method public static float signum(float);
+    method public static double sin(double);
+    method public static double sinh(double);
+    method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
+    method public static double tan(double);
+    method public static double tanh(double);
+    method public static double toDegrees(double);
+    method public static int toIntExact(long);
+    method public static double toRadians(double);
+    method public static double ulp(double);
+    method public static float ulp(float);
+    field public static final double E = 2.718281828459045;
+    field public static final double PI = 3.141592653589793;
+  }
+
+  public class NegativeArraySizeException extends java.lang.RuntimeException {
+    ctor public NegativeArraySizeException();
+    ctor public NegativeArraySizeException(String);
+  }
+
+  public class NoClassDefFoundError extends java.lang.LinkageError {
+    ctor public NoClassDefFoundError();
+    ctor public NoClassDefFoundError(String);
+  }
+
+  public class NoSuchFieldError extends java.lang.IncompatibleClassChangeError {
+    ctor public NoSuchFieldError();
+    ctor public NoSuchFieldError(String);
+  }
+
+  public class NoSuchFieldException extends java.lang.ReflectiveOperationException {
+    ctor public NoSuchFieldException();
+    ctor public NoSuchFieldException(String);
+  }
+
+  public class NoSuchMethodError extends java.lang.IncompatibleClassChangeError {
+    ctor public NoSuchMethodError();
+    ctor public NoSuchMethodError(String);
+  }
+
+  public class NoSuchMethodException extends java.lang.ReflectiveOperationException {
+    ctor public NoSuchMethodException();
+    ctor public NoSuchMethodException(String);
+  }
+
+  public class NullPointerException extends java.lang.RuntimeException {
+    ctor public NullPointerException();
+    ctor public NullPointerException(String);
+  }
+
+  public abstract class Number implements java.io.Serializable {
+    ctor public Number();
+    method public byte byteValue();
+    method public abstract double doubleValue();
+    method public abstract float floatValue();
+    method public abstract int intValue();
+    method public abstract long longValue();
+    method public short shortValue();
+  }
+
+  public class NumberFormatException extends java.lang.IllegalArgumentException {
+    ctor public NumberFormatException();
+    ctor public NumberFormatException(String);
+  }
+
+  public class Object {
+    ctor public Object();
+    method @NonNull protected Object clone() throws java.lang.CloneNotSupportedException;
+    method public boolean equals(@Nullable Object);
+    method protected void finalize() throws java.lang.Throwable;
+    method @NonNull public final Class<?> getClass();
+    method public int hashCode();
+    method public final void notify();
+    method public final void notifyAll();
+    method @NonNull public String toString();
+    method public final void wait(long) throws java.lang.InterruptedException;
+    method public final void wait(long, int) throws java.lang.InterruptedException;
+    method public final void wait() throws java.lang.InterruptedException;
+  }
+
+  public class OutOfMemoryError extends java.lang.VirtualMachineError {
+    ctor public OutOfMemoryError();
+    ctor public OutOfMemoryError(String);
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface Override {
+  }
+
+  public class Package implements java.lang.reflect.AnnotatedElement {
+    method public <A extends java.lang.annotation.Annotation> A getAnnotation(Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public <A extends java.lang.annotation.Annotation> A[] getAnnotationsByType(Class<A>);
+    method public <A extends java.lang.annotation.Annotation> A getDeclaredAnnotation(Class<A>);
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public <A extends java.lang.annotation.Annotation> A[] getDeclaredAnnotationsByType(Class<A>);
+    method public String getImplementationTitle();
+    method public String getImplementationVendor();
+    method public String getImplementationVersion();
+    method public String getName();
+    method public static Package getPackage(String);
+    method public static Package[] getPackages();
+    method public String getSpecificationTitle();
+    method public String getSpecificationVendor();
+    method public String getSpecificationVersion();
+    method public boolean isCompatibleWith(String) throws java.lang.NumberFormatException;
+    method public boolean isSealed();
+    method public boolean isSealed(java.net.URL);
+  }
+
+  public abstract class Process {
+    ctor public Process();
+    method public abstract void destroy();
+    method public Process destroyForcibly();
+    method public abstract int exitValue();
+    method public abstract java.io.InputStream getErrorStream();
+    method public abstract java.io.InputStream getInputStream();
+    method public abstract java.io.OutputStream getOutputStream();
+    method public boolean isAlive();
+    method public abstract int waitFor() throws java.lang.InterruptedException;
+    method public boolean waitFor(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+  }
+
+  public final class ProcessBuilder {
+    ctor public ProcessBuilder(java.util.List<java.lang.String>);
+    ctor public ProcessBuilder(java.lang.String...);
+    method public ProcessBuilder command(java.util.List<java.lang.String>);
+    method public ProcessBuilder command(java.lang.String...);
+    method public java.util.List<java.lang.String> command();
+    method public java.io.File directory();
+    method public ProcessBuilder directory(java.io.File);
+    method public java.util.Map<java.lang.String,java.lang.String> environment();
+    method public ProcessBuilder inheritIO();
+    method public ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
+    method public ProcessBuilder redirectError(java.io.File);
+    method public java.lang.ProcessBuilder.Redirect redirectError();
+    method public boolean redirectErrorStream();
+    method public ProcessBuilder redirectErrorStream(boolean);
+    method public ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
+    method public ProcessBuilder redirectInput(java.io.File);
+    method public java.lang.ProcessBuilder.Redirect redirectInput();
+    method public ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
+    method public ProcessBuilder redirectOutput(java.io.File);
+    method public java.lang.ProcessBuilder.Redirect redirectOutput();
+    method public Process start() throws java.io.IOException;
+  }
+
+  public abstract static class ProcessBuilder.Redirect {
+    method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
+    method public java.io.File file();
+    method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
+    method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
+    method public abstract java.lang.ProcessBuilder.Redirect.Type type();
+    field public static final java.lang.ProcessBuilder.Redirect INHERIT;
+    field public static final java.lang.ProcessBuilder.Redirect PIPE;
+  }
+
+  public enum ProcessBuilder.Redirect.Type {
+    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
+    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
+    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
+    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
+    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
+  }
+
+  public interface Readable {
+    method public int read(java.nio.CharBuffer) throws java.io.IOException;
+  }
+
+  public class ReflectiveOperationException extends java.lang.Exception {
+    ctor public ReflectiveOperationException();
+    ctor public ReflectiveOperationException(String);
+    ctor public ReflectiveOperationException(String, Throwable);
+    ctor public ReflectiveOperationException(Throwable);
+  }
+
+  @java.lang.FunctionalInterface public interface Runnable {
+    method public void run();
+  }
+
+  public class Runtime {
+    method public void addShutdownHook(Thread);
+    method public int availableProcessors();
+    method public Process exec(String) throws java.io.IOException;
+    method public Process exec(String, String[]) throws java.io.IOException;
+    method public Process exec(String, String[], java.io.File) throws java.io.IOException;
+    method public Process exec(String[]) throws java.io.IOException;
+    method public Process exec(String[], String[]) throws java.io.IOException;
+    method public Process exec(String[], String[], java.io.File) throws java.io.IOException;
+    method public void exit(int);
+    method public long freeMemory();
+    method public void gc();
+    method @Deprecated public java.io.InputStream getLocalizedInputStream(java.io.InputStream);
+    method @Deprecated public java.io.OutputStream getLocalizedOutputStream(java.io.OutputStream);
+    method public static Runtime getRuntime();
+    method public void halt(int);
+    method public void load(String);
+    method public void loadLibrary(String);
+    method public long maxMemory();
+    method public boolean removeShutdownHook(Thread);
+    method public void runFinalization();
+    method @Deprecated public static void runFinalizersOnExit(boolean);
+    method public long totalMemory();
+    method public void traceInstructions(boolean);
+    method public void traceMethodCalls(boolean);
+  }
+
+  public class RuntimeException extends java.lang.Exception {
+    ctor public RuntimeException();
+    ctor public RuntimeException(String);
+    ctor public RuntimeException(String, Throwable);
+    ctor public RuntimeException(Throwable);
+    ctor protected RuntimeException(String, Throwable, boolean, boolean);
+  }
+
+  public final class RuntimePermission extends java.security.BasicPermission {
+    ctor public RuntimePermission(String);
+    ctor public RuntimePermission(String, String);
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.METHOD}) public @interface SafeVarargs {
+  }
+
+  public class SecurityException extends java.lang.RuntimeException {
+    ctor public SecurityException();
+    ctor public SecurityException(String);
+    ctor public SecurityException(String, Throwable);
+    ctor public SecurityException(Throwable);
+  }
+
+  public class SecurityManager {
+    ctor public SecurityManager();
+    method public void checkAccept(String, int);
+    method public void checkAccess(Thread);
+    method public void checkAccess(ThreadGroup);
+    method public void checkAwtEventQueueAccess();
+    method public void checkConnect(String, int);
+    method public void checkConnect(String, int, Object);
+    method public void checkCreateClassLoader();
+    method public void checkDelete(String);
+    method public void checkExec(String);
+    method public void checkExit(int);
+    method public void checkLink(String);
+    method public void checkListen(int);
+    method public void checkMemberAccess(Class<?>, int);
+    method public void checkMulticast(java.net.InetAddress);
+    method @Deprecated public void checkMulticast(java.net.InetAddress, byte);
+    method public void checkPackageAccess(String);
+    method public void checkPackageDefinition(String);
+    method public void checkPermission(java.security.Permission);
+    method public void checkPermission(java.security.Permission, Object);
+    method public void checkPrintJobAccess();
+    method public void checkPropertiesAccess();
+    method public void checkPropertyAccess(String);
+    method public void checkRead(java.io.FileDescriptor);
+    method public void checkRead(String);
+    method public void checkRead(String, Object);
+    method public void checkSecurityAccess(String);
+    method public void checkSetFactory();
+    method public void checkSystemClipboardAccess();
+    method public boolean checkTopLevelWindow(Object);
+    method public void checkWrite(java.io.FileDescriptor);
+    method public void checkWrite(String);
+    method @Deprecated protected int classDepth(String);
+    method @Deprecated protected int classLoaderDepth();
+    method @Deprecated protected ClassLoader currentClassLoader();
+    method @Deprecated protected Class<?> currentLoadedClass();
+    method protected Class[] getClassContext();
+    method @Deprecated public boolean getInCheck();
+    method public Object getSecurityContext();
+    method public ThreadGroup getThreadGroup();
+    method @Deprecated protected boolean inClass(String);
+    method @Deprecated protected boolean inClassLoader();
+    field @Deprecated protected boolean inCheck;
+  }
+
+  public final class Short extends java.lang.Number implements java.lang.Comparable<java.lang.Short> {
+    ctor @Deprecated public Short(short);
+    ctor @Deprecated public Short(String) throws java.lang.NumberFormatException;
+    method public static int compare(short, short);
+    method public int compareTo(Short);
+    method public static int compareUnsigned(short, short);
+    method public static Short decode(String) throws java.lang.NumberFormatException;
+    method public double doubleValue();
+    method public float floatValue();
+    method public static int hashCode(short);
+    method public int intValue();
+    method public long longValue();
+    method public static short parseShort(String, int) throws java.lang.NumberFormatException;
+    method public static short parseShort(String) throws java.lang.NumberFormatException;
+    method public static short reverseBytes(short);
+    method public static String toString(short);
+    method public static int toUnsignedInt(short);
+    method public static long toUnsignedLong(short);
+    method public static Short valueOf(String, int) throws java.lang.NumberFormatException;
+    method public static Short valueOf(String) throws java.lang.NumberFormatException;
+    method public static Short valueOf(short);
+    field public static final int BYTES = 2; // 0x2
+    field public static final short MAX_VALUE = 32767; // 0x7fff
+    field public static final short MIN_VALUE = -32768; // 0xffff8000
+    field public static final int SIZE = 16; // 0x10
+    field public static final Class<java.lang.Short> TYPE;
+  }
+
+  public class StackOverflowError extends java.lang.VirtualMachineError {
+    ctor public StackOverflowError();
+    ctor public StackOverflowError(String);
+  }
+
+  public final class StackTraceElement implements java.io.Serializable {
+    ctor public StackTraceElement(String, String, String, int);
+    method public String getClassName();
+    method public String getFileName();
+    method public int getLineNumber();
+    method public String getMethodName();
+    method public boolean isNativeMethod();
+  }
+
+  public final class StrictMath {
+    method public static double IEEEremainder(double, double);
+    method public static int abs(int);
+    method public static long abs(long);
+    method public static float abs(float);
+    method public static double abs(double);
+    method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
+    method public static double asin(double);
+    method public static double atan(double);
+    method public static double atan2(double, double);
+    method public static double cbrt(double);
+    method public static double ceil(double);
+    method public static double copySign(double, double);
+    method public static float copySign(float, float);
+    method public static double cos(double);
+    method public static double cosh(double);
+    method public static double exp(double);
+    method public static double expm1(double);
+    method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static int floorMod(long, int);
+    method public static long floorMod(long, long);
+    method public static int getExponent(float);
+    method public static int getExponent(double);
+    method public static double hypot(double, double);
+    method public static double log(double);
+    method public static double log10(double);
+    method public static double log1p(double);
+    method public static int max(int, int);
+    method public static long max(long, long);
+    method public static float max(float, float);
+    method public static double max(double, double);
+    method public static int min(int, int);
+    method public static long min(long, long);
+    method public static float min(float, float);
+    method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, int);
+    method public static long multiplyExact(long, long);
+    method public static long multiplyFull(int, int);
+    method public static long multiplyHigh(long, long);
+    method public static double nextAfter(double, double);
+    method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
+    method public static double nextUp(double);
+    method public static float nextUp(float);
+    method public static double pow(double, double);
+    method public static double random();
+    method public static double rint(double);
+    method public static int round(float);
+    method public static long round(double);
+    method public static double scalb(double, int);
+    method public static float scalb(float, int);
+    method public static double signum(double);
+    method public static float signum(float);
+    method public static double sin(double);
+    method public static double sinh(double);
+    method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
+    method public static double tan(double);
+    method public static double tanh(double);
+    method public static double toDegrees(double);
+    method public static int toIntExact(long);
+    method public static double toRadians(double);
+    method public static double ulp(double);
+    method public static float ulp(float);
+    field public static final double E = 2.718281828459045;
+    field public static final double PI = 3.141592653589793;
+  }
+
+  public final class String implements java.lang.CharSequence java.lang.Comparable<java.lang.String> java.io.Serializable {
+    ctor public String();
+    ctor public String(@NonNull String);
+    ctor public String(char[]);
+    ctor public String(char[], int, int);
+    ctor public String(int[], int, int);
+    ctor @Deprecated public String(byte[], int, int, int);
+    ctor @Deprecated public String(byte[], int);
+    ctor public String(byte[], int, int, @NonNull String) throws java.io.UnsupportedEncodingException;
+    ctor public String(byte[], int, int, @NonNull java.nio.charset.Charset);
+    ctor public String(byte[], @NonNull String) throws java.io.UnsupportedEncodingException;
+    ctor public String(byte[], @NonNull java.nio.charset.Charset);
+    ctor public String(byte[], int, int);
+    ctor public String(byte[]);
+    ctor public String(@NonNull StringBuffer);
+    ctor public String(@NonNull StringBuilder);
+    method public char charAt(int);
+    method public int codePointAt(int);
+    method public int codePointBefore(int);
+    method public int codePointCount(int, int);
+    method public int compareTo(@NonNull String);
+    method public int compareToIgnoreCase(@NonNull String);
+    method @NonNull public String concat(@NonNull String);
+    method public boolean contains(@NonNull CharSequence);
+    method public boolean contentEquals(@NonNull StringBuffer);
+    method public boolean contentEquals(@NonNull CharSequence);
+    method @NonNull public static String copyValueOf(char[], int, int);
+    method @NonNull public static String copyValueOf(char[]);
+    method public boolean endsWith(@NonNull String);
+    method public boolean equalsIgnoreCase(@Nullable String);
+    method @NonNull public static String format(@NonNull String, @NonNull java.lang.Object...);
+    method @NonNull public static String format(@NonNull java.util.Locale, @NonNull String, @NonNull java.lang.Object...);
+    method @Deprecated public void getBytes(int, int, byte[], int);
+    method public byte[] getBytes(@NonNull String) throws java.io.UnsupportedEncodingException;
+    method public byte[] getBytes(@NonNull java.nio.charset.Charset);
+    method public byte[] getBytes();
+    method public void getChars(int, int, char[], int);
+    method public int indexOf(int);
+    method public int indexOf(int, int);
+    method public int indexOf(@NonNull String);
+    method public int indexOf(@NonNull String, int);
+    method @NonNull public String intern();
+    method public boolean isEmpty();
+    method @NonNull public static String join(@NonNull CharSequence, @Nullable java.lang.CharSequence...);
+    method @NonNull public static String join(@NonNull CharSequence, @NonNull Iterable<? extends java.lang.CharSequence>);
+    method public int lastIndexOf(int);
+    method public int lastIndexOf(int, int);
+    method public int lastIndexOf(@NonNull String);
+    method public int lastIndexOf(@NonNull String, int);
+    method public int length();
+    method public boolean matches(@NonNull String);
+    method public int offsetByCodePoints(int, int);
+    method public boolean regionMatches(int, @NonNull String, int, int);
+    method public boolean regionMatches(boolean, int, @NonNull String, int, int);
+    method @NonNull public String replace(char, char);
+    method @NonNull public String replace(@NonNull CharSequence, @NonNull CharSequence);
+    method @NonNull public String replaceAll(@NonNull String, @NonNull String);
+    method @NonNull public String replaceFirst(@NonNull String, @NonNull String);
+    method @NonNull public String[] split(@NonNull String, int);
+    method @NonNull public String[] split(@NonNull String);
+    method public boolean startsWith(@NonNull String, int);
+    method public boolean startsWith(@NonNull String);
+    method @NonNull public CharSequence subSequence(int, int);
+    method @NonNull public String substring(int);
+    method @NonNull public String substring(int, int);
+    method public char[] toCharArray();
+    method @NonNull public String toLowerCase(@NonNull java.util.Locale);
+    method @NonNull public String toLowerCase();
+    method @NonNull public String toUpperCase(@NonNull java.util.Locale);
+    method @NonNull public String toUpperCase();
+    method @NonNull public String trim();
+    method @NonNull public static String valueOf(@Nullable Object);
+    method @NonNull public static String valueOf(char[]);
+    method @NonNull public static String valueOf(char[], int, int);
+    method @NonNull public static String valueOf(boolean);
+    method @NonNull public static String valueOf(char);
+    method @NonNull public static String valueOf(int);
+    method @NonNull public static String valueOf(long);
+    method @NonNull public static String valueOf(float);
+    method @NonNull public static String valueOf(double);
+    field public static final java.util.Comparator<java.lang.String> CASE_INSENSITIVE_ORDER;
+  }
+
+  public final class StringBuffer implements java.lang.Appendable java.lang.CharSequence java.io.Serializable {
+    ctor public StringBuffer();
+    ctor public StringBuffer(int);
+    ctor public StringBuffer(@NonNull String);
+    ctor public StringBuffer(@NonNull CharSequence);
+    method @NonNull public StringBuffer append(@Nullable Object);
+    method @NonNull public StringBuffer append(@Nullable String);
+    method @NonNull public StringBuffer append(@Nullable StringBuffer);
+    method @NonNull public StringBuffer append(@Nullable CharSequence);
+    method @NonNull public StringBuffer append(@Nullable CharSequence, int, int);
+    method @NonNull public StringBuffer append(char[]);
+    method @NonNull public StringBuffer append(char[], int, int);
+    method @NonNull public StringBuffer append(boolean);
+    method @NonNull public StringBuffer append(char);
+    method @NonNull public StringBuffer append(int);
+    method @NonNull public StringBuffer append(long);
+    method @NonNull public StringBuffer append(float);
+    method @NonNull public StringBuffer append(double);
+    method @NonNull public StringBuffer appendCodePoint(int);
+    method public int capacity();
+    method public char charAt(int);
+    method public int codePointAt(int);
+    method public int codePointBefore(int);
+    method public int codePointCount(int, int);
+    method @NonNull public StringBuffer delete(int, int);
+    method @NonNull public StringBuffer deleteCharAt(int);
+    method public void ensureCapacity(int);
+    method public void getChars(int, int, char[], int);
+    method public int indexOf(@NonNull String);
+    method public int indexOf(@NonNull String, int);
+    method @NonNull public StringBuffer insert(int, char[], int, int);
+    method @NonNull public StringBuffer insert(int, @Nullable Object);
+    method @NonNull public StringBuffer insert(int, @Nullable String);
+    method @NonNull public StringBuffer insert(int, char[]);
+    method @NonNull public StringBuffer insert(int, @Nullable CharSequence);
+    method @NonNull public StringBuffer insert(int, @Nullable CharSequence, int, int);
+    method @NonNull public StringBuffer insert(int, boolean);
+    method @NonNull public StringBuffer insert(int, char);
+    method @NonNull public StringBuffer insert(int, int);
+    method @NonNull public StringBuffer insert(int, long);
+    method @NonNull public StringBuffer insert(int, float);
+    method @NonNull public StringBuffer insert(int, double);
+    method public int lastIndexOf(@NonNull String);
+    method public int lastIndexOf(@NonNull String, int);
+    method public int length();
+    method public int offsetByCodePoints(int, int);
+    method @NonNull public StringBuffer replace(int, int, @NonNull String);
+    method @NonNull public StringBuffer reverse();
+    method public void setCharAt(int, char);
+    method public void setLength(int);
+    method @NonNull public CharSequence subSequence(int, int);
+    method @NonNull public String substring(int);
+    method @NonNull public String substring(int, int);
+    method public void trimToSize();
+  }
+
+  public final class StringBuilder implements java.lang.Appendable java.lang.CharSequence java.io.Serializable {
+    ctor public StringBuilder();
+    ctor public StringBuilder(int);
+    ctor public StringBuilder(@NonNull String);
+    ctor public StringBuilder(@NonNull CharSequence);
+    method @NonNull public StringBuilder append(@Nullable Object);
+    method @NonNull public StringBuilder append(@Nullable String);
+    method @NonNull public StringBuilder append(@Nullable StringBuffer);
+    method @NonNull public StringBuilder append(@Nullable CharSequence);
+    method @NonNull public StringBuilder append(@Nullable CharSequence, int, int);
+    method @NonNull public StringBuilder append(char[]);
+    method @NonNull public StringBuilder append(char[], int, int);
+    method @NonNull public StringBuilder append(boolean);
+    method @NonNull public StringBuilder append(char);
+    method @NonNull public StringBuilder append(int);
+    method @NonNull public StringBuilder append(long);
+    method @NonNull public StringBuilder append(float);
+    method @NonNull public StringBuilder append(double);
+    method @NonNull public StringBuilder appendCodePoint(int);
+    method public int capacity();
+    method public char charAt(int);
+    method public int codePointAt(int);
+    method public int codePointBefore(int);
+    method public int codePointCount(int, int);
+    method @NonNull public StringBuilder delete(int, int);
+    method @NonNull public StringBuilder deleteCharAt(int);
+    method public void ensureCapacity(int);
+    method public void getChars(int, int, char[], int);
+    method public int indexOf(@NonNull String);
+    method public int indexOf(@NonNull String, int);
+    method @NonNull public StringBuilder insert(int, char[], int, int);
+    method @NonNull public StringBuilder insert(int, @Nullable Object);
+    method @NonNull public StringBuilder insert(int, @Nullable String);
+    method @NonNull public StringBuilder insert(int, char[]);
+    method @NonNull public StringBuilder insert(int, @Nullable CharSequence);
+    method @NonNull public StringBuilder insert(int, @Nullable CharSequence, int, int);
+    method @NonNull public StringBuilder insert(int, boolean);
+    method @NonNull public StringBuilder insert(int, char);
+    method @NonNull public StringBuilder insert(int, int);
+    method @NonNull public StringBuilder insert(int, long);
+    method @NonNull public StringBuilder insert(int, float);
+    method @NonNull public StringBuilder insert(int, double);
+    method public int lastIndexOf(@NonNull String);
+    method public int lastIndexOf(@NonNull String, int);
+    method public int length();
+    method public int offsetByCodePoints(int, int);
+    method @NonNull public StringBuilder replace(int, int, @NonNull String);
+    method @NonNull public StringBuilder reverse();
+    method public void setCharAt(int, char);
+    method public void setLength(int);
+    method @NonNull public CharSequence subSequence(int, int);
+    method @NonNull public String substring(int);
+    method @NonNull public String substring(int, int);
+    method public void trimToSize();
+  }
+
+  public class StringIndexOutOfBoundsException extends java.lang.IndexOutOfBoundsException {
+    ctor public StringIndexOutOfBoundsException();
+    ctor public StringIndexOutOfBoundsException(String);
+    ctor public StringIndexOutOfBoundsException(int);
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) public @interface SuppressWarnings {
+    method public abstract String[] value();
+  }
+
+  public final class System {
+    method public static void arraycopy(@NonNull Object, int, @NonNull Object, int, int);
+    method @Nullable public static String clearProperty(@NonNull String);
+    method @Nullable public static java.io.Console console();
+    method public static long currentTimeMillis();
+    method public static void exit(int);
+    method public static void gc();
+    method @NonNull public static java.util.Properties getProperties();
+    method @Nullable public static String getProperty(@NonNull String);
+    method @Nullable public static String getProperty(@NonNull String, @Nullable String);
+    method @Nullable public static SecurityManager getSecurityManager();
+    method @Nullable public static String getenv(@NonNull String);
+    method @NonNull public static java.util.Map<java.lang.String,java.lang.String> getenv();
+    method public static int identityHashCode(@Nullable Object);
+    method @Nullable public static java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
+    method @NonNull public static String lineSeparator();
+    method public static void load(@NonNull String);
+    method public static void loadLibrary(@NonNull String);
+    method @NonNull public static String mapLibraryName(@NonNull String);
+    method public static long nanoTime();
+    method public static void runFinalization();
+    method @Deprecated public static void runFinalizersOnExit(boolean);
+    method public static void setErr(@Nullable java.io.PrintStream);
+    method public static void setIn(@Nullable java.io.InputStream);
+    method public static void setOut(@Nullable java.io.PrintStream);
+    method public static void setProperties(@Nullable java.util.Properties);
+    method @Nullable public static String setProperty(@NonNull String, @Nullable String);
+    method public static void setSecurityManager(@Nullable SecurityManager);
+    field public static final java.io.PrintStream err;
+    field public static final java.io.InputStream in;
+    field public static final java.io.PrintStream out;
+  }
+
+  public class Thread implements java.lang.Runnable {
+    ctor public Thread();
+    ctor public Thread(@Nullable Runnable);
+    ctor public Thread(@Nullable ThreadGroup, @Nullable Runnable);
+    ctor public Thread(@NonNull String);
+    ctor public Thread(@Nullable ThreadGroup, @NonNull String);
+    ctor public Thread(@Nullable Runnable, @NonNull String);
+    ctor public Thread(@Nullable ThreadGroup, @Nullable Runnable, @NonNull String);
+    ctor public Thread(@Nullable ThreadGroup, @Nullable Runnable, @NonNull String, long);
+    method public static int activeCount();
+    method public final void checkAccess();
+    method @Deprecated public int countStackFrames();
+    method @NonNull public static Thread currentThread();
+    method @Deprecated public void destroy();
+    method public static void dumpStack();
+    method public static int enumerate(Thread[]);
+    method @NonNull public static java.util.Map<java.lang.Thread,java.lang.StackTraceElement[]> getAllStackTraces();
+    method @Nullable public ClassLoader getContextClassLoader();
+    method @Nullable public static java.lang.Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler();
+    method public long getId();
+    method @NonNull public final String getName();
+    method public final int getPriority();
+    method @NonNull public StackTraceElement[] getStackTrace();
+    method @NonNull public java.lang.Thread.State getState();
+    method @Nullable public final ThreadGroup getThreadGroup();
+    method @Nullable public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler();
+    method public static boolean holdsLock(@NonNull Object);
+    method public void interrupt();
+    method public static boolean interrupted();
+    method public final boolean isAlive();
+    method public final boolean isDaemon();
+    method public boolean isInterrupted();
+    method public final void join(long) throws java.lang.InterruptedException;
+    method public final void join(long, int) throws java.lang.InterruptedException;
+    method public final void join() throws java.lang.InterruptedException;
+    method @Deprecated public final void resume();
+    method public void run();
+    method public void setContextClassLoader(@Nullable ClassLoader);
+    method public final void setDaemon(boolean);
+    method public static void setDefaultUncaughtExceptionHandler(@Nullable java.lang.Thread.UncaughtExceptionHandler);
+    method public final void setName(@NonNull String);
+    method public final void setPriority(int);
+    method public void setUncaughtExceptionHandler(@Nullable java.lang.Thread.UncaughtExceptionHandler);
+    method public static void sleep(long) throws java.lang.InterruptedException;
+    method public static void sleep(long, int) throws java.lang.InterruptedException;
+    method public void start();
+    method @Deprecated public final void stop();
+    method @Deprecated public final void stop(@Nullable Throwable);
+    method @Deprecated public final void suspend();
+    method public static void yield();
+    field public static final int MAX_PRIORITY = 10; // 0xa
+    field public static final int MIN_PRIORITY = 1; // 0x1
+    field public static final int NORM_PRIORITY = 5; // 0x5
+  }
+
+  public enum Thread.State {
+    enum_constant public static final java.lang.Thread.State BLOCKED;
+    enum_constant public static final java.lang.Thread.State NEW;
+    enum_constant public static final java.lang.Thread.State RUNNABLE;
+    enum_constant public static final java.lang.Thread.State TERMINATED;
+    enum_constant public static final java.lang.Thread.State TIMED_WAITING;
+    enum_constant public static final java.lang.Thread.State WAITING;
+  }
+
+  @java.lang.FunctionalInterface public static interface Thread.UncaughtExceptionHandler {
+    method public void uncaughtException(@NonNull Thread, @NonNull Throwable);
+  }
+
+  public class ThreadDeath extends java.lang.Error {
+    ctor public ThreadDeath();
+  }
+
+  public class ThreadGroup implements java.lang.Thread.UncaughtExceptionHandler {
+    ctor public ThreadGroup(String);
+    ctor public ThreadGroup(ThreadGroup, String);
+    method public int activeCount();
+    method public int activeGroupCount();
+    method @Deprecated public boolean allowThreadSuspension(boolean);
+    method public final void checkAccess();
+    method public final void destroy();
+    method public int enumerate(Thread[]);
+    method public int enumerate(Thread[], boolean);
+    method public int enumerate(ThreadGroup[]);
+    method public int enumerate(ThreadGroup[], boolean);
+    method public final int getMaxPriority();
+    method public final String getName();
+    method public final ThreadGroup getParent();
+    method public final void interrupt();
+    method public final boolean isDaemon();
+    method public boolean isDestroyed();
+    method public void list();
+    method public final boolean parentOf(ThreadGroup);
+    method @Deprecated public final void resume();
+    method public final void setDaemon(boolean);
+    method public final void setMaxPriority(int);
+    method @Deprecated public final void stop();
+    method @Deprecated public final void suspend();
+    method public void uncaughtException(Thread, Throwable);
+  }
+
+  public class ThreadLocal<T> {
+    ctor public ThreadLocal();
+    method @Nullable public T get();
+    method @Nullable protected T initialValue();
+    method public void remove();
+    method public void set(T);
+    method @NonNull public static <S> ThreadLocal<S> withInitial(@NonNull java.util.function.Supplier<? extends S>);
+  }
+
+  public class Throwable implements java.io.Serializable {
+    ctor public Throwable();
+    ctor public Throwable(@Nullable String);
+    ctor public Throwable(@Nullable String, @Nullable Throwable);
+    ctor public Throwable(@Nullable Throwable);
+    ctor protected Throwable(@Nullable String, @Nullable Throwable, boolean, boolean);
+    method public final void addSuppressed(@NonNull Throwable);
+    method @NonNull public Throwable fillInStackTrace();
+    method @Nullable public Throwable getCause();
+    method @Nullable public String getLocalizedMessage();
+    method @Nullable public String getMessage();
+    method @NonNull public StackTraceElement[] getStackTrace();
+    method @NonNull public final Throwable[] getSuppressed();
+    method @NonNull public Throwable initCause(@Nullable Throwable);
+    method public void printStackTrace();
+    method public void printStackTrace(@NonNull java.io.PrintStream);
+    method public void printStackTrace(@NonNull java.io.PrintWriter);
+    method public void setStackTrace(@NonNull StackTraceElement[]);
+  }
+
+  public class TypeNotPresentException extends java.lang.RuntimeException {
+    ctor public TypeNotPresentException(String, Throwable);
+    method public String typeName();
+  }
+
+  public class UnknownError extends java.lang.VirtualMachineError {
+    ctor public UnknownError();
+    ctor public UnknownError(String);
+  }
+
+  public class UnsatisfiedLinkError extends java.lang.LinkageError {
+    ctor public UnsatisfiedLinkError();
+    ctor public UnsatisfiedLinkError(String);
+  }
+
+  public class UnsupportedClassVersionError extends java.lang.ClassFormatError {
+    ctor public UnsupportedClassVersionError();
+    ctor public UnsupportedClassVersionError(String);
+  }
+
+  public class UnsupportedOperationException extends java.lang.RuntimeException {
+    ctor public UnsupportedOperationException();
+    ctor public UnsupportedOperationException(String);
+    ctor public UnsupportedOperationException(String, Throwable);
+    ctor public UnsupportedOperationException(Throwable);
+  }
+
+  public class VerifyError extends java.lang.LinkageError {
+    ctor public VerifyError();
+    ctor public VerifyError(String);
+  }
+
+  public abstract class VirtualMachineError extends java.lang.Error {
+    ctor public VirtualMachineError();
+    ctor public VirtualMachineError(String);
+    ctor public VirtualMachineError(String, Throwable);
+    ctor public VirtualMachineError(Throwable);
+  }
+
+  public final class Void {
+    field public static final Class<java.lang.Void> TYPE;
+  }
+
+}
+
+package java.lang.annotation {
+
+  public interface Annotation {
+    method public Class<? extends java.lang.annotation.Annotation> annotationType();
+    method public boolean equals(Object);
+    method public int hashCode();
+    method public String toString();
+  }
+
+  public class AnnotationFormatError extends java.lang.Error {
+    ctor public AnnotationFormatError(String);
+    ctor public AnnotationFormatError(String, Throwable);
+    ctor public AnnotationFormatError(Throwable);
+  }
+
+  public class AnnotationTypeMismatchException extends java.lang.RuntimeException {
+    ctor public AnnotationTypeMismatchException(java.lang.reflect.Method, String);
+    method public java.lang.reflect.Method element();
+    method public String foundType();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.ANNOTATION_TYPE) public @interface Documented {
+  }
+
+  public enum ElementType {
+    enum_constant public static final java.lang.annotation.ElementType ANNOTATION_TYPE;
+    enum_constant public static final java.lang.annotation.ElementType CONSTRUCTOR;
+    enum_constant public static final java.lang.annotation.ElementType FIELD;
+    enum_constant public static final java.lang.annotation.ElementType LOCAL_VARIABLE;
+    enum_constant public static final java.lang.annotation.ElementType METHOD;
+    enum_constant public static final java.lang.annotation.ElementType MODULE;
+    enum_constant public static final java.lang.annotation.ElementType PACKAGE;
+    enum_constant public static final java.lang.annotation.ElementType PARAMETER;
+    enum_constant public static final java.lang.annotation.ElementType TYPE;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_PARAMETER;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_USE;
+  }
+
+  public class IncompleteAnnotationException extends java.lang.RuntimeException {
+    ctor public IncompleteAnnotationException(Class<? extends java.lang.annotation.Annotation>, String);
+    method public Class<? extends java.lang.annotation.Annotation> annotationType();
+    method public String elementName();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.ANNOTATION_TYPE) public @interface Inherited {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.FIELD) public @interface Native {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.ANNOTATION_TYPE) public @interface Repeatable {
+    method public abstract Class<? extends java.lang.annotation.Annotation> value();
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.ANNOTATION_TYPE) public @interface Retention {
+    method public abstract java.lang.annotation.RetentionPolicy value();
+  }
+
+  public enum RetentionPolicy {
+    enum_constant public static final java.lang.annotation.RetentionPolicy CLASS;
+    enum_constant public static final java.lang.annotation.RetentionPolicy RUNTIME;
+    enum_constant public static final java.lang.annotation.RetentionPolicy SOURCE;
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target(java.lang.annotation.ElementType.ANNOTATION_TYPE) public @interface Target {
+    method public abstract java.lang.annotation.ElementType[] value();
+  }
+
+}
+
+package java.lang.invoke {
+
+  public abstract class CallSite {
+    method public abstract java.lang.invoke.MethodHandle dynamicInvoker();
+    method public abstract java.lang.invoke.MethodHandle getTarget();
+    method public abstract void setTarget(java.lang.invoke.MethodHandle);
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public class ConstantCallSite extends java.lang.invoke.CallSite {
+    ctor public ConstantCallSite(java.lang.invoke.MethodHandle);
+    ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable;
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public final void setTarget(java.lang.invoke.MethodHandle);
+  }
+
+  public class LambdaConversionException extends java.lang.Exception {
+    ctor public LambdaConversionException();
+    ctor public LambdaConversionException(String);
+    ctor public LambdaConversionException(String, Throwable);
+    ctor public LambdaConversionException(Throwable);
+    ctor public LambdaConversionException(String, Throwable, boolean, boolean);
+  }
+
+  public abstract class MethodHandle {
+    method public java.lang.invoke.MethodHandle asCollector(Class<?>, int);
+    method public java.lang.invoke.MethodHandle asFixedArity();
+    method public java.lang.invoke.MethodHandle asSpreader(Class<?>, int);
+    method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType);
+    method public java.lang.invoke.MethodHandle asVarargsCollector(Class<?>);
+    method public java.lang.invoke.MethodHandle bindTo(Object);
+    method public final Object invoke(java.lang.Object...) throws java.lang.Throwable;
+    method public final Object invokeExact(java.lang.Object...) throws java.lang.Throwable;
+    method public Object invokeWithArguments(java.lang.Object...) throws java.lang.Throwable;
+    method public Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable;
+    method public boolean isVarargsCollector();
+    method public java.lang.invoke.MethodType type();
+  }
+
+  public interface MethodHandleInfo {
+    method public Class<?> getDeclaringClass();
+    method public java.lang.invoke.MethodType getMethodType();
+    method public int getModifiers();
+    method public String getName();
+    method public int getReferenceKind();
+    method public default boolean isVarArgs();
+    method @Deprecated public static boolean refKindIsField(int);
+    method @Deprecated public static boolean refKindIsValid(int);
+    method @Deprecated public static String refKindName(int);
+    method public static String referenceKindToString(int);
+    method public <T extends java.lang.reflect.Member> T reflectAs(Class<T>, java.lang.invoke.MethodHandles.Lookup);
+    method public static String toString(int, Class<?>, String, java.lang.invoke.MethodType);
+    field public static final int REF_getField = 1; // 0x1
+    field public static final int REF_getStatic = 2; // 0x2
+    field public static final int REF_invokeInterface = 9; // 0x9
+    field public static final int REF_invokeSpecial = 7; // 0x7
+    field public static final int REF_invokeStatic = 6; // 0x6
+    field public static final int REF_invokeVirtual = 5; // 0x5
+    field public static final int REF_newInvokeSpecial = 8; // 0x8
+    field public static final int REF_putField = 3; // 0x3
+    field public static final int REF_putStatic = 4; // 0x4
+  }
+
+  public class MethodHandles {
+    method public static java.lang.invoke.MethodHandle arrayElementGetter(Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle arrayElementSetter(Class<?>) throws java.lang.IllegalArgumentException;
+    method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle collectArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle constant(Class<?>, Object);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, Class<?>...);
+    method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
+    method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle identity(Class<?>);
+    method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...);
+    method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandles.Lookup lookup();
+    method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...);
+    method public static java.lang.invoke.MethodHandles.Lookup publicLookup();
+    method public static <T extends java.lang.reflect.Member> T reflectAs(Class<T>, java.lang.invoke.MethodHandle);
+    method public static java.lang.invoke.MethodHandle spreadInvoker(java.lang.invoke.MethodType, int);
+    method public static java.lang.invoke.MethodHandle throwException(Class<?>, Class<? extends java.lang.Throwable>);
+  }
+
+  public static final class MethodHandles.Lookup {
+    method public java.lang.invoke.MethodHandle bind(Object, String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findConstructor(Class<?>, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findGetter(Class<?>, String, Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSetter(Class<?>, String, Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findSpecial(Class<?>, String, java.lang.invoke.MethodType, Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStatic(Class<?>, String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandle findStaticGetter(Class<?>, String, Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findStaticSetter(Class<?>, String, Class<?>) throws java.lang.IllegalAccessException, java.lang.NoSuchFieldException;
+    method public java.lang.invoke.MethodHandle findVirtual(Class<?>, String, java.lang.invoke.MethodType) throws java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
+    method public java.lang.invoke.MethodHandles.Lookup in(Class<?>);
+    method public Class<?> lookupClass();
+    method public int lookupModes();
+    method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle);
+    method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
+    method public java.lang.invoke.MethodHandle unreflectSpecial(java.lang.reflect.Method, Class<?>) throws java.lang.IllegalAccessException;
+    field public static final int PACKAGE = 8; // 0x8
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+  }
+
+  public final class MethodType implements java.io.Serializable {
+    method public java.lang.invoke.MethodType appendParameterTypes(Class<?>...);
+    method public java.lang.invoke.MethodType appendParameterTypes(java.util.List<java.lang.Class<?>>);
+    method public java.lang.invoke.MethodType changeParameterType(int, Class<?>);
+    method public java.lang.invoke.MethodType changeReturnType(Class<?>);
+    method public java.lang.invoke.MethodType dropParameterTypes(int, int);
+    method public java.lang.invoke.MethodType erase();
+    method public static java.lang.invoke.MethodType fromMethodDescriptorString(String, ClassLoader) throws java.lang.IllegalArgumentException, java.lang.TypeNotPresentException;
+    method public java.lang.invoke.MethodType generic();
+    method public static java.lang.invoke.MethodType genericMethodType(int, boolean);
+    method public static java.lang.invoke.MethodType genericMethodType(int);
+    method public boolean hasPrimitives();
+    method public boolean hasWrappers();
+    method public java.lang.invoke.MethodType insertParameterTypes(int, Class<?>...);
+    method public java.lang.invoke.MethodType insertParameterTypes(int, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(Class<?>, Class<?>[]);
+    method public static java.lang.invoke.MethodType methodType(Class<?>, java.util.List<java.lang.Class<?>>);
+    method public static java.lang.invoke.MethodType methodType(Class<?>, Class<?>, Class<?>...);
+    method public static java.lang.invoke.MethodType methodType(Class<?>);
+    method public static java.lang.invoke.MethodType methodType(Class<?>, Class<?>);
+    method public static java.lang.invoke.MethodType methodType(Class<?>, java.lang.invoke.MethodType);
+    method public Class<?>[] parameterArray();
+    method public int parameterCount();
+    method public java.util.List<java.lang.Class<?>> parameterList();
+    method public Class<?> parameterType(int);
+    method public Class<?> returnType();
+    method public String toMethodDescriptorString();
+    method public java.lang.invoke.MethodType unwrap();
+    method public java.lang.invoke.MethodType wrap();
+  }
+
+  public class MutableCallSite extends java.lang.invoke.CallSite {
+    ctor public MutableCallSite(java.lang.invoke.MethodType);
+    ctor public MutableCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
+  public class VolatileCallSite extends java.lang.invoke.CallSite {
+    ctor public VolatileCallSite(java.lang.invoke.MethodType);
+    ctor public VolatileCallSite(java.lang.invoke.MethodHandle);
+    method public final java.lang.invoke.MethodHandle dynamicInvoker();
+    method public final java.lang.invoke.MethodHandle getTarget();
+    method public void setTarget(java.lang.invoke.MethodHandle);
+  }
+
+  public class WrongMethodTypeException extends java.lang.RuntimeException {
+    ctor public WrongMethodTypeException();
+    ctor public WrongMethodTypeException(String);
+  }
+
+}
+
+package java.lang.ref {
+
+  public class PhantomReference<T> extends java.lang.ref.Reference<T> {
+    ctor public PhantomReference(T, java.lang.ref.ReferenceQueue<? super T>);
+  }
+
+  public abstract class Reference<T> {
+    method public void clear();
+    method public boolean enqueue();
+    method public T get();
+    method @Deprecated public boolean isEnqueued();
+    method public static void reachabilityFence(Object);
+  }
+
+  public class ReferenceQueue<T> {
+    ctor public ReferenceQueue();
+    method public java.lang.ref.Reference<? extends T> poll();
+    method public java.lang.ref.Reference<? extends T> remove(long) throws java.lang.IllegalArgumentException, java.lang.InterruptedException;
+    method public java.lang.ref.Reference<? extends T> remove() throws java.lang.InterruptedException;
+  }
+
+  public class SoftReference<T> extends java.lang.ref.Reference<T> {
+    ctor public SoftReference(T);
+    ctor public SoftReference(T, java.lang.ref.ReferenceQueue<? super T>);
+  }
+
+  public class WeakReference<T> extends java.lang.ref.Reference<T> {
+    ctor public WeakReference(T);
+    ctor public WeakReference(T, java.lang.ref.ReferenceQueue<? super T>);
+  }
+
+}
+
+package java.lang.reflect {
+
+  public class AccessibleObject implements java.lang.reflect.AnnotatedElement {
+    ctor protected AccessibleObject();
+    method @Nullable public <T extends java.lang.annotation.Annotation> T getAnnotation(@NonNull Class<T>);
+    method @NonNull public java.lang.annotation.Annotation[] getAnnotations();
+    method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public boolean isAccessible();
+    method public static void setAccessible(java.lang.reflect.AccessibleObject[], boolean) throws java.lang.SecurityException;
+    method public void setAccessible(boolean) throws java.lang.SecurityException;
+  }
+
+  public interface AnnotatedElement {
+    method @Nullable public <T extends java.lang.annotation.Annotation> T getAnnotation(@NonNull Class<T>);
+    method @NonNull public java.lang.annotation.Annotation[] getAnnotations();
+    method public default <T extends java.lang.annotation.Annotation> T[] getAnnotationsByType(@NonNull Class<T>);
+    method @Nullable public default <T extends java.lang.annotation.Annotation> T getDeclaredAnnotation(@NonNull Class<T>);
+    method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method public default <T extends java.lang.annotation.Annotation> T[] getDeclaredAnnotationsByType(@NonNull Class<T>);
+    method public default boolean isAnnotationPresent(@NonNull Class<? extends java.lang.annotation.Annotation>);
+  }
+
+  public final class Array {
+    method @Nullable public static Object get(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static boolean getBoolean(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static byte getByte(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static char getChar(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static double getDouble(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static float getFloat(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static int getInt(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static int getLength(@NonNull Object);
+    method public static long getLong(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static short getShort(@NonNull Object, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method @NonNull public static Object newInstance(@NonNull Class<?>, int) throws java.lang.NegativeArraySizeException;
+    method @NonNull public static Object newInstance(@NonNull Class<?>, int...) throws java.lang.IllegalArgumentException, java.lang.NegativeArraySizeException;
+    method public static void set(@NonNull Object, int, @Nullable Object) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static void setBoolean(@NonNull Object, int, boolean);
+    method public static void setByte(@NonNull Object, int, byte) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static void setChar(@NonNull Object, int, char) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static void setDouble(@NonNull Object, int, double) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static void setFloat(@NonNull Object, int, float) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static void setInt(@NonNull Object, int, int) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static void setLong(@NonNull Object, int, long) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+    method public static void setShort(@NonNull Object, int, short) throws java.lang.ArrayIndexOutOfBoundsException, java.lang.IllegalArgumentException;
+  }
+
+  public final class Constructor<T> extends java.lang.reflect.Executable {
+    method @NonNull public Class<T> getDeclaringClass();
+    method public Class<?>[] getExceptionTypes();
+    method public int getModifiers();
+    method @NonNull public String getName();
+    method public java.lang.annotation.Annotation[][] getParameterAnnotations();
+    method @NonNull public Class<?>[] getParameterTypes();
+    method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters();
+    method @NonNull public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
+    method @NonNull public String toGenericString();
+  }
+
+  public abstract class Executable extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method @NonNull public abstract Class<?>[] getExceptionTypes();
+    method @NonNull public java.lang.reflect.Type[] getGenericExceptionTypes();
+    method @NonNull public java.lang.reflect.Type[] getGenericParameterTypes();
+    method @NonNull public abstract java.lang.annotation.Annotation[][] getParameterAnnotations();
+    method public int getParameterCount();
+    method @NonNull public abstract Class<?>[] getParameterTypes();
+    method @NonNull public java.lang.reflect.Parameter[] getParameters();
+    method public final boolean isAnnotationPresent(@NonNull Class<? extends java.lang.annotation.Annotation>);
+    method public boolean isSynthetic();
+    method public boolean isVarArgs();
+    method @NonNull public abstract String toGenericString();
+  }
+
+  public final class Field extends java.lang.reflect.AccessibleObject implements java.lang.reflect.Member {
+    method @Nullable public Object get(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public boolean getBoolean(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public byte getByte(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public char getChar(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method @NonNull public Class<?> getDeclaringClass();
+    method public double getDouble(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public float getFloat(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method @NonNull public java.lang.reflect.Type getGenericType();
+    method public int getInt(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public long getLong(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public int getModifiers();
+    method @NonNull public String getName();
+    method public short getShort(@Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method @NonNull public Class<?> getType();
+    method public boolean isEnumConstant();
+    method public boolean isSynthetic();
+    method public void set(@Nullable Object, @Nullable Object) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setBoolean(@Nullable Object, boolean) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setByte(@Nullable Object, byte) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setChar(@Nullable Object, char) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setDouble(@Nullable Object, double) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setFloat(@Nullable Object, float) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setInt(@Nullable Object, int) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setLong(@Nullable Object, long) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method public void setShort(@Nullable Object, short) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException;
+    method @NonNull public String toGenericString();
+  }
+
+  public interface GenericArrayType extends java.lang.reflect.Type {
+    method @NonNull public java.lang.reflect.Type getGenericComponentType();
+  }
+
+  public interface GenericDeclaration extends java.lang.reflect.AnnotatedElement {
+    method @NonNull public java.lang.reflect.TypeVariable<?>[] getTypeParameters();
+  }
+
+  public class GenericSignatureFormatError extends java.lang.ClassFormatError {
+    ctor public GenericSignatureFormatError();
+    ctor public GenericSignatureFormatError(String);
+  }
+
+  public interface InvocationHandler {
+    method public Object invoke(Object, java.lang.reflect.Method, Object[]) throws java.lang.Throwable;
+  }
+
+  public class InvocationTargetException extends java.lang.ReflectiveOperationException {
+    ctor protected InvocationTargetException();
+    ctor public InvocationTargetException(Throwable);
+    ctor public InvocationTargetException(Throwable, String);
+    method public Throwable getTargetException();
+  }
+
+  public class MalformedParameterizedTypeException extends java.lang.RuntimeException {
+    ctor public MalformedParameterizedTypeException();
+  }
+
+  public class MalformedParametersException extends java.lang.RuntimeException {
+    ctor public MalformedParametersException();
+    ctor public MalformedParametersException(String);
+  }
+
+  public interface Member {
+    method @NonNull public Class<?> getDeclaringClass();
+    method public int getModifiers();
+    method @NonNull public String getName();
+    method public boolean isSynthetic();
+    field public static final int DECLARED = 1; // 0x1
+    field public static final int PUBLIC = 0; // 0x0
+  }
+
+  public final class Method extends java.lang.reflect.Executable {
+    method @NonNull public Class<?> getDeclaringClass();
+    method @Nullable public Object getDefaultValue();
+    method @NonNull public Class<?>[] getExceptionTypes();
+    method @NonNull public java.lang.reflect.Type getGenericReturnType();
+    method public int getModifiers();
+    method @NonNull public String getName();
+    method @NonNull public java.lang.annotation.Annotation[][] getParameterAnnotations();
+    method @NonNull public Class<?>[] getParameterTypes();
+    method @NonNull public Class<?> getReturnType();
+    method @NonNull public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
+    method @Nullable public Object invoke(@Nullable Object, @Nullable java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
+    method public boolean isBridge();
+    method public boolean isDefault();
+    method @NonNull public String toGenericString();
+  }
+
+  public class Modifier {
+    ctor public Modifier();
+    method public static int classModifiers();
+    method public static int constructorModifiers();
+    method public static int fieldModifiers();
+    method public static int interfaceModifiers();
+    method public static boolean isAbstract(int);
+    method public static boolean isFinal(int);
+    method public static boolean isInterface(int);
+    method public static boolean isNative(int);
+    method public static boolean isPrivate(int);
+    method public static boolean isProtected(int);
+    method public static boolean isPublic(int);
+    method public static boolean isStatic(int);
+    method public static boolean isStrict(int);
+    method public static boolean isSynchronized(int);
+    method public static boolean isTransient(int);
+    method public static boolean isVolatile(int);
+    method public static int methodModifiers();
+    method public static int parameterModifiers();
+    method public static String toString(int);
+    field public static final int ABSTRACT = 1024; // 0x400
+    field public static final int FINAL = 16; // 0x10
+    field public static final int INTERFACE = 512; // 0x200
+    field public static final int NATIVE = 256; // 0x100
+    field public static final int PRIVATE = 2; // 0x2
+    field public static final int PROTECTED = 4; // 0x4
+    field public static final int PUBLIC = 1; // 0x1
+    field public static final int STATIC = 8; // 0x8
+    field public static final int STRICT = 2048; // 0x800
+    field public static final int SYNCHRONIZED = 32; // 0x20
+    field public static final int TRANSIENT = 128; // 0x80
+    field public static final int VOLATILE = 64; // 0x40
+  }
+
+  public final class Parameter implements java.lang.reflect.AnnotatedElement {
+    method @Nullable public <T extends java.lang.annotation.Annotation> T getAnnotation(@NonNull Class<T>);
+    method @NonNull public java.lang.annotation.Annotation[] getAnnotations();
+    method @NonNull public java.lang.annotation.Annotation[] getDeclaredAnnotations();
+    method @NonNull public java.lang.reflect.Executable getDeclaringExecutable();
+    method public int getModifiers();
+    method @NonNull public String getName();
+    method @NonNull public java.lang.reflect.Type getParameterizedType();
+    method @NonNull public Class<?> getType();
+    method public boolean isImplicit();
+    method public boolean isNamePresent();
+    method public boolean isSynthetic();
+    method public boolean isVarArgs();
+  }
+
+  public interface ParameterizedType extends java.lang.reflect.Type {
+    method @NonNull public java.lang.reflect.Type[] getActualTypeArguments();
+    method @Nullable public java.lang.reflect.Type getOwnerType();
+    method @NonNull public java.lang.reflect.Type getRawType();
+  }
+
+  public class Proxy implements java.io.Serializable {
+    ctor protected Proxy(@NonNull java.lang.reflect.InvocationHandler);
+    method @NonNull public static java.lang.reflect.InvocationHandler getInvocationHandler(@NonNull Object) throws java.lang.IllegalArgumentException;
+    method @NonNull public static Class<?> getProxyClass(@Nullable ClassLoader, @NonNull Class<?>...) throws java.lang.IllegalArgumentException;
+    method public static boolean isProxyClass(@NonNull Class<?>);
+    method @NonNull public static Object newProxyInstance(@Nullable ClassLoader, @NonNull Class<?>[], @NonNull java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentException;
+    field protected java.lang.reflect.InvocationHandler h;
+  }
+
+  public final class ReflectPermission extends java.security.BasicPermission {
+    ctor public ReflectPermission(String);
+    ctor public ReflectPermission(String, String);
+  }
+
+  public interface Type {
+    method @NonNull public default String getTypeName();
+  }
+
+  public interface TypeVariable<D extends java.lang.reflect.GenericDeclaration> extends java.lang.reflect.Type {
+    method @NonNull public java.lang.reflect.Type[] getBounds();
+    method @NonNull public D getGenericDeclaration();
+    method @NonNull public String getName();
+  }
+
+  public class UndeclaredThrowableException extends java.lang.RuntimeException {
+    ctor public UndeclaredThrowableException(Throwable);
+    ctor public UndeclaredThrowableException(Throwable, String);
+    method public Throwable getUndeclaredThrowable();
+  }
+
+  public interface WildcardType extends java.lang.reflect.Type {
+    method @NonNull public java.lang.reflect.Type[] getLowerBounds();
+    method @NonNull public java.lang.reflect.Type[] getUpperBounds();
+  }
+
+}
+
+package java.math {
+
+  public class BigDecimal extends java.lang.Number implements java.lang.Comparable<java.math.BigDecimal> {
+    ctor public BigDecimal(char[], int, int);
+    ctor public BigDecimal(char[], int, int, java.math.MathContext);
+    ctor public BigDecimal(char[]);
+    ctor public BigDecimal(char[], java.math.MathContext);
+    ctor public BigDecimal(String);
+    ctor public BigDecimal(String, java.math.MathContext);
+    ctor public BigDecimal(double);
+    ctor public BigDecimal(double, java.math.MathContext);
+    ctor public BigDecimal(java.math.BigInteger);
+    ctor public BigDecimal(java.math.BigInteger, java.math.MathContext);
+    ctor public BigDecimal(java.math.BigInteger, int);
+    ctor public BigDecimal(java.math.BigInteger, int, java.math.MathContext);
+    ctor public BigDecimal(int);
+    ctor public BigDecimal(int, java.math.MathContext);
+    ctor public BigDecimal(long);
+    ctor public BigDecimal(long, java.math.MathContext);
+    method public java.math.BigDecimal abs();
+    method public java.math.BigDecimal abs(java.math.MathContext);
+    method public java.math.BigDecimal add(java.math.BigDecimal);
+    method public java.math.BigDecimal add(java.math.BigDecimal, java.math.MathContext);
+    method public byte byteValueExact();
+    method public int compareTo(java.math.BigDecimal);
+    method public java.math.BigDecimal divide(java.math.BigDecimal, int, int);
+    method public java.math.BigDecimal divide(java.math.BigDecimal, int, java.math.RoundingMode);
+    method public java.math.BigDecimal divide(java.math.BigDecimal, int);
+    method public java.math.BigDecimal divide(java.math.BigDecimal, java.math.RoundingMode);
+    method public java.math.BigDecimal divide(java.math.BigDecimal);
+    method public java.math.BigDecimal divide(java.math.BigDecimal, java.math.MathContext);
+    method public java.math.BigDecimal[] divideAndRemainder(java.math.BigDecimal);
+    method public java.math.BigDecimal[] divideAndRemainder(java.math.BigDecimal, java.math.MathContext);
+    method public java.math.BigDecimal divideToIntegralValue(java.math.BigDecimal);
+    method public java.math.BigDecimal divideToIntegralValue(java.math.BigDecimal, java.math.MathContext);
+    method public double doubleValue();
+    method public float floatValue();
+    method public int intValue();
+    method public int intValueExact();
+    method public long longValue();
+    method public long longValueExact();
+    method public java.math.BigDecimal max(java.math.BigDecimal);
+    method public java.math.BigDecimal min(java.math.BigDecimal);
+    method public java.math.BigDecimal movePointLeft(int);
+    method public java.math.BigDecimal movePointRight(int);
+    method public java.math.BigDecimal multiply(java.math.BigDecimal);
+    method public java.math.BigDecimal multiply(java.math.BigDecimal, java.math.MathContext);
+    method public java.math.BigDecimal negate();
+    method public java.math.BigDecimal negate(java.math.MathContext);
+    method public java.math.BigDecimal plus();
+    method public java.math.BigDecimal plus(java.math.MathContext);
+    method public java.math.BigDecimal pow(int);
+    method public java.math.BigDecimal pow(int, java.math.MathContext);
+    method public int precision();
+    method public java.math.BigDecimal remainder(java.math.BigDecimal);
+    method public java.math.BigDecimal remainder(java.math.BigDecimal, java.math.MathContext);
+    method public java.math.BigDecimal round(java.math.MathContext);
+    method public int scale();
+    method public java.math.BigDecimal scaleByPowerOfTen(int);
+    method public java.math.BigDecimal setScale(int, java.math.RoundingMode);
+    method public java.math.BigDecimal setScale(int, int);
+    method public java.math.BigDecimal setScale(int);
+    method public short shortValueExact();
+    method public int signum();
+    method public java.math.BigDecimal stripTrailingZeros();
+    method public java.math.BigDecimal subtract(java.math.BigDecimal);
+    method public java.math.BigDecimal subtract(java.math.BigDecimal, java.math.MathContext);
+    method public java.math.BigInteger toBigInteger();
+    method public java.math.BigInteger toBigIntegerExact();
+    method public String toEngineeringString();
+    method public String toPlainString();
+    method public java.math.BigDecimal ulp();
+    method public java.math.BigInteger unscaledValue();
+    method public static java.math.BigDecimal valueOf(long, int);
+    method public static java.math.BigDecimal valueOf(long);
+    method public static java.math.BigDecimal valueOf(double);
+    field public static final java.math.BigDecimal ONE;
+    field public static final int ROUND_CEILING = 2; // 0x2
+    field public static final int ROUND_DOWN = 1; // 0x1
+    field public static final int ROUND_FLOOR = 3; // 0x3
+    field public static final int ROUND_HALF_DOWN = 5; // 0x5
+    field public static final int ROUND_HALF_EVEN = 6; // 0x6
+    field public static final int ROUND_HALF_UP = 4; // 0x4
+    field public static final int ROUND_UNNECESSARY = 7; // 0x7
+    field public static final int ROUND_UP = 0; // 0x0
+    field public static final java.math.BigDecimal TEN;
+    field public static final java.math.BigDecimal ZERO;
+  }
+
+  public class BigInteger extends java.lang.Number implements java.lang.Comparable<java.math.BigInteger> {
+    ctor public BigInteger(byte[]);
+    ctor public BigInteger(int, byte[]);
+    ctor public BigInteger(@NonNull String, int);
+    ctor public BigInteger(@NonNull String);
+    ctor public BigInteger(int, @NonNull java.util.Random);
+    ctor public BigInteger(int, int, @NonNull java.util.Random);
+    method @NonNull public java.math.BigInteger abs();
+    method @NonNull public java.math.BigInteger add(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger and(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger andNot(@NonNull java.math.BigInteger);
+    method public int bitCount();
+    method public int bitLength();
+    method public byte byteValueExact();
+    method @NonNull public java.math.BigInteger clearBit(int);
+    method public int compareTo(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger divide(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger[] divideAndRemainder(@NonNull java.math.BigInteger);
+    method public double doubleValue();
+    method @NonNull public java.math.BigInteger flipBit(int);
+    method public float floatValue();
+    method @NonNull public java.math.BigInteger gcd(@NonNull java.math.BigInteger);
+    method public int getLowestSetBit();
+    method public int intValue();
+    method public int intValueExact();
+    method public boolean isProbablePrime(int);
+    method public long longValue();
+    method public long longValueExact();
+    method @NonNull public java.math.BigInteger max(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger min(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger mod(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger modInverse(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger modPow(@NonNull java.math.BigInteger, @NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger multiply(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger negate();
+    method @NonNull public java.math.BigInteger nextProbablePrime();
+    method @NonNull public java.math.BigInteger not();
+    method @NonNull public java.math.BigInteger or(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger pow(int);
+    method @NonNull public static java.math.BigInteger probablePrime(int, @NonNull java.util.Random);
+    method @NonNull public java.math.BigInteger remainder(@NonNull java.math.BigInteger);
+    method @NonNull public java.math.BigInteger setBit(int);
+    method @NonNull public java.math.BigInteger shiftLeft(int);
+    method @NonNull public java.math.BigInteger shiftRight(int);
+    method public short shortValueExact();
+    method public int signum();
+    method @NonNull public java.math.BigInteger subtract(@NonNull java.math.BigInteger);
+    method public boolean testBit(int);
+    method public byte[] toByteArray();
+    method @NonNull public String toString(int);
+    method @NonNull public static java.math.BigInteger valueOf(long);
+    method @NonNull public java.math.BigInteger xor(@NonNull java.math.BigInteger);
+    field @NonNull public static final java.math.BigInteger ONE;
+    field @NonNull public static final java.math.BigInteger TEN;
+    field @NonNull public static final java.math.BigInteger ZERO;
+  }
+
+  public final class MathContext implements java.io.Serializable {
+    ctor public MathContext(int);
+    ctor public MathContext(int, java.math.RoundingMode);
+    ctor public MathContext(String);
+    method public int getPrecision();
+    method public java.math.RoundingMode getRoundingMode();
+    field public static final java.math.MathContext DECIMAL128;
+    field public static final java.math.MathContext DECIMAL32;
+    field public static final java.math.MathContext DECIMAL64;
+    field public static final java.math.MathContext UNLIMITED;
+  }
+
+  public enum RoundingMode {
+    method public static java.math.RoundingMode valueOf(int);
+    enum_constant public static final java.math.RoundingMode CEILING;
+    enum_constant public static final java.math.RoundingMode DOWN;
+    enum_constant public static final java.math.RoundingMode FLOOR;
+    enum_constant public static final java.math.RoundingMode HALF_DOWN;
+    enum_constant public static final java.math.RoundingMode HALF_EVEN;
+    enum_constant public static final java.math.RoundingMode HALF_UP;
+    enum_constant public static final java.math.RoundingMode UNNECESSARY;
+    enum_constant public static final java.math.RoundingMode UP;
+  }
+
+}
+
+package java.net {
+
+  public abstract class Authenticator {
+    ctor public Authenticator();
+    method protected java.net.PasswordAuthentication getPasswordAuthentication();
+    method protected final String getRequestingHost();
+    method protected final int getRequestingPort();
+    method protected final String getRequestingPrompt();
+    method protected final String getRequestingProtocol();
+    method protected final String getRequestingScheme();
+    method protected final java.net.InetAddress getRequestingSite();
+    method protected java.net.URL getRequestingURL();
+    method protected java.net.Authenticator.RequestorType getRequestorType();
+    method public static java.net.PasswordAuthentication requestPasswordAuthentication(java.net.InetAddress, int, String, String, String);
+    method public static java.net.PasswordAuthentication requestPasswordAuthentication(String, java.net.InetAddress, int, String, String, String);
+    method public static java.net.PasswordAuthentication requestPasswordAuthentication(String, java.net.InetAddress, int, String, String, String, java.net.URL, java.net.Authenticator.RequestorType);
+    method public static void setDefault(java.net.Authenticator);
+  }
+
+  public enum Authenticator.RequestorType {
+    enum_constant public static final java.net.Authenticator.RequestorType PROXY;
+    enum_constant public static final java.net.Authenticator.RequestorType SERVER;
+  }
+
+  public class BindException extends java.net.SocketException {
+    ctor public BindException(String);
+    ctor public BindException();
+  }
+
+  public abstract class CacheRequest {
+    ctor public CacheRequest();
+    method public abstract void abort();
+    method public abstract java.io.OutputStream getBody() throws java.io.IOException;
+  }
+
+  public abstract class CacheResponse {
+    ctor public CacheResponse();
+    method public abstract java.io.InputStream getBody() throws java.io.IOException;
+    method public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> getHeaders() throws java.io.IOException;
+  }
+
+  public class ConnectException extends java.net.SocketException {
+    ctor public ConnectException(String);
+    ctor public ConnectException();
+  }
+
+  public abstract class ContentHandler {
+    ctor public ContentHandler();
+    method public abstract Object getContent(java.net.URLConnection) throws java.io.IOException;
+    method public Object getContent(java.net.URLConnection, Class[]) throws java.io.IOException;
+  }
+
+  public interface ContentHandlerFactory {
+    method public java.net.ContentHandler createContentHandler(String);
+  }
+
+  public abstract class CookieHandler {
+    ctor public CookieHandler();
+    method public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> get(java.net.URI, java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method public static java.net.CookieHandler getDefault();
+    method public abstract void put(java.net.URI, java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method public static void setDefault(java.net.CookieHandler);
+  }
+
+  public class CookieManager extends java.net.CookieHandler {
+    ctor public CookieManager();
+    ctor public CookieManager(java.net.CookieStore, java.net.CookiePolicy);
+    method public java.util.Map<java.lang.String,java.util.List<java.lang.String>> get(java.net.URI, java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method public java.net.CookieStore getCookieStore();
+    method public void put(java.net.URI, java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method public void setCookiePolicy(java.net.CookiePolicy);
+  }
+
+  public interface CookiePolicy {
+    method public boolean shouldAccept(java.net.URI, java.net.HttpCookie);
+    field public static final java.net.CookiePolicy ACCEPT_ALL;
+    field public static final java.net.CookiePolicy ACCEPT_NONE;
+    field public static final java.net.CookiePolicy ACCEPT_ORIGINAL_SERVER;
+  }
+
+  public interface CookieStore {
+    method public void add(java.net.URI, java.net.HttpCookie);
+    method public java.util.List<java.net.HttpCookie> get(java.net.URI);
+    method public java.util.List<java.net.HttpCookie> getCookies();
+    method public java.util.List<java.net.URI> getURIs();
+    method public boolean remove(java.net.URI, java.net.HttpCookie);
+    method public boolean removeAll();
+  }
+
+  public final class DatagramPacket {
+    ctor public DatagramPacket(byte[], int, int);
+    ctor public DatagramPacket(byte[], int);
+    ctor public DatagramPacket(byte[], int, int, java.net.InetAddress, int);
+    ctor public DatagramPacket(byte[], int, int, java.net.SocketAddress);
+    ctor public DatagramPacket(byte[], int, java.net.InetAddress, int);
+    ctor public DatagramPacket(byte[], int, java.net.SocketAddress);
+    method public java.net.InetAddress getAddress();
+    method public byte[] getData();
+    method public int getLength();
+    method public int getOffset();
+    method public int getPort();
+    method public java.net.SocketAddress getSocketAddress();
+    method public void setAddress(java.net.InetAddress);
+    method public void setData(byte[], int, int);
+    method public void setData(byte[]);
+    method public void setLength(int);
+    method public void setPort(int);
+    method public void setSocketAddress(java.net.SocketAddress);
+  }
+
+  public class DatagramSocket implements java.io.Closeable {
+    ctor public DatagramSocket() throws java.net.SocketException;
+    ctor protected DatagramSocket(java.net.DatagramSocketImpl);
+    ctor public DatagramSocket(java.net.SocketAddress) throws java.net.SocketException;
+    ctor public DatagramSocket(int) throws java.net.SocketException;
+    ctor public DatagramSocket(int, java.net.InetAddress) throws java.net.SocketException;
+    method public void bind(java.net.SocketAddress) throws java.net.SocketException;
+    method public void close();
+    method public void connect(java.net.InetAddress, int);
+    method public void connect(java.net.SocketAddress) throws java.net.SocketException;
+    method public void disconnect();
+    method public boolean getBroadcast() throws java.net.SocketException;
+    method public java.nio.channels.DatagramChannel getChannel();
+    method public java.net.InetAddress getInetAddress();
+    method public java.net.InetAddress getLocalAddress();
+    method public int getLocalPort();
+    method public java.net.SocketAddress getLocalSocketAddress();
+    method public int getPort();
+    method public int getReceiveBufferSize() throws java.net.SocketException;
+    method public java.net.SocketAddress getRemoteSocketAddress();
+    method public boolean getReuseAddress() throws java.net.SocketException;
+    method public int getSendBufferSize() throws java.net.SocketException;
+    method public int getSoTimeout() throws java.net.SocketException;
+    method public int getTrafficClass() throws java.net.SocketException;
+    method public boolean isBound();
+    method public boolean isClosed();
+    method public boolean isConnected();
+    method public void receive(java.net.DatagramPacket) throws java.io.IOException;
+    method public void send(java.net.DatagramPacket) throws java.io.IOException;
+    method public void setBroadcast(boolean) throws java.net.SocketException;
+    method public static void setDatagramSocketImplFactory(java.net.DatagramSocketImplFactory) throws java.io.IOException;
+    method public void setReceiveBufferSize(int) throws java.net.SocketException;
+    method public void setReuseAddress(boolean) throws java.net.SocketException;
+    method public void setSendBufferSize(int) throws java.net.SocketException;
+    method public void setSoTimeout(int) throws java.net.SocketException;
+    method public void setTrafficClass(int) throws java.net.SocketException;
+  }
+
+  public abstract class DatagramSocketImpl implements java.net.SocketOptions {
+    ctor public DatagramSocketImpl();
+    method protected abstract void bind(int, java.net.InetAddress) throws java.net.SocketException;
+    method protected abstract void close();
+    method protected void connect(java.net.InetAddress, int) throws java.net.SocketException;
+    method protected abstract void create() throws java.net.SocketException;
+    method protected void disconnect();
+    method protected java.io.FileDescriptor getFileDescriptor();
+    method protected int getLocalPort();
+    method @Deprecated protected abstract byte getTTL() throws java.io.IOException;
+    method protected abstract int getTimeToLive() throws java.io.IOException;
+    method protected abstract void join(java.net.InetAddress) throws java.io.IOException;
+    method protected abstract void joinGroup(java.net.SocketAddress, java.net.NetworkInterface) throws java.io.IOException;
+    method protected abstract void leave(java.net.InetAddress) throws java.io.IOException;
+    method protected abstract void leaveGroup(java.net.SocketAddress, java.net.NetworkInterface) throws java.io.IOException;
+    method protected abstract int peek(java.net.InetAddress) throws java.io.IOException;
+    method protected abstract int peekData(java.net.DatagramPacket) throws java.io.IOException;
+    method protected abstract void receive(java.net.DatagramPacket) throws java.io.IOException;
+    method protected abstract void send(java.net.DatagramPacket) throws java.io.IOException;
+    method @Deprecated protected abstract void setTTL(byte) throws java.io.IOException;
+    method protected abstract void setTimeToLive(int) throws java.io.IOException;
+    field protected java.io.FileDescriptor fd;
+    field protected int localPort;
+  }
+
+  public interface DatagramSocketImplFactory {
+    method public java.net.DatagramSocketImpl createDatagramSocketImpl();
+  }
+
+  public interface FileNameMap {
+    method public String getContentTypeFor(String);
+  }
+
+  public final class HttpCookie implements java.lang.Cloneable {
+    ctor public HttpCookie(String, String);
+    method public Object clone();
+    method public static boolean domainMatches(String, String);
+    method public String getComment();
+    method public String getCommentURL();
+    method public boolean getDiscard();
+    method public String getDomain();
+    method public long getMaxAge();
+    method public String getName();
+    method public String getPath();
+    method public String getPortlist();
+    method public boolean getSecure();
+    method public String getValue();
+    method public int getVersion();
+    method public boolean hasExpired();
+    method public boolean isHttpOnly();
+    method public static java.util.List<java.net.HttpCookie> parse(String);
+    method public void setComment(String);
+    method public void setCommentURL(String);
+    method public void setDiscard(boolean);
+    method public void setDomain(String);
+    method public void setHttpOnly(boolean);
+    method public void setMaxAge(long);
+    method public void setPath(String);
+    method public void setPortlist(String);
+    method public void setSecure(boolean);
+    method public void setValue(String);
+    method public void setVersion(int);
+  }
+
+  public class HttpRetryException extends java.io.IOException {
+    ctor public HttpRetryException(String, int);
+    ctor public HttpRetryException(String, int, String);
+    method public String getLocation();
+    method public String getReason();
+    method public int responseCode();
+  }
+
+  public abstract class HttpURLConnection extends java.net.URLConnection {
+    ctor protected HttpURLConnection(java.net.URL);
+    method public abstract void disconnect();
+    method public java.io.InputStream getErrorStream();
+    method public static boolean getFollowRedirects();
+    method public boolean getInstanceFollowRedirects();
+    method public String getRequestMethod();
+    method public int getResponseCode() throws java.io.IOException;
+    method public String getResponseMessage() throws java.io.IOException;
+    method public void setChunkedStreamingMode(int);
+    method public void setFixedLengthStreamingMode(int);
+    method public void setFixedLengthStreamingMode(long);
+    method public static void setFollowRedirects(boolean);
+    method public void setInstanceFollowRedirects(boolean);
+    method public void setRequestMethod(String) throws java.net.ProtocolException;
+    method public abstract boolean usingProxy();
+    field public static final int HTTP_ACCEPTED = 202; // 0xca
+    field public static final int HTTP_BAD_GATEWAY = 502; // 0x1f6
+    field public static final int HTTP_BAD_METHOD = 405; // 0x195
+    field public static final int HTTP_BAD_REQUEST = 400; // 0x190
+    field public static final int HTTP_CLIENT_TIMEOUT = 408; // 0x198
+    field public static final int HTTP_CONFLICT = 409; // 0x199
+    field public static final int HTTP_CREATED = 201; // 0xc9
+    field public static final int HTTP_ENTITY_TOO_LARGE = 413; // 0x19d
+    field public static final int HTTP_FORBIDDEN = 403; // 0x193
+    field public static final int HTTP_GATEWAY_TIMEOUT = 504; // 0x1f8
+    field public static final int HTTP_GONE = 410; // 0x19a
+    field public static final int HTTP_INTERNAL_ERROR = 500; // 0x1f4
+    field public static final int HTTP_LENGTH_REQUIRED = 411; // 0x19b
+    field public static final int HTTP_MOVED_PERM = 301; // 0x12d
+    field public static final int HTTP_MOVED_TEMP = 302; // 0x12e
+    field public static final int HTTP_MULT_CHOICE = 300; // 0x12c
+    field public static final int HTTP_NOT_ACCEPTABLE = 406; // 0x196
+    field public static final int HTTP_NOT_AUTHORITATIVE = 203; // 0xcb
+    field public static final int HTTP_NOT_FOUND = 404; // 0x194
+    field public static final int HTTP_NOT_IMPLEMENTED = 501; // 0x1f5
+    field public static final int HTTP_NOT_MODIFIED = 304; // 0x130
+    field public static final int HTTP_NO_CONTENT = 204; // 0xcc
+    field public static final int HTTP_OK = 200; // 0xc8
+    field public static final int HTTP_PARTIAL = 206; // 0xce
+    field public static final int HTTP_PAYMENT_REQUIRED = 402; // 0x192
+    field public static final int HTTP_PRECON_FAILED = 412; // 0x19c
+    field public static final int HTTP_PROXY_AUTH = 407; // 0x197
+    field public static final int HTTP_REQ_TOO_LONG = 414; // 0x19e
+    field public static final int HTTP_RESET = 205; // 0xcd
+    field public static final int HTTP_SEE_OTHER = 303; // 0x12f
+    field @Deprecated public static final int HTTP_SERVER_ERROR = 500; // 0x1f4
+    field public static final int HTTP_UNAUTHORIZED = 401; // 0x191
+    field public static final int HTTP_UNAVAILABLE = 503; // 0x1f7
+    field public static final int HTTP_UNSUPPORTED_TYPE = 415; // 0x19f
+    field public static final int HTTP_USE_PROXY = 305; // 0x131
+    field public static final int HTTP_VERSION = 505; // 0x1f9
+    field protected int chunkLength;
+    field protected int fixedContentLength;
+    field protected long fixedContentLengthLong;
+    field protected boolean instanceFollowRedirects;
+    field protected String method;
+    field protected int responseCode;
+    field protected String responseMessage;
+  }
+
+  public final class IDN {
+    method public static String toASCII(String, int);
+    method public static String toASCII(String);
+    method public static String toUnicode(String, int);
+    method public static String toUnicode(String);
+    field public static final int ALLOW_UNASSIGNED = 1; // 0x1
+    field public static final int USE_STD3_ASCII_RULES = 2; // 0x2
+  }
+
+  public final class Inet4Address extends java.net.InetAddress {
+  }
+
+  public final class Inet6Address extends java.net.InetAddress {
+    method public static java.net.Inet6Address getByAddress(String, byte[], java.net.NetworkInterface) throws java.net.UnknownHostException;
+    method public static java.net.Inet6Address getByAddress(String, byte[], int) throws java.net.UnknownHostException;
+    method public int getScopeId();
+    method public java.net.NetworkInterface getScopedInterface();
+    method public boolean isIPv4CompatibleAddress();
+  }
+
+  public class InetAddress implements java.io.Serializable {
+    method public byte[] getAddress();
+    method public static java.net.InetAddress[] getAllByName(@Nullable String) throws java.net.UnknownHostException;
+    method @NonNull public static java.net.InetAddress getByAddress(@Nullable String, byte[]) throws java.net.UnknownHostException;
+    method @NonNull public static java.net.InetAddress getByAddress(byte[]) throws java.net.UnknownHostException;
+    method @NonNull public static java.net.InetAddress getByName(@Nullable String) throws java.net.UnknownHostException;
+    method @NonNull public String getCanonicalHostName();
+    method @Nullable public String getHostAddress();
+    method @NonNull public String getHostName();
+    method @NonNull public static java.net.InetAddress getLocalHost() throws java.net.UnknownHostException;
+    method @NonNull public static java.net.InetAddress getLoopbackAddress();
+    method public boolean isAnyLocalAddress();
+    method public boolean isLinkLocalAddress();
+    method public boolean isLoopbackAddress();
+    method public boolean isMCGlobal();
+    method public boolean isMCLinkLocal();
+    method public boolean isMCNodeLocal();
+    method public boolean isMCOrgLocal();
+    method public boolean isMCSiteLocal();
+    method public boolean isMulticastAddress();
+    method public boolean isReachable(int) throws java.io.IOException;
+    method public boolean isReachable(@Nullable java.net.NetworkInterface, int, int) throws java.io.IOException;
+    method public boolean isSiteLocalAddress();
+  }
+
+  public class InetSocketAddress extends java.net.SocketAddress {
+    ctor public InetSocketAddress(int);
+    ctor public InetSocketAddress(java.net.InetAddress, int);
+    ctor public InetSocketAddress(String, int);
+    method public static java.net.InetSocketAddress createUnresolved(String, int);
+    method public final boolean equals(Object);
+    method public final java.net.InetAddress getAddress();
+    method public final String getHostName();
+    method public final String getHostString();
+    method public final int getPort();
+    method public final int hashCode();
+    method public final boolean isUnresolved();
+  }
+
+  public class InterfaceAddress {
+    method public java.net.InetAddress getAddress();
+    method public java.net.InetAddress getBroadcast();
+    method public short getNetworkPrefixLength();
+  }
+
+  public abstract class JarURLConnection extends java.net.URLConnection {
+    ctor protected JarURLConnection(java.net.URL) throws java.net.MalformedURLException;
+    method public java.util.jar.Attributes getAttributes() throws java.io.IOException;
+    method public java.security.cert.Certificate[] getCertificates() throws java.io.IOException;
+    method public String getEntryName();
+    method public java.util.jar.JarEntry getJarEntry() throws java.io.IOException;
+    method public abstract java.util.jar.JarFile getJarFile() throws java.io.IOException;
+    method public java.net.URL getJarFileURL();
+    method public java.util.jar.Attributes getMainAttributes() throws java.io.IOException;
+    method public java.util.jar.Manifest getManifest() throws java.io.IOException;
+    field protected java.net.URLConnection jarFileURLConnection;
+  }
+
+  public class MalformedURLException extends java.io.IOException {
+    ctor public MalformedURLException();
+    ctor public MalformedURLException(String);
+  }
+
+  public class MulticastSocket extends java.net.DatagramSocket {
+    ctor public MulticastSocket() throws java.io.IOException;
+    ctor public MulticastSocket(int) throws java.io.IOException;
+    ctor public MulticastSocket(java.net.SocketAddress) throws java.io.IOException;
+    method public java.net.InetAddress getInterface() throws java.net.SocketException;
+    method public boolean getLoopbackMode() throws java.net.SocketException;
+    method public java.net.NetworkInterface getNetworkInterface() throws java.net.SocketException;
+    method @Deprecated public byte getTTL() throws java.io.IOException;
+    method public int getTimeToLive() throws java.io.IOException;
+    method public void joinGroup(java.net.InetAddress) throws java.io.IOException;
+    method public void joinGroup(java.net.SocketAddress, java.net.NetworkInterface) throws java.io.IOException;
+    method public void leaveGroup(java.net.InetAddress) throws java.io.IOException;
+    method public void leaveGroup(java.net.SocketAddress, java.net.NetworkInterface) throws java.io.IOException;
+    method @Deprecated public void send(java.net.DatagramPacket, byte) throws java.io.IOException;
+    method public void setInterface(java.net.InetAddress) throws java.net.SocketException;
+    method public void setLoopbackMode(boolean) throws java.net.SocketException;
+    method public void setNetworkInterface(java.net.NetworkInterface) throws java.net.SocketException;
+    method @Deprecated public void setTTL(byte) throws java.io.IOException;
+    method public void setTimeToLive(int) throws java.io.IOException;
+  }
+
+  public final class NetPermission extends java.security.BasicPermission {
+    ctor public NetPermission(String);
+    ctor public NetPermission(String, String);
+  }
+
+  public final class NetworkInterface {
+    method public static java.net.NetworkInterface getByIndex(int) throws java.net.SocketException;
+    method public static java.net.NetworkInterface getByInetAddress(java.net.InetAddress) throws java.net.SocketException;
+    method public static java.net.NetworkInterface getByName(String) throws java.net.SocketException;
+    method public String getDisplayName();
+    method public byte[] getHardwareAddress() throws java.net.SocketException;
+    method public int getIndex();
+    method public java.util.Enumeration<java.net.InetAddress> getInetAddresses();
+    method public java.util.List<java.net.InterfaceAddress> getInterfaceAddresses();
+    method public int getMTU() throws java.net.SocketException;
+    method public String getName();
+    method public static java.util.Enumeration<java.net.NetworkInterface> getNetworkInterfaces() throws java.net.SocketException;
+    method public java.net.NetworkInterface getParent();
+    method public java.util.Enumeration<java.net.NetworkInterface> getSubInterfaces();
+    method public boolean isLoopback() throws java.net.SocketException;
+    method public boolean isPointToPoint() throws java.net.SocketException;
+    method public boolean isUp() throws java.net.SocketException;
+    method public boolean isVirtual();
+    method public boolean supportsMulticast() throws java.net.SocketException;
+  }
+
+  public class NoRouteToHostException extends java.net.SocketException {
+    ctor public NoRouteToHostException(String);
+    ctor public NoRouteToHostException();
+  }
+
+  public final class PasswordAuthentication {
+    ctor public PasswordAuthentication(String, char[]);
+    method public char[] getPassword();
+    method public String getUserName();
+  }
+
+  public class PortUnreachableException extends java.net.SocketException {
+    ctor public PortUnreachableException(String);
+    ctor public PortUnreachableException();
+  }
+
+  public class ProtocolException extends java.io.IOException {
+    ctor public ProtocolException(String);
+    ctor public ProtocolException();
+  }
+
+  public interface ProtocolFamily {
+    method public String name();
+  }
+
+  public class Proxy {
+    ctor public Proxy(java.net.Proxy.Type, java.net.SocketAddress);
+    method public java.net.SocketAddress address();
+    method public final boolean equals(Object);
+    method public final int hashCode();
+    method public java.net.Proxy.Type type();
+    field public static final java.net.Proxy NO_PROXY;
+  }
+
+  public enum Proxy.Type {
+    enum_constant public static final java.net.Proxy.Type DIRECT;
+    enum_constant public static final java.net.Proxy.Type HTTP;
+    enum_constant public static final java.net.Proxy.Type SOCKS;
+  }
+
+  public abstract class ProxySelector {
+    ctor public ProxySelector();
+    method public abstract void connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException);
+    method public static java.net.ProxySelector getDefault();
+    method public abstract java.util.List<java.net.Proxy> select(java.net.URI);
+    method public static void setDefault(java.net.ProxySelector);
+  }
+
+  public abstract class ResponseCache {
+    ctor public ResponseCache();
+    method public abstract java.net.CacheResponse get(java.net.URI, String, java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method public static java.net.ResponseCache getDefault();
+    method public abstract java.net.CacheRequest put(java.net.URI, java.net.URLConnection) throws java.io.IOException;
+    method public static void setDefault(java.net.ResponseCache);
+  }
+
+  public abstract class SecureCacheResponse extends java.net.CacheResponse {
+    ctor public SecureCacheResponse();
+    method public abstract String getCipherSuite();
+    method public abstract java.util.List<java.security.cert.Certificate> getLocalCertificateChain();
+    method public abstract java.security.Principal getLocalPrincipal();
+    method public abstract java.security.Principal getPeerPrincipal() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public abstract java.util.List<java.security.cert.Certificate> getServerCertificateChain() throws javax.net.ssl.SSLPeerUnverifiedException;
+  }
+
+  public class ServerSocket implements java.io.Closeable {
+    ctor public ServerSocket() throws java.io.IOException;
+    ctor public ServerSocket(int) throws java.io.IOException;
+    ctor public ServerSocket(int, int) throws java.io.IOException;
+    ctor public ServerSocket(int, int, java.net.InetAddress) throws java.io.IOException;
+    method public java.net.Socket accept() throws java.io.IOException;
+    method public void bind(java.net.SocketAddress) throws java.io.IOException;
+    method public void bind(java.net.SocketAddress, int) throws java.io.IOException;
+    method public void close() throws java.io.IOException;
+    method public java.nio.channels.ServerSocketChannel getChannel();
+    method public java.net.InetAddress getInetAddress();
+    method public int getLocalPort();
+    method public java.net.SocketAddress getLocalSocketAddress();
+    method public int getReceiveBufferSize() throws java.net.SocketException;
+    method public boolean getReuseAddress() throws java.net.SocketException;
+    method public int getSoTimeout() throws java.io.IOException;
+    method protected final void implAccept(java.net.Socket) throws java.io.IOException;
+    method public boolean isBound();
+    method public boolean isClosed();
+    method public void setPerformancePreferences(int, int, int);
+    method public void setReceiveBufferSize(int) throws java.net.SocketException;
+    method public void setReuseAddress(boolean) throws java.net.SocketException;
+    method public void setSoTimeout(int) throws java.net.SocketException;
+    method public static void setSocketFactory(java.net.SocketImplFactory) throws java.io.IOException;
+  }
+
+  public class Socket implements java.io.Closeable {
+    ctor public Socket();
+    ctor public Socket(java.net.Proxy);
+    ctor protected Socket(java.net.SocketImpl) throws java.net.SocketException;
+    ctor public Socket(String, int) throws java.io.IOException, java.net.UnknownHostException;
+    ctor public Socket(java.net.InetAddress, int) throws java.io.IOException;
+    ctor public Socket(String, int, java.net.InetAddress, int) throws java.io.IOException;
+    ctor public Socket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
+    ctor @Deprecated public Socket(String, int, boolean) throws java.io.IOException;
+    ctor @Deprecated public Socket(java.net.InetAddress, int, boolean) throws java.io.IOException;
+    method public void bind(java.net.SocketAddress) throws java.io.IOException;
+    method public void close() throws java.io.IOException;
+    method public void connect(java.net.SocketAddress) throws java.io.IOException;
+    method public void connect(java.net.SocketAddress, int) throws java.io.IOException;
+    method public java.nio.channels.SocketChannel getChannel();
+    method public java.net.InetAddress getInetAddress();
+    method public java.io.InputStream getInputStream() throws java.io.IOException;
+    method public boolean getKeepAlive() throws java.net.SocketException;
+    method public java.net.InetAddress getLocalAddress();
+    method public int getLocalPort();
+    method public java.net.SocketAddress getLocalSocketAddress();
+    method public boolean getOOBInline() throws java.net.SocketException;
+    method public java.io.OutputStream getOutputStream() throws java.io.IOException;
+    method public int getPort();
+    method public int getReceiveBufferSize() throws java.net.SocketException;
+    method public java.net.SocketAddress getRemoteSocketAddress();
+    method public boolean getReuseAddress() throws java.net.SocketException;
+    method public int getSendBufferSize() throws java.net.SocketException;
+    method public int getSoLinger() throws java.net.SocketException;
+    method public int getSoTimeout() throws java.net.SocketException;
+    method public boolean getTcpNoDelay() throws java.net.SocketException;
+    method public int getTrafficClass() throws java.net.SocketException;
+    method public boolean isBound();
+    method public boolean isClosed();
+    method public boolean isConnected();
+    method public boolean isInputShutdown();
+    method public boolean isOutputShutdown();
+    method public void sendUrgentData(int) throws java.io.IOException;
+    method public void setKeepAlive(boolean) throws java.net.SocketException;
+    method public void setOOBInline(boolean) throws java.net.SocketException;
+    method public void setPerformancePreferences(int, int, int);
+    method public void setReceiveBufferSize(int) throws java.net.SocketException;
+    method public void setReuseAddress(boolean) throws java.net.SocketException;
+    method public void setSendBufferSize(int) throws java.net.SocketException;
+    method public void setSoLinger(boolean, int) throws java.net.SocketException;
+    method public void setSoTimeout(int) throws java.net.SocketException;
+    method public static void setSocketImplFactory(java.net.SocketImplFactory) throws java.io.IOException;
+    method public void setTcpNoDelay(boolean) throws java.net.SocketException;
+    method public void setTrafficClass(int) throws java.net.SocketException;
+    method public void shutdownInput() throws java.io.IOException;
+    method public void shutdownOutput() throws java.io.IOException;
+  }
+
+  public abstract class SocketAddress implements java.io.Serializable {
+    ctor public SocketAddress();
+  }
+
+  public class SocketException extends java.io.IOException {
+    ctor public SocketException(String);
+    ctor public SocketException();
+  }
+
+  public abstract class SocketImpl implements java.net.SocketOptions {
+    ctor public SocketImpl();
+    method protected abstract void accept(java.net.SocketImpl) throws java.io.IOException;
+    method protected abstract int available() throws java.io.IOException;
+    method protected abstract void bind(java.net.InetAddress, int) throws java.io.IOException;
+    method protected abstract void close() throws java.io.IOException;
+    method protected abstract void connect(String, int) throws java.io.IOException;
+    method protected abstract void connect(java.net.InetAddress, int) throws java.io.IOException;
+    method protected abstract void connect(java.net.SocketAddress, int) throws java.io.IOException;
+    method protected abstract void create(boolean) throws java.io.IOException;
+    method protected java.io.FileDescriptor getFileDescriptor();
+    method protected java.net.InetAddress getInetAddress();
+    method protected abstract java.io.InputStream getInputStream() throws java.io.IOException;
+    method protected int getLocalPort();
+    method protected abstract java.io.OutputStream getOutputStream() throws java.io.IOException;
+    method protected int getPort();
+    method protected abstract void listen(int) throws java.io.IOException;
+    method protected abstract void sendUrgentData(int) throws java.io.IOException;
+    method protected void setPerformancePreferences(int, int, int);
+    method protected void shutdownInput() throws java.io.IOException;
+    method protected void shutdownOutput() throws java.io.IOException;
+    method protected boolean supportsUrgentData();
+    field protected java.net.InetAddress address;
+    field protected java.io.FileDescriptor fd;
+    field protected int localport;
+    field protected int port;
+  }
+
+  public interface SocketImplFactory {
+    method public java.net.SocketImpl createSocketImpl();
+  }
+
+  public interface SocketOption<T> {
+    method public String name();
+    method public Class<T> type();
+  }
+
+  public interface SocketOptions {
+    method public Object getOption(int) throws java.net.SocketException;
+    method public void setOption(int, Object) throws java.net.SocketException;
+    field public static final int IP_MULTICAST_IF = 16; // 0x10
+    field public static final int IP_MULTICAST_IF2 = 31; // 0x1f
+    field public static final int IP_MULTICAST_LOOP = 18; // 0x12
+    field public static final int IP_TOS = 3; // 0x3
+    field public static final int SO_BINDADDR = 15; // 0xf
+    field public static final int SO_BROADCAST = 32; // 0x20
+    field public static final int SO_KEEPALIVE = 8; // 0x8
+    field public static final int SO_LINGER = 128; // 0x80
+    field public static final int SO_OOBINLINE = 4099; // 0x1003
+    field public static final int SO_RCVBUF = 4098; // 0x1002
+    field public static final int SO_REUSEADDR = 4; // 0x4
+    field public static final int SO_SNDBUF = 4097; // 0x1001
+    field public static final int SO_TIMEOUT = 4102; // 0x1006
+    field public static final int TCP_NODELAY = 1; // 0x1
+  }
+
+  public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
+    ctor public SocketPermission(String, String);
+    method public String getActions();
+    method public boolean implies(java.security.Permission);
+  }
+
+  public class SocketTimeoutException extends java.io.InterruptedIOException {
+    ctor public SocketTimeoutException(String);
+    ctor public SocketTimeoutException();
+  }
+
+  public enum StandardProtocolFamily implements java.net.ProtocolFamily {
+    enum_constant public static final java.net.StandardProtocolFamily INET;
+    enum_constant public static final java.net.StandardProtocolFamily INET6;
+  }
+
+  public final class StandardSocketOptions {
+    field public static final java.net.SocketOption<java.net.NetworkInterface> IP_MULTICAST_IF;
+    field public static final java.net.SocketOption<java.lang.Boolean> IP_MULTICAST_LOOP;
+    field public static final java.net.SocketOption<java.lang.Integer> IP_MULTICAST_TTL;
+    field public static final java.net.SocketOption<java.lang.Integer> IP_TOS;
+    field public static final java.net.SocketOption<java.lang.Boolean> SO_BROADCAST;
+    field public static final java.net.SocketOption<java.lang.Boolean> SO_KEEPALIVE;
+    field public static final java.net.SocketOption<java.lang.Integer> SO_LINGER;
+    field public static final java.net.SocketOption<java.lang.Integer> SO_RCVBUF;
+    field public static final java.net.SocketOption<java.lang.Boolean> SO_REUSEADDR;
+    field public static final java.net.SocketOption<java.lang.Integer> SO_SNDBUF;
+    field public static final java.net.SocketOption<java.lang.Boolean> TCP_NODELAY;
+  }
+
+  public final class URI implements java.lang.Comparable<java.net.URI> java.io.Serializable {
+    ctor public URI(String) throws java.net.URISyntaxException;
+    ctor public URI(String, String, String, int, String, String, String) throws java.net.URISyntaxException;
+    ctor public URI(String, String, String, String, String) throws java.net.URISyntaxException;
+    ctor public URI(String, String, String, String) throws java.net.URISyntaxException;
+    ctor public URI(String, String, String) throws java.net.URISyntaxException;
+    method public int compareTo(java.net.URI);
+    method public static java.net.URI create(String);
+    method public String getAuthority();
+    method public String getFragment();
+    method public String getHost();
+    method public String getPath();
+    method public int getPort();
+    method public String getQuery();
+    method public String getRawAuthority();
+    method public String getRawFragment();
+    method public String getRawPath();
+    method public String getRawQuery();
+    method public String getRawSchemeSpecificPart();
+    method public String getRawUserInfo();
+    method public String getScheme();
+    method public String getSchemeSpecificPart();
+    method public String getUserInfo();
+    method public boolean isAbsolute();
+    method public boolean isOpaque();
+    method public java.net.URI normalize();
+    method public java.net.URI parseServerAuthority() throws java.net.URISyntaxException;
+    method public java.net.URI relativize(java.net.URI);
+    method public java.net.URI resolve(java.net.URI);
+    method public java.net.URI resolve(String);
+    method public String toASCIIString();
+    method public java.net.URL toURL() throws java.net.MalformedURLException;
+  }
+
+  public class URISyntaxException extends java.lang.Exception {
+    ctor public URISyntaxException(String, String, int);
+    ctor public URISyntaxException(String, String);
+    method public int getIndex();
+    method public String getInput();
+    method public String getReason();
+  }
+
+  public final class URL implements java.io.Serializable {
+    ctor public URL(String, String, int, String) throws java.net.MalformedURLException;
+    ctor public URL(String, String, String) throws java.net.MalformedURLException;
+    ctor public URL(String, String, int, String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
+    ctor public URL(String) throws java.net.MalformedURLException;
+    ctor public URL(java.net.URL, String) throws java.net.MalformedURLException;
+    ctor public URL(java.net.URL, String, java.net.URLStreamHandler) throws java.net.MalformedURLException;
+    method public String getAuthority();
+    method public Object getContent() throws java.io.IOException;
+    method public Object getContent(Class[]) throws java.io.IOException;
+    method public int getDefaultPort();
+    method public String getFile();
+    method public String getHost();
+    method public String getPath();
+    method public int getPort();
+    method public String getProtocol();
+    method public String getQuery();
+    method public String getRef();
+    method public String getUserInfo();
+    method public java.net.URLConnection openConnection() throws java.io.IOException;
+    method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
+    method public java.io.InputStream openStream() throws java.io.IOException;
+    method public boolean sameFile(java.net.URL);
+    method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
+    method public String toExternalForm();
+    method public java.net.URI toURI() throws java.net.URISyntaxException;
+  }
+
+  public class URLClassLoader extends java.security.SecureClassLoader implements java.io.Closeable {
+    ctor public URLClassLoader(java.net.URL[], ClassLoader);
+    ctor public URLClassLoader(java.net.URL[]);
+    ctor public URLClassLoader(java.net.URL[], ClassLoader, java.net.URLStreamHandlerFactory);
+    method protected void addURL(java.net.URL);
+    method public void close() throws java.io.IOException;
+    method protected Package definePackage(String, java.util.jar.Manifest, java.net.URL) throws java.lang.IllegalArgumentException;
+    method public java.net.URL findResource(String);
+    method public java.util.Enumeration<java.net.URL> findResources(String) throws java.io.IOException;
+    method public java.net.URL[] getURLs();
+    method public static java.net.URLClassLoader newInstance(java.net.URL[], ClassLoader);
+    method public static java.net.URLClassLoader newInstance(java.net.URL[]);
+  }
+
+  public abstract class URLConnection {
+    ctor protected URLConnection(java.net.URL);
+    method public void addRequestProperty(String, String);
+    method public abstract void connect() throws java.io.IOException;
+    method public boolean getAllowUserInteraction();
+    method public int getConnectTimeout();
+    method public Object getContent() throws java.io.IOException;
+    method public Object getContent(Class[]) throws java.io.IOException;
+    method public String getContentEncoding();
+    method public int getContentLength();
+    method public long getContentLengthLong();
+    method public String getContentType();
+    method public long getDate();
+    method public static boolean getDefaultAllowUserInteraction();
+    method @Deprecated public static String getDefaultRequestProperty(String);
+    method public boolean getDefaultUseCaches();
+    method public boolean getDoInput();
+    method public boolean getDoOutput();
+    method public long getExpiration();
+    method public static java.net.FileNameMap getFileNameMap();
+    method public String getHeaderField(String);
+    method public String getHeaderField(int);
+    method public long getHeaderFieldDate(String, long);
+    method public int getHeaderFieldInt(String, int);
+    method public String getHeaderFieldKey(int);
+    method public long getHeaderFieldLong(String, long);
+    method public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getHeaderFields();
+    method public long getIfModifiedSince();
+    method public java.io.InputStream getInputStream() throws java.io.IOException;
+    method public long getLastModified();
+    method public java.io.OutputStream getOutputStream() throws java.io.IOException;
+    method public java.security.Permission getPermission() throws java.io.IOException;
+    method public int getReadTimeout();
+    method public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getRequestProperties();
+    method public String getRequestProperty(String);
+    method public java.net.URL getURL();
+    method public boolean getUseCaches();
+    method public static String guessContentTypeFromName(String);
+    method public static String guessContentTypeFromStream(java.io.InputStream) throws java.io.IOException;
+    method public void setAllowUserInteraction(boolean);
+    method public void setConnectTimeout(int);
+    method public static void setContentHandlerFactory(java.net.ContentHandlerFactory);
+    method public static void setDefaultAllowUserInteraction(boolean);
+    method @Deprecated public static void setDefaultRequestProperty(String, String);
+    method public void setDefaultUseCaches(boolean);
+    method public void setDoInput(boolean);
+    method public void setDoOutput(boolean);
+    method public static void setFileNameMap(java.net.FileNameMap);
+    method public void setIfModifiedSince(long);
+    method public void setReadTimeout(int);
+    method public void setRequestProperty(String, String);
+    method public void setUseCaches(boolean);
+    field protected boolean allowUserInteraction;
+    field protected boolean connected;
+    field protected boolean doInput;
+    field protected boolean doOutput;
+    field protected long ifModifiedSince;
+    field protected java.net.URL url;
+    field protected boolean useCaches;
+  }
+
+  public class URLDecoder {
+    ctor public URLDecoder();
+    method @Deprecated public static String decode(String);
+    method public static String decode(String, String) throws java.io.UnsupportedEncodingException;
+  }
+
+  public class URLEncoder {
+    method @Deprecated public static String encode(String);
+    method public static String encode(String, String) throws java.io.UnsupportedEncodingException;
+  }
+
+  public abstract class URLStreamHandler {
+    ctor public URLStreamHandler();
+    method protected boolean equals(java.net.URL, java.net.URL);
+    method protected int getDefaultPort();
+    method protected java.net.InetAddress getHostAddress(java.net.URL);
+    method protected int hashCode(java.net.URL);
+    method protected boolean hostsEqual(java.net.URL, java.net.URL);
+    method protected abstract java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
+    method protected java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
+    method protected void parseURL(java.net.URL, String, int, int);
+    method protected boolean sameFile(java.net.URL, java.net.URL);
+    method protected void setURL(java.net.URL, String, String, int, String, String, String, String, String);
+    method @Deprecated protected void setURL(java.net.URL, String, String, int, String, String);
+    method protected String toExternalForm(java.net.URL);
+  }
+
+  public interface URLStreamHandlerFactory {
+    method public java.net.URLStreamHandler createURLStreamHandler(String);
+  }
+
+  public class UnknownHostException extends java.io.IOException {
+    ctor public UnknownHostException(String);
+    ctor public UnknownHostException();
+  }
+
+  public class UnknownServiceException extends java.io.IOException {
+    ctor public UnknownServiceException();
+    ctor public UnknownServiceException(String);
+  }
+
+}
+
+package java.nio {
+
+  public abstract class Buffer {
+    method public abstract Object array();
+    method public abstract int arrayOffset();
+    method public final int capacity();
+    method public java.nio.Buffer clear();
+    method public java.nio.Buffer flip();
+    method public abstract boolean hasArray();
+    method public final boolean hasRemaining();
+    method public abstract boolean isDirect();
+    method public abstract boolean isReadOnly();
+    method public final int limit();
+    method public java.nio.Buffer limit(int);
+    method public java.nio.Buffer mark();
+    method public final int position();
+    method public java.nio.Buffer position(int);
+    method public final int remaining();
+    method public java.nio.Buffer reset();
+    method public java.nio.Buffer rewind();
+  }
+
+  public class BufferOverflowException extends java.lang.RuntimeException {
+    ctor public BufferOverflowException();
+  }
+
+  public class BufferUnderflowException extends java.lang.RuntimeException {
+    ctor public BufferUnderflowException();
+  }
+
+  public abstract class ByteBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.ByteBuffer> {
+    method @NonNull public static java.nio.ByteBuffer allocate(int);
+    method @NonNull public static java.nio.ByteBuffer allocateDirect(int);
+    method @NonNull public final byte[] array();
+    method public final int arrayOffset();
+    method @NonNull public abstract java.nio.CharBuffer asCharBuffer();
+    method @NonNull public abstract java.nio.DoubleBuffer asDoubleBuffer();
+    method @NonNull public abstract java.nio.FloatBuffer asFloatBuffer();
+    method @NonNull public abstract java.nio.IntBuffer asIntBuffer();
+    method @NonNull public abstract java.nio.LongBuffer asLongBuffer();
+    method @NonNull public abstract java.nio.ByteBuffer asReadOnlyBuffer();
+    method @NonNull public abstract java.nio.ShortBuffer asShortBuffer();
+    method @NonNull public abstract java.nio.ByteBuffer compact();
+    method public int compareTo(@NonNull java.nio.ByteBuffer);
+    method @NonNull public abstract java.nio.ByteBuffer duplicate();
+    method public abstract byte get();
+    method public abstract byte get(int);
+    method @NonNull public java.nio.ByteBuffer get(@NonNull byte[], int, int);
+    method @NonNull public java.nio.ByteBuffer get(@NonNull byte[]);
+    method public abstract char getChar();
+    method public abstract char getChar(int);
+    method public abstract double getDouble();
+    method public abstract double getDouble(int);
+    method public abstract float getFloat();
+    method public abstract float getFloat(int);
+    method public abstract int getInt();
+    method public abstract int getInt(int);
+    method public abstract long getLong();
+    method public abstract long getLong(int);
+    method public abstract short getShort();
+    method public abstract short getShort(int);
+    method public final boolean hasArray();
+    method @NonNull public final java.nio.ByteOrder order();
+    method @NonNull public final java.nio.ByteBuffer order(@NonNull java.nio.ByteOrder);
+    method @NonNull public abstract java.nio.ByteBuffer put(byte);
+    method @NonNull public abstract java.nio.ByteBuffer put(int, byte);
+    method @NonNull public java.nio.ByteBuffer put(@NonNull java.nio.ByteBuffer);
+    method @NonNull public java.nio.ByteBuffer put(@NonNull byte[], int, int);
+    method @NonNull public final java.nio.ByteBuffer put(@NonNull byte[]);
+    method @NonNull public abstract java.nio.ByteBuffer putChar(char);
+    method @NonNull public abstract java.nio.ByteBuffer putChar(int, char);
+    method @NonNull public abstract java.nio.ByteBuffer putDouble(double);
+    method @NonNull public abstract java.nio.ByteBuffer putDouble(int, double);
+    method @NonNull public abstract java.nio.ByteBuffer putFloat(float);
+    method @NonNull public abstract java.nio.ByteBuffer putFloat(int, float);
+    method @NonNull public abstract java.nio.ByteBuffer putInt(int);
+    method @NonNull public abstract java.nio.ByteBuffer putInt(int, int);
+    method @NonNull public abstract java.nio.ByteBuffer putLong(long);
+    method @NonNull public abstract java.nio.ByteBuffer putLong(int, long);
+    method @NonNull public abstract java.nio.ByteBuffer putShort(short);
+    method @NonNull public abstract java.nio.ByteBuffer putShort(int, short);
+    method @NonNull public abstract java.nio.ByteBuffer slice();
+    method @NonNull public static java.nio.ByteBuffer wrap(@NonNull byte[], int, int);
+    method @NonNull public static java.nio.ByteBuffer wrap(@NonNull byte[]);
+  }
+
+  public final class ByteOrder {
+    method public static java.nio.ByteOrder nativeOrder();
+    field public static final java.nio.ByteOrder BIG_ENDIAN;
+    field public static final java.nio.ByteOrder LITTLE_ENDIAN;
+  }
+
+  public abstract class CharBuffer extends java.nio.Buffer implements java.lang.Appendable java.lang.CharSequence java.lang.Comparable<java.nio.CharBuffer> java.lang.Readable {
+    method public static java.nio.CharBuffer allocate(int);
+    method public java.nio.CharBuffer append(CharSequence);
+    method public java.nio.CharBuffer append(CharSequence, int, int);
+    method public java.nio.CharBuffer append(char);
+    method public final char[] array();
+    method public final int arrayOffset();
+    method public abstract java.nio.CharBuffer asReadOnlyBuffer();
+    method public final char charAt(int);
+    method public abstract java.nio.CharBuffer compact();
+    method public int compareTo(java.nio.CharBuffer);
+    method public abstract java.nio.CharBuffer duplicate();
+    method public abstract char get();
+    method public abstract char get(int);
+    method public java.nio.CharBuffer get(char[], int, int);
+    method public java.nio.CharBuffer get(char[]);
+    method public final boolean hasArray();
+    method public final int length();
+    method public abstract java.nio.ByteOrder order();
+    method public abstract java.nio.CharBuffer put(char);
+    method public abstract java.nio.CharBuffer put(int, char);
+    method public java.nio.CharBuffer put(java.nio.CharBuffer);
+    method public java.nio.CharBuffer put(char[], int, int);
+    method public final java.nio.CharBuffer put(char[]);
+    method public java.nio.CharBuffer put(String, int, int);
+    method public final java.nio.CharBuffer put(String);
+    method public int read(java.nio.CharBuffer) throws java.io.IOException;
+    method public abstract java.nio.CharBuffer slice();
+    method public abstract java.nio.CharBuffer subSequence(int, int);
+    method public static java.nio.CharBuffer wrap(char[], int, int);
+    method public static java.nio.CharBuffer wrap(char[]);
+    method public static java.nio.CharBuffer wrap(CharSequence, int, int);
+    method public static java.nio.CharBuffer wrap(CharSequence);
+  }
+
+  public abstract class DoubleBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.DoubleBuffer> {
+    method public static java.nio.DoubleBuffer allocate(int);
+    method public final double[] array();
+    method public final int arrayOffset();
+    method public abstract java.nio.DoubleBuffer asReadOnlyBuffer();
+    method public abstract java.nio.DoubleBuffer compact();
+    method public int compareTo(java.nio.DoubleBuffer);
+    method public abstract java.nio.DoubleBuffer duplicate();
+    method public abstract double get();
+    method public abstract double get(int);
+    method public java.nio.DoubleBuffer get(double[], int, int);
+    method public java.nio.DoubleBuffer get(double[]);
+    method public final boolean hasArray();
+    method public abstract java.nio.ByteOrder order();
+    method public abstract java.nio.DoubleBuffer put(double);
+    method public abstract java.nio.DoubleBuffer put(int, double);
+    method public java.nio.DoubleBuffer put(java.nio.DoubleBuffer);
+    method public java.nio.DoubleBuffer put(double[], int, int);
+    method public final java.nio.DoubleBuffer put(double[]);
+    method public abstract java.nio.DoubleBuffer slice();
+    method public static java.nio.DoubleBuffer wrap(double[], int, int);
+    method public static java.nio.DoubleBuffer wrap(double[]);
+  }
+
+  public abstract class FloatBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.FloatBuffer> {
+    method public static java.nio.FloatBuffer allocate(int);
+    method public final float[] array();
+    method public final int arrayOffset();
+    method public abstract java.nio.FloatBuffer asReadOnlyBuffer();
+    method public abstract java.nio.FloatBuffer compact();
+    method public int compareTo(java.nio.FloatBuffer);
+    method public abstract java.nio.FloatBuffer duplicate();
+    method public abstract float get();
+    method public abstract float get(int);
+    method public java.nio.FloatBuffer get(float[], int, int);
+    method public java.nio.FloatBuffer get(float[]);
+    method public final boolean hasArray();
+    method public abstract java.nio.ByteOrder order();
+    method public abstract java.nio.FloatBuffer put(float);
+    method public abstract java.nio.FloatBuffer put(int, float);
+    method public java.nio.FloatBuffer put(java.nio.FloatBuffer);
+    method public java.nio.FloatBuffer put(float[], int, int);
+    method public final java.nio.FloatBuffer put(float[]);
+    method public abstract java.nio.FloatBuffer slice();
+    method public static java.nio.FloatBuffer wrap(float[], int, int);
+    method public static java.nio.FloatBuffer wrap(float[]);
+  }
+
+  public abstract class IntBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.IntBuffer> {
+    method public static java.nio.IntBuffer allocate(int);
+    method public final int[] array();
+    method public final int arrayOffset();
+    method public abstract java.nio.IntBuffer asReadOnlyBuffer();
+    method public abstract java.nio.IntBuffer compact();
+    method public int compareTo(java.nio.IntBuffer);
+    method public abstract java.nio.IntBuffer duplicate();
+    method public abstract int get();
+    method public abstract int get(int);
+    method public java.nio.IntBuffer get(int[], int, int);
+    method public java.nio.IntBuffer get(int[]);
+    method public final boolean hasArray();
+    method public abstract java.nio.ByteOrder order();
+    method public abstract java.nio.IntBuffer put(int);
+    method public abstract java.nio.IntBuffer put(int, int);
+    method public java.nio.IntBuffer put(java.nio.IntBuffer);
+    method public java.nio.IntBuffer put(int[], int, int);
+    method public final java.nio.IntBuffer put(int[]);
+    method public abstract java.nio.IntBuffer slice();
+    method public static java.nio.IntBuffer wrap(int[], int, int);
+    method public static java.nio.IntBuffer wrap(int[]);
+  }
+
+  public class InvalidMarkException extends java.lang.IllegalStateException {
+    ctor public InvalidMarkException();
+  }
+
+  public abstract class LongBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.LongBuffer> {
+    method public static java.nio.LongBuffer allocate(int);
+    method public final long[] array();
+    method public final int arrayOffset();
+    method public abstract java.nio.LongBuffer asReadOnlyBuffer();
+    method public abstract java.nio.LongBuffer compact();
+    method public int compareTo(java.nio.LongBuffer);
+    method public abstract java.nio.LongBuffer duplicate();
+    method public abstract long get();
+    method public abstract long get(int);
+    method public java.nio.LongBuffer get(long[], int, int);
+    method public java.nio.LongBuffer get(long[]);
+    method public final boolean hasArray();
+    method public abstract java.nio.ByteOrder order();
+    method public abstract java.nio.LongBuffer put(long);
+    method public abstract java.nio.LongBuffer put(int, long);
+    method public java.nio.LongBuffer put(java.nio.LongBuffer);
+    method public java.nio.LongBuffer put(long[], int, int);
+    method public final java.nio.LongBuffer put(long[]);
+    method public abstract java.nio.LongBuffer slice();
+    method public static java.nio.LongBuffer wrap(long[], int, int);
+    method public static java.nio.LongBuffer wrap(long[]);
+  }
+
+  public abstract class MappedByteBuffer extends java.nio.ByteBuffer {
+    method public final java.nio.MappedByteBuffer force();
+    method public final boolean isLoaded();
+    method public final java.nio.MappedByteBuffer load();
+  }
+
+  public class ReadOnlyBufferException extends java.lang.UnsupportedOperationException {
+    ctor public ReadOnlyBufferException();
+  }
+
+  public abstract class ShortBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.ShortBuffer> {
+    method public static java.nio.ShortBuffer allocate(int);
+    method public final short[] array();
+    method public final int arrayOffset();
+    method public abstract java.nio.ShortBuffer asReadOnlyBuffer();
+    method public abstract java.nio.ShortBuffer compact();
+    method public int compareTo(java.nio.ShortBuffer);
+    method public abstract java.nio.ShortBuffer duplicate();
+    method public abstract short get();
+    method public abstract short get(int);
+    method public java.nio.ShortBuffer get(short[], int, int);
+    method public java.nio.ShortBuffer get(short[]);
+    method public final boolean hasArray();
+    method public abstract java.nio.ByteOrder order();
+    method public abstract java.nio.ShortBuffer put(short);
+    method public abstract java.nio.ShortBuffer put(int, short);
+    method public java.nio.ShortBuffer put(java.nio.ShortBuffer);
+    method public java.nio.ShortBuffer put(short[], int, int);
+    method public final java.nio.ShortBuffer put(short[]);
+    method public abstract java.nio.ShortBuffer slice();
+    method public static java.nio.ShortBuffer wrap(short[], int, int);
+    method public static java.nio.ShortBuffer wrap(short[]);
+  }
+
+}
+
+package java.nio.channels {
+
+  public class AcceptPendingException extends java.lang.IllegalStateException {
+    ctor public AcceptPendingException();
+  }
+
+  public class AlreadyBoundException extends java.lang.IllegalStateException {
+    ctor public AlreadyBoundException();
+  }
+
+  public class AlreadyConnectedException extends java.lang.IllegalStateException {
+    ctor public AlreadyConnectedException();
+  }
+
+  public interface AsynchronousByteChannel extends java.nio.channels.AsynchronousChannel {
+    method public <A> void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer);
+    method public <A> void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer);
+  }
+
+  public interface AsynchronousChannel extends java.nio.channels.Channel {
+  }
+
+  public abstract class AsynchronousChannelGroup {
+    ctor protected AsynchronousChannelGroup(java.nio.channels.spi.AsynchronousChannelProvider);
+    method public abstract boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public abstract boolean isShutdown();
+    method public abstract boolean isTerminated();
+    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+    method public abstract void shutdown();
+    method public abstract void shutdownNow() throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousChannelGroup withCachedThreadPool(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousChannelGroup withFixedThreadPool(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousChannelGroup withThreadPool(java.util.concurrent.ExecutorService) throws java.io.IOException;
+  }
+
+  public class AsynchronousCloseException extends java.nio.channels.ClosedChannelException {
+    ctor public AsynchronousCloseException();
+  }
+
+  public abstract class AsynchronousFileChannel implements java.nio.channels.AsynchronousChannel {
+    ctor protected AsynchronousFileChannel();
+    method public abstract void force(boolean) throws java.io.IOException;
+    method public abstract <A> void lock(long, long, boolean, A, java.nio.channels.CompletionHandler<java.nio.channels.FileLock,? super A>);
+    method public final <A> void lock(A, java.nio.channels.CompletionHandler<java.nio.channels.FileLock,? super A>);
+    method public abstract java.util.concurrent.Future<java.nio.channels.FileLock> lock(long, long, boolean);
+    method public final java.util.concurrent.Future<java.nio.channels.FileLock> lock();
+    method public static java.nio.channels.AsynchronousFileChannel open(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousFileChannel open(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public abstract <A> void read(java.nio.ByteBuffer, long, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public abstract java.util.concurrent.Future<java.lang.Integer> read(java.nio.ByteBuffer, long);
+    method public abstract long size() throws java.io.IOException;
+    method public abstract java.nio.channels.AsynchronousFileChannel truncate(long) throws java.io.IOException;
+    method public abstract java.nio.channels.FileLock tryLock(long, long, boolean) throws java.io.IOException;
+    method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
+    method public abstract <A> void write(java.nio.ByteBuffer, long, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public abstract java.util.concurrent.Future<java.lang.Integer> write(java.nio.ByteBuffer, long);
+  }
+
+  public abstract class AsynchronousServerSocketChannel implements java.nio.channels.AsynchronousChannel java.nio.channels.NetworkChannel {
+    ctor protected AsynchronousServerSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
+    method public abstract <A> void accept(A, java.nio.channels.CompletionHandler<java.nio.channels.AsynchronousSocketChannel,? super A>);
+    method public abstract java.util.concurrent.Future<java.nio.channels.AsynchronousSocketChannel> accept();
+    method public final java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract java.nio.channels.AsynchronousServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousServerSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousServerSocketChannel open() throws java.io.IOException;
+    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+    method public abstract <T> java.nio.channels.AsynchronousServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+  }
+
+  public abstract class AsynchronousSocketChannel implements java.nio.channels.AsynchronousByteChannel java.nio.channels.NetworkChannel {
+    ctor protected AsynchronousSocketChannel(java.nio.channels.spi.AsynchronousChannelProvider);
+    method public abstract java.nio.channels.AsynchronousSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract <A> void connect(java.net.SocketAddress, A, java.nio.channels.CompletionHandler<java.lang.Void,? super A>);
+    method public abstract java.util.concurrent.Future<java.lang.Void> connect(java.net.SocketAddress);
+    method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousSocketChannel open(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+    method public static java.nio.channels.AsynchronousSocketChannel open() throws java.io.IOException;
+    method public final java.nio.channels.spi.AsynchronousChannelProvider provider();
+    method public abstract <A> void read(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public final <A> void read(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public abstract <A> void read(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long,? super A>);
+    method public abstract <T> java.nio.channels.AsynchronousSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownInput() throws java.io.IOException;
+    method public abstract java.nio.channels.AsynchronousSocketChannel shutdownOutput() throws java.io.IOException;
+    method public abstract <A> void write(java.nio.ByteBuffer, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public final <A> void write(java.nio.ByteBuffer, A, java.nio.channels.CompletionHandler<java.lang.Integer,? super A>);
+    method public abstract <A> void write(java.nio.ByteBuffer[], int, int, long, java.util.concurrent.TimeUnit, A, java.nio.channels.CompletionHandler<java.lang.Long,? super A>);
+  }
+
+  public interface ByteChannel extends java.nio.channels.ReadableByteChannel java.nio.channels.WritableByteChannel {
+  }
+
+  public class CancelledKeyException extends java.lang.IllegalStateException {
+    ctor public CancelledKeyException();
+  }
+
+  public interface Channel extends java.io.Closeable {
+    method public boolean isOpen();
+  }
+
+  public final class Channels {
+    method public static java.nio.channels.ReadableByteChannel newChannel(java.io.InputStream);
+    method public static java.nio.channels.WritableByteChannel newChannel(java.io.OutputStream);
+    method public static java.io.InputStream newInputStream(java.nio.channels.ReadableByteChannel);
+    method public static java.io.InputStream newInputStream(java.nio.channels.AsynchronousByteChannel);
+    method public static java.io.OutputStream newOutputStream(java.nio.channels.WritableByteChannel);
+    method public static java.io.OutputStream newOutputStream(java.nio.channels.AsynchronousByteChannel);
+    method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, java.nio.charset.CharsetDecoder, int);
+    method public static java.io.Reader newReader(java.nio.channels.ReadableByteChannel, String);
+    method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, java.nio.charset.CharsetEncoder, int);
+    method public static java.io.Writer newWriter(java.nio.channels.WritableByteChannel, String);
+  }
+
+  public class ClosedByInterruptException extends java.nio.channels.AsynchronousCloseException {
+    ctor public ClosedByInterruptException();
+  }
+
+  public class ClosedChannelException extends java.io.IOException {
+    ctor public ClosedChannelException();
+  }
+
+  public class ClosedSelectorException extends java.lang.IllegalStateException {
+    ctor public ClosedSelectorException();
+  }
+
+  public interface CompletionHandler<V, A> {
+    method public void completed(V, A);
+    method public void failed(Throwable, A);
+  }
+
+  public class ConnectionPendingException extends java.lang.IllegalStateException {
+    ctor public ConnectionPendingException();
+  }
+
+  public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
+    ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
+    method public abstract java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException;
+    method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
+    method public abstract boolean isConnected();
+    method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
+    method public static java.nio.channels.DatagramChannel open(java.net.ProtocolFamily) throws java.io.IOException;
+    method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+    method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException;
+    method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException;
+    method public abstract <T> java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+    method public abstract java.net.DatagramSocket socket();
+    method public final int validOps();
+    method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+  }
+
+  public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel {
+    ctor protected FileChannel();
+    method public abstract void force(boolean) throws java.io.IOException;
+    method public abstract java.nio.channels.FileLock lock(long, long, boolean) throws java.io.IOException;
+    method public final java.nio.channels.FileLock lock() throws java.io.IOException;
+    method public abstract java.nio.MappedByteBuffer map(java.nio.channels.FileChannel.MapMode, long, long) throws java.io.IOException;
+    method public static java.nio.channels.FileChannel open(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.channels.FileChannel open(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public abstract java.nio.channels.FileChannel position(long) throws java.io.IOException;
+    method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+    method public abstract int read(java.nio.ByteBuffer, long) throws java.io.IOException;
+    method public abstract long transferFrom(java.nio.channels.ReadableByteChannel, long, long) throws java.io.IOException;
+    method public abstract long transferTo(long, long, java.nio.channels.WritableByteChannel) throws java.io.IOException;
+    method public abstract java.nio.channels.FileChannel truncate(long) throws java.io.IOException;
+    method public abstract java.nio.channels.FileLock tryLock(long, long, boolean) throws java.io.IOException;
+    method public final java.nio.channels.FileLock tryLock() throws java.io.IOException;
+    method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+    method public abstract int write(java.nio.ByteBuffer, long) throws java.io.IOException;
+  }
+
+  public static class FileChannel.MapMode {
+    field public static final java.nio.channels.FileChannel.MapMode PRIVATE;
+    field public static final java.nio.channels.FileChannel.MapMode READ_ONLY;
+    field public static final java.nio.channels.FileChannel.MapMode READ_WRITE;
+  }
+
+  public abstract class FileLock implements java.lang.AutoCloseable {
+    ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean);
+    ctor protected FileLock(java.nio.channels.AsynchronousFileChannel, long, long, boolean);
+    method public java.nio.channels.Channel acquiredBy();
+    method public final java.nio.channels.FileChannel channel();
+    method public final void close() throws java.io.IOException;
+    method public final boolean isShared();
+    method public abstract boolean isValid();
+    method public final boolean overlaps(long, long);
+    method public final long position();
+    method public abstract void release() throws java.io.IOException;
+    method public final long size();
+    method public final String toString();
+  }
+
+  public class FileLockInterruptionException extends java.io.IOException {
+    ctor public FileLockInterruptionException();
+  }
+
+  public interface GatheringByteChannel extends java.nio.channels.WritableByteChannel {
+    method public long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+    method public long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+  }
+
+  public class IllegalBlockingModeException extends java.lang.IllegalStateException {
+    ctor public IllegalBlockingModeException();
+  }
+
+  public class IllegalChannelGroupException extends java.lang.IllegalArgumentException {
+    ctor public IllegalChannelGroupException();
+  }
+
+  public class IllegalSelectorException extends java.lang.IllegalArgumentException {
+    ctor public IllegalSelectorException();
+  }
+
+  public class InterruptedByTimeoutException extends java.io.IOException {
+    ctor public InterruptedByTimeoutException();
+  }
+
+  public interface InterruptibleChannel extends java.nio.channels.Channel {
+  }
+
+  public abstract class MembershipKey {
+    ctor protected MembershipKey();
+    method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
+    method public abstract java.nio.channels.MulticastChannel channel();
+    method public abstract void drop();
+    method public abstract java.net.InetAddress group();
+    method public abstract boolean isValid();
+    method public abstract java.net.NetworkInterface networkInterface();
+    method public abstract java.net.InetAddress sourceAddress();
+    method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
+  }
+
+  public interface MulticastChannel extends java.nio.channels.NetworkChannel {
+    method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
+    method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
+  }
+
+  public interface NetworkChannel extends java.nio.channels.Channel {
+    method public java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+    method public <T> T getOption(java.net.SocketOption<T>) throws java.io.IOException;
+    method public <T> java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+    method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
+  }
+
+  public class NoConnectionPendingException extends java.lang.IllegalStateException {
+    ctor public NoConnectionPendingException();
+  }
+
+  public class NonReadableChannelException extends java.lang.IllegalStateException {
+    ctor public NonReadableChannelException();
+  }
+
+  public class NonWritableChannelException extends java.lang.IllegalStateException {
+    ctor public NonWritableChannelException();
+  }
+
+  public class NotYetBoundException extends java.lang.IllegalStateException {
+    ctor public NotYetBoundException();
+  }
+
+  public class NotYetConnectedException extends java.lang.IllegalStateException {
+    ctor public NotYetConnectedException();
+  }
+
+  public class OverlappingFileLockException extends java.lang.IllegalStateException {
+    ctor public OverlappingFileLockException();
+  }
+
+  public abstract class Pipe {
+    ctor protected Pipe();
+    method public static java.nio.channels.Pipe open() throws java.io.IOException;
+    method public abstract java.nio.channels.Pipe.SinkChannel sink();
+    method public abstract java.nio.channels.Pipe.SourceChannel source();
+  }
+
+  public abstract static class Pipe.SinkChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.WritableByteChannel {
+    ctor protected Pipe.SinkChannel(java.nio.channels.spi.SelectorProvider);
+    method public final int validOps();
+  }
+
+  public abstract static class Pipe.SourceChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ReadableByteChannel java.nio.channels.ScatteringByteChannel {
+    ctor protected Pipe.SourceChannel(java.nio.channels.spi.SelectorProvider);
+    method public final int validOps();
+  }
+
+  public class ReadPendingException extends java.lang.IllegalStateException {
+    ctor public ReadPendingException();
+  }
+
+  public interface ReadableByteChannel extends java.nio.channels.Channel {
+    method public int read(java.nio.ByteBuffer) throws java.io.IOException;
+  }
+
+  public interface ScatteringByteChannel extends java.nio.channels.ReadableByteChannel {
+    method public long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
+    method public long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+  }
+
+  public interface SeekableByteChannel extends java.nio.channels.ByteChannel {
+    method public long position() throws java.io.IOException;
+    method public java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException;
+    method public long size() throws java.io.IOException;
+    method public java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException;
+  }
+
+  public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel {
+    ctor protected SelectableChannel();
+    method public abstract Object blockingLock();
+    method public abstract java.nio.channels.SelectableChannel configureBlocking(boolean) throws java.io.IOException;
+    method public abstract boolean isBlocking();
+    method public abstract boolean isRegistered();
+    method public abstract java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
+    method public abstract java.nio.channels.spi.SelectorProvider provider();
+    method public abstract java.nio.channels.SelectionKey register(java.nio.channels.Selector, int, Object) throws java.nio.channels.ClosedChannelException;
+    method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int) throws java.nio.channels.ClosedChannelException;
+    method public abstract int validOps();
+  }
+
+  public abstract class SelectionKey {
+    ctor protected SelectionKey();
+    method public final Object attach(Object);
+    method public final Object attachment();
+    method public abstract void cancel();
+    method public abstract java.nio.channels.SelectableChannel channel();
+    method public abstract int interestOps();
+    method public abstract java.nio.channels.SelectionKey interestOps(int);
+    method public final boolean isAcceptable();
+    method public final boolean isConnectable();
+    method public final boolean isReadable();
+    method public abstract boolean isValid();
+    method public final boolean isWritable();
+    method public abstract int readyOps();
+    method public abstract java.nio.channels.Selector selector();
+    field public static final int OP_ACCEPT = 16; // 0x10
+    field public static final int OP_CONNECT = 8; // 0x8
+    field public static final int OP_READ = 1; // 0x1
+    field public static final int OP_WRITE = 4; // 0x4
+  }
+
+  public abstract class Selector implements java.io.Closeable {
+    ctor protected Selector();
+    method public abstract boolean isOpen();
+    method public abstract java.util.Set<java.nio.channels.SelectionKey> keys();
+    method public static java.nio.channels.Selector open() throws java.io.IOException;
+    method public abstract java.nio.channels.spi.SelectorProvider provider();
+    method public abstract int select(long) throws java.io.IOException;
+    method public abstract int select() throws java.io.IOException;
+    method public abstract int selectNow() throws java.io.IOException;
+    method public abstract java.util.Set<java.nio.channels.SelectionKey> selectedKeys();
+    method public abstract java.nio.channels.Selector wakeup();
+  }
+
+  public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel {
+    ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider);
+    method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException;
+    method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
+    method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException;
+    method public abstract <T> java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+    method public abstract java.net.ServerSocket socket();
+    method public final int validOps();
+  }
+
+  public class ShutdownChannelGroupException extends java.lang.IllegalStateException {
+    ctor public ShutdownChannelGroupException();
+  }
+
+  public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
+    ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
+    method public abstract java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException;
+    method public abstract boolean finishConnect() throws java.io.IOException;
+    method public abstract java.net.SocketAddress getRemoteAddress() throws java.io.IOException;
+    method public abstract boolean isConnected();
+    method public abstract boolean isConnectionPending();
+    method public static java.nio.channels.SocketChannel open() throws java.io.IOException;
+    method public static java.nio.channels.SocketChannel open(java.net.SocketAddress) throws java.io.IOException;
+    method public final long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+    method public abstract <T> java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+    method public abstract java.nio.channels.SocketChannel shutdownInput() throws java.io.IOException;
+    method public abstract java.nio.channels.SocketChannel shutdownOutput() throws java.io.IOException;
+    method public abstract java.net.Socket socket();
+    method public final int validOps();
+    method public final long write(java.nio.ByteBuffer[]) throws java.io.IOException;
+  }
+
+  public class UnresolvedAddressException extends java.lang.IllegalArgumentException {
+    ctor public UnresolvedAddressException();
+  }
+
+  public class UnsupportedAddressTypeException extends java.lang.IllegalArgumentException {
+    ctor public UnsupportedAddressTypeException();
+  }
+
+  public interface WritableByteChannel extends java.nio.channels.Channel {
+    method public int write(java.nio.ByteBuffer) throws java.io.IOException;
+  }
+
+  public class WritePendingException extends java.lang.IllegalStateException {
+    ctor public WritePendingException();
+  }
+
+}
+
+package java.nio.channels.spi {
+
+  public abstract class AbstractInterruptibleChannel implements java.nio.channels.Channel java.nio.channels.InterruptibleChannel {
+    ctor protected AbstractInterruptibleChannel();
+    method protected final void begin();
+    method public final void close() throws java.io.IOException;
+    method protected final void end(boolean) throws java.nio.channels.AsynchronousCloseException;
+    method protected abstract void implCloseChannel() throws java.io.IOException;
+    method public final boolean isOpen();
+  }
+
+  public abstract class AbstractSelectableChannel extends java.nio.channels.SelectableChannel {
+    ctor protected AbstractSelectableChannel(java.nio.channels.spi.SelectorProvider);
+    method public final Object blockingLock();
+    method public final java.nio.channels.SelectableChannel configureBlocking(boolean) throws java.io.IOException;
+    method protected final void implCloseChannel() throws java.io.IOException;
+    method protected abstract void implCloseSelectableChannel() throws java.io.IOException;
+    method protected abstract void implConfigureBlocking(boolean) throws java.io.IOException;
+    method public final boolean isBlocking();
+    method public final boolean isRegistered();
+    method public final java.nio.channels.SelectionKey keyFor(java.nio.channels.Selector);
+    method public final java.nio.channels.spi.SelectorProvider provider();
+    method public final java.nio.channels.SelectionKey register(java.nio.channels.Selector, int, Object) throws java.nio.channels.ClosedChannelException;
+  }
+
+  public abstract class AbstractSelectionKey extends java.nio.channels.SelectionKey {
+    ctor protected AbstractSelectionKey();
+    method public final void cancel();
+    method public final boolean isValid();
+  }
+
+  public abstract class AbstractSelector extends java.nio.channels.Selector {
+    ctor protected AbstractSelector(java.nio.channels.spi.SelectorProvider);
+    method protected final void begin();
+    method protected final java.util.Set<java.nio.channels.SelectionKey> cancelledKeys();
+    method public final void close() throws java.io.IOException;
+    method protected final void deregister(java.nio.channels.spi.AbstractSelectionKey);
+    method protected final void end();
+    method protected abstract void implCloseSelector() throws java.io.IOException;
+    method public final boolean isOpen();
+    method public final java.nio.channels.spi.SelectorProvider provider();
+    method protected abstract java.nio.channels.SelectionKey register(java.nio.channels.spi.AbstractSelectableChannel, int, Object);
+  }
+
+  public abstract class AsynchronousChannelProvider {
+    ctor protected AsynchronousChannelProvider();
+    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(int, java.util.concurrent.ThreadFactory) throws java.io.IOException;
+    method public abstract java.nio.channels.AsynchronousChannelGroup openAsynchronousChannelGroup(java.util.concurrent.ExecutorService, int) throws java.io.IOException;
+    method public abstract java.nio.channels.AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+    method public abstract java.nio.channels.AsynchronousSocketChannel openAsynchronousSocketChannel(java.nio.channels.AsynchronousChannelGroup) throws java.io.IOException;
+    method public static java.nio.channels.spi.AsynchronousChannelProvider provider();
+  }
+
+  public abstract class SelectorProvider {
+    ctor protected SelectorProvider();
+    method public java.nio.channels.Channel inheritedChannel() throws java.io.IOException;
+    method public abstract java.nio.channels.DatagramChannel openDatagramChannel() throws java.io.IOException;
+    method public abstract java.nio.channels.DatagramChannel openDatagramChannel(java.net.ProtocolFamily) throws java.io.IOException;
+    method public abstract java.nio.channels.Pipe openPipe() throws java.io.IOException;
+    method public abstract java.nio.channels.spi.AbstractSelector openSelector() throws java.io.IOException;
+    method public abstract java.nio.channels.ServerSocketChannel openServerSocketChannel() throws java.io.IOException;
+    method public abstract java.nio.channels.SocketChannel openSocketChannel() throws java.io.IOException;
+    method public static java.nio.channels.spi.SelectorProvider provider();
+  }
+
+}
+
+package java.nio.charset {
+
+  public class CharacterCodingException extends java.io.IOException {
+    ctor public CharacterCodingException();
+  }
+
+  public abstract class Charset implements java.lang.Comparable<java.nio.charset.Charset> {
+    ctor protected Charset(String, String[]);
+    method public final java.util.Set<java.lang.String> aliases();
+    method public static java.util.SortedMap<java.lang.String,java.nio.charset.Charset> availableCharsets();
+    method public boolean canEncode();
+    method public final int compareTo(java.nio.charset.Charset);
+    method public abstract boolean contains(java.nio.charset.Charset);
+    method public final java.nio.CharBuffer decode(java.nio.ByteBuffer);
+    method public static java.nio.charset.Charset defaultCharset();
+    method public String displayName();
+    method public String displayName(java.util.Locale);
+    method public final java.nio.ByteBuffer encode(java.nio.CharBuffer);
+    method public final java.nio.ByteBuffer encode(String);
+    method public final boolean equals(Object);
+    method public static java.nio.charset.Charset forName(String);
+    method public final int hashCode();
+    method public final boolean isRegistered();
+    method public static boolean isSupported(String);
+    method public final String name();
+    method public abstract java.nio.charset.CharsetDecoder newDecoder();
+    method public abstract java.nio.charset.CharsetEncoder newEncoder();
+    method public final String toString();
+  }
+
+  public abstract class CharsetDecoder {
+    ctor protected CharsetDecoder(java.nio.charset.Charset, float, float);
+    method public final float averageCharsPerByte();
+    method public final java.nio.charset.Charset charset();
+    method public final java.nio.charset.CoderResult decode(java.nio.ByteBuffer, java.nio.CharBuffer, boolean);
+    method public final java.nio.CharBuffer decode(java.nio.ByteBuffer) throws java.nio.charset.CharacterCodingException;
+    method protected abstract java.nio.charset.CoderResult decodeLoop(java.nio.ByteBuffer, java.nio.CharBuffer);
+    method public java.nio.charset.Charset detectedCharset();
+    method public final java.nio.charset.CoderResult flush(java.nio.CharBuffer);
+    method protected java.nio.charset.CoderResult implFlush(java.nio.CharBuffer);
+    method protected void implOnMalformedInput(java.nio.charset.CodingErrorAction);
+    method protected void implOnUnmappableCharacter(java.nio.charset.CodingErrorAction);
+    method protected void implReplaceWith(String);
+    method protected void implReset();
+    method public boolean isAutoDetecting();
+    method public boolean isCharsetDetected();
+    method public java.nio.charset.CodingErrorAction malformedInputAction();
+    method public final float maxCharsPerByte();
+    method public final java.nio.charset.CharsetDecoder onMalformedInput(java.nio.charset.CodingErrorAction);
+    method public final java.nio.charset.CharsetDecoder onUnmappableCharacter(java.nio.charset.CodingErrorAction);
+    method public final java.nio.charset.CharsetDecoder replaceWith(String);
+    method public final String replacement();
+    method public final java.nio.charset.CharsetDecoder reset();
+    method public java.nio.charset.CodingErrorAction unmappableCharacterAction();
+  }
+
+  public abstract class CharsetEncoder {
+    ctor protected CharsetEncoder(java.nio.charset.Charset, float, float, byte[]);
+    ctor protected CharsetEncoder(java.nio.charset.Charset, float, float);
+    method public final float averageBytesPerChar();
+    method public boolean canEncode(char);
+    method public boolean canEncode(CharSequence);
+    method public final java.nio.charset.Charset charset();
+    method public final java.nio.charset.CoderResult encode(java.nio.CharBuffer, java.nio.ByteBuffer, boolean);
+    method public final java.nio.ByteBuffer encode(java.nio.CharBuffer) throws java.nio.charset.CharacterCodingException;
+    method protected abstract java.nio.charset.CoderResult encodeLoop(java.nio.CharBuffer, java.nio.ByteBuffer);
+    method public final java.nio.charset.CoderResult flush(java.nio.ByteBuffer);
+    method protected java.nio.charset.CoderResult implFlush(java.nio.ByteBuffer);
+    method protected void implOnMalformedInput(java.nio.charset.CodingErrorAction);
+    method protected void implOnUnmappableCharacter(java.nio.charset.CodingErrorAction);
+    method protected void implReplaceWith(byte[]);
+    method protected void implReset();
+    method public boolean isLegalReplacement(byte[]);
+    method public java.nio.charset.CodingErrorAction malformedInputAction();
+    method public final float maxBytesPerChar();
+    method public final java.nio.charset.CharsetEncoder onMalformedInput(java.nio.charset.CodingErrorAction);
+    method public final java.nio.charset.CharsetEncoder onUnmappableCharacter(java.nio.charset.CodingErrorAction);
+    method public final java.nio.charset.CharsetEncoder replaceWith(byte[]);
+    method public final byte[] replacement();
+    method public final java.nio.charset.CharsetEncoder reset();
+    method public java.nio.charset.CodingErrorAction unmappableCharacterAction();
+  }
+
+  public class CoderMalfunctionError extends java.lang.Error {
+    ctor public CoderMalfunctionError(Exception);
+  }
+
+  public class CoderResult {
+    method public boolean isError();
+    method public boolean isMalformed();
+    method public boolean isOverflow();
+    method public boolean isUnderflow();
+    method public boolean isUnmappable();
+    method public int length();
+    method public static java.nio.charset.CoderResult malformedForLength(int);
+    method public void throwException() throws java.nio.charset.CharacterCodingException;
+    method public static java.nio.charset.CoderResult unmappableForLength(int);
+    field public static final java.nio.charset.CoderResult OVERFLOW;
+    field public static final java.nio.charset.CoderResult UNDERFLOW;
+  }
+
+  public class CodingErrorAction {
+    field public static final java.nio.charset.CodingErrorAction IGNORE;
+    field public static final java.nio.charset.CodingErrorAction REPLACE;
+    field public static final java.nio.charset.CodingErrorAction REPORT;
+  }
+
+  public class IllegalCharsetNameException extends java.lang.IllegalArgumentException {
+    ctor public IllegalCharsetNameException(String);
+    method public String getCharsetName();
+  }
+
+  public class MalformedInputException extends java.nio.charset.CharacterCodingException {
+    ctor public MalformedInputException(int);
+    method public int getInputLength();
+  }
+
+  public final class StandardCharsets {
+    field public static final java.nio.charset.Charset ISO_8859_1;
+    field public static final java.nio.charset.Charset US_ASCII;
+    field public static final java.nio.charset.Charset UTF_16;
+    field public static final java.nio.charset.Charset UTF_16BE;
+    field public static final java.nio.charset.Charset UTF_16LE;
+    field public static final java.nio.charset.Charset UTF_8;
+  }
+
+  public class UnmappableCharacterException extends java.nio.charset.CharacterCodingException {
+    ctor public UnmappableCharacterException(int);
+    method public int getInputLength();
+  }
+
+  public class UnsupportedCharsetException extends java.lang.IllegalArgumentException {
+    ctor public UnsupportedCharsetException(String);
+    method public String getCharsetName();
+  }
+
+}
+
+package java.nio.charset.spi {
+
+  public abstract class CharsetProvider {
+    ctor protected CharsetProvider();
+    method public abstract java.nio.charset.Charset charsetForName(String);
+    method public abstract java.util.Iterator<java.nio.charset.Charset> charsets();
+  }
+
+}
+
+package java.nio.file {
+
+  public class AccessDeniedException extends java.nio.file.FileSystemException {
+    ctor public AccessDeniedException(String);
+    ctor public AccessDeniedException(String, String, String);
+  }
+
+  public enum AccessMode {
+    enum_constant public static final java.nio.file.AccessMode EXECUTE;
+    enum_constant public static final java.nio.file.AccessMode READ;
+    enum_constant public static final java.nio.file.AccessMode WRITE;
+  }
+
+  public class AtomicMoveNotSupportedException extends java.nio.file.FileSystemException {
+    ctor public AtomicMoveNotSupportedException(String, String, String);
+  }
+
+  public class ClosedDirectoryStreamException extends java.lang.IllegalStateException {
+    ctor public ClosedDirectoryStreamException();
+  }
+
+  public class ClosedFileSystemException extends java.lang.IllegalStateException {
+    ctor public ClosedFileSystemException();
+  }
+
+  public class ClosedWatchServiceException extends java.lang.IllegalStateException {
+    ctor public ClosedWatchServiceException();
+  }
+
+  public interface CopyOption {
+  }
+
+  public final class DirectoryIteratorException extends java.util.ConcurrentModificationException {
+    ctor public DirectoryIteratorException(java.io.IOException);
+    method public java.io.IOException getCause();
+  }
+
+  public class DirectoryNotEmptyException extends java.nio.file.FileSystemException {
+    ctor public DirectoryNotEmptyException(String);
+  }
+
+  public interface DirectoryStream<T> extends java.io.Closeable java.lang.Iterable<T> {
+  }
+
+  @java.lang.FunctionalInterface public static interface DirectoryStream.Filter<T> {
+    method public boolean accept(T) throws java.io.IOException;
+  }
+
+  public class FileAlreadyExistsException extends java.nio.file.FileSystemException {
+    ctor public FileAlreadyExistsException(String);
+    ctor public FileAlreadyExistsException(String, String, String);
+  }
+
+  public abstract class FileStore {
+    ctor protected FileStore();
+    method public abstract Object getAttribute(String) throws java.io.IOException;
+    method public abstract <V extends java.nio.file.attribute.FileStoreAttributeView> V getFileStoreAttributeView(Class<V>);
+    method public abstract long getTotalSpace() throws java.io.IOException;
+    method public abstract long getUnallocatedSpace() throws java.io.IOException;
+    method public abstract long getUsableSpace() throws java.io.IOException;
+    method public abstract boolean isReadOnly();
+    method public abstract String name();
+    method public abstract boolean supportsFileAttributeView(Class<? extends java.nio.file.attribute.FileAttributeView>);
+    method public abstract boolean supportsFileAttributeView(String);
+    method public abstract String type();
+  }
+
+  public abstract class FileSystem implements java.io.Closeable {
+    ctor protected FileSystem();
+    method public abstract Iterable<java.nio.file.FileStore> getFileStores();
+    method public abstract java.nio.file.Path getPath(String, java.lang.String...);
+    method public abstract java.nio.file.PathMatcher getPathMatcher(String);
+    method public abstract Iterable<java.nio.file.Path> getRootDirectories();
+    method public abstract String getSeparator();
+    method public abstract java.nio.file.attribute.UserPrincipalLookupService getUserPrincipalLookupService();
+    method public abstract boolean isOpen();
+    method public abstract boolean isReadOnly();
+    method public abstract java.nio.file.WatchService newWatchService() throws java.io.IOException;
+    method public abstract java.nio.file.spi.FileSystemProvider provider();
+    method public abstract java.util.Set<java.lang.String> supportedFileAttributeViews();
+  }
+
+  public class FileSystemAlreadyExistsException extends java.lang.RuntimeException {
+    ctor public FileSystemAlreadyExistsException();
+    ctor public FileSystemAlreadyExistsException(String);
+  }
+
+  public class FileSystemException extends java.io.IOException {
+    ctor public FileSystemException(String);
+    ctor public FileSystemException(String, String, String);
+    method public String getFile();
+    method public String getOtherFile();
+    method public String getReason();
+  }
+
+  public class FileSystemLoopException extends java.nio.file.FileSystemException {
+    ctor public FileSystemLoopException(String);
+  }
+
+  public class FileSystemNotFoundException extends java.lang.RuntimeException {
+    ctor public FileSystemNotFoundException();
+    ctor public FileSystemNotFoundException(String);
+  }
+
+  public final class FileSystems {
+    method public static java.nio.file.FileSystem getDefault();
+    method public static java.nio.file.FileSystem getFileSystem(java.net.URI);
+    method public static java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String,?>) throws java.io.IOException;
+    method public static java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String,?>, ClassLoader) throws java.io.IOException;
+    method public static java.nio.file.FileSystem newFileSystem(java.nio.file.Path, ClassLoader) throws java.io.IOException;
+  }
+
+  public enum FileVisitOption {
+    enum_constant public static final java.nio.file.FileVisitOption FOLLOW_LINKS;
+  }
+
+  public enum FileVisitResult {
+    enum_constant public static final java.nio.file.FileVisitResult CONTINUE;
+    enum_constant public static final java.nio.file.FileVisitResult SKIP_SIBLINGS;
+    enum_constant public static final java.nio.file.FileVisitResult SKIP_SUBTREE;
+    enum_constant public static final java.nio.file.FileVisitResult TERMINATE;
+  }
+
+  public interface FileVisitor<T> {
+    method public java.nio.file.FileVisitResult postVisitDirectory(T, java.io.IOException) throws java.io.IOException;
+    method public java.nio.file.FileVisitResult preVisitDirectory(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+    method public java.nio.file.FileVisitResult visitFile(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+    method public java.nio.file.FileVisitResult visitFileFailed(T, java.io.IOException) throws java.io.IOException;
+  }
+
+  public final class Files {
+    method public static java.nio.file.Path copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+    method public static long copy(java.io.InputStream, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+    method public static long copy(java.nio.file.Path, java.io.OutputStream) throws java.io.IOException;
+    method public static java.nio.file.Path createDirectories(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.file.Path createDirectory(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.file.Path createFile(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.file.Path createLink(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+    method public static java.nio.file.Path createSymbolicLink(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.file.Path createTempDirectory(java.nio.file.Path, String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.file.Path createTempDirectory(String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.file.Path createTempFile(java.nio.file.Path, String, String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.file.Path createTempFile(String, String, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static void delete(java.nio.file.Path) throws java.io.IOException;
+    method public static boolean deleteIfExists(java.nio.file.Path) throws java.io.IOException;
+    method public static boolean exists(java.nio.file.Path, java.nio.file.LinkOption...);
+    method public static java.util.stream.Stream<java.nio.file.Path> find(java.nio.file.Path, int, java.util.function.BiPredicate<java.nio.file.Path,java.nio.file.attribute.BasicFileAttributes>, java.nio.file.FileVisitOption...) throws java.io.IOException;
+    method public static Object getAttribute(java.nio.file.Path, String, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public static <V extends java.nio.file.attribute.FileAttributeView> V getFileAttributeView(java.nio.file.Path, Class<V>, java.nio.file.LinkOption...);
+    method public static java.nio.file.FileStore getFileStore(java.nio.file.Path) throws java.io.IOException;
+    method public static java.nio.file.attribute.FileTime getLastModifiedTime(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public static java.nio.file.attribute.UserPrincipal getOwner(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public static java.util.Set<java.nio.file.attribute.PosixFilePermission> getPosixFilePermissions(java.nio.file.Path, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public static boolean isDirectory(java.nio.file.Path, java.nio.file.LinkOption...);
+    method public static boolean isExecutable(java.nio.file.Path);
+    method public static boolean isHidden(java.nio.file.Path) throws java.io.IOException;
+    method public static boolean isReadable(java.nio.file.Path);
+    method public static boolean isRegularFile(java.nio.file.Path, java.nio.file.LinkOption...);
+    method public static boolean isSameFile(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+    method public static boolean isSymbolicLink(java.nio.file.Path);
+    method public static boolean isWritable(java.nio.file.Path);
+    method public static java.util.stream.Stream<java.lang.String> lines(java.nio.file.Path, java.nio.charset.Charset) throws java.io.IOException;
+    method public static java.util.stream.Stream<java.lang.String> lines(java.nio.file.Path) throws java.io.IOException;
+    method public static java.util.stream.Stream<java.nio.file.Path> list(java.nio.file.Path) throws java.io.IOException;
+    method public static java.nio.file.Path move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+    method public static java.io.BufferedReader newBufferedReader(java.nio.file.Path, java.nio.charset.Charset) throws java.io.IOException;
+    method public static java.io.BufferedReader newBufferedReader(java.nio.file.Path) throws java.io.IOException;
+    method public static java.io.BufferedWriter newBufferedWriter(java.nio.file.Path, java.nio.charset.Charset, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public static java.io.BufferedWriter newBufferedWriter(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public static java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public static java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path) throws java.io.IOException;
+    method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, String) throws java.io.IOException;
+    method public static java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream.Filter<? super java.nio.file.Path>) throws java.io.IOException;
+    method public static java.io.InputStream newInputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public static java.io.OutputStream newOutputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public static boolean notExists(java.nio.file.Path, java.nio.file.LinkOption...);
+    method public static String probeContentType(java.nio.file.Path) throws java.io.IOException;
+    method public static byte[] readAllBytes(java.nio.file.Path) throws java.io.IOException;
+    method public static java.util.List<java.lang.String> readAllLines(java.nio.file.Path, java.nio.charset.Charset) throws java.io.IOException;
+    method public static java.util.List<java.lang.String> readAllLines(java.nio.file.Path) throws java.io.IOException;
+    method public static <A extends java.nio.file.attribute.BasicFileAttributes> A readAttributes(java.nio.file.Path, Class<A>, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public static java.util.Map<java.lang.String,java.lang.Object> readAttributes(java.nio.file.Path, String, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public static java.nio.file.Path readSymbolicLink(java.nio.file.Path) throws java.io.IOException;
+    method public static java.nio.file.Path setAttribute(java.nio.file.Path, String, Object, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public static java.nio.file.Path setLastModifiedTime(java.nio.file.Path, java.nio.file.attribute.FileTime) throws java.io.IOException;
+    method public static java.nio.file.Path setOwner(java.nio.file.Path, java.nio.file.attribute.UserPrincipal) throws java.io.IOException;
+    method public static java.nio.file.Path setPosixFilePermissions(java.nio.file.Path, java.util.Set<java.nio.file.attribute.PosixFilePermission>) throws java.io.IOException;
+    method public static long size(java.nio.file.Path) throws java.io.IOException;
+    method public static java.util.stream.Stream<java.nio.file.Path> walk(java.nio.file.Path, int, java.nio.file.FileVisitOption...) throws java.io.IOException;
+    method public static java.util.stream.Stream<java.nio.file.Path> walk(java.nio.file.Path, java.nio.file.FileVisitOption...) throws java.io.IOException;
+    method public static java.nio.file.Path walkFileTree(java.nio.file.Path, java.util.Set<java.nio.file.FileVisitOption>, int, java.nio.file.FileVisitor<? super java.nio.file.Path>) throws java.io.IOException;
+    method public static java.nio.file.Path walkFileTree(java.nio.file.Path, java.nio.file.FileVisitor<? super java.nio.file.Path>) throws java.io.IOException;
+    method public static java.nio.file.Path write(java.nio.file.Path, byte[], java.nio.file.OpenOption...) throws java.io.IOException;
+    method public static java.nio.file.Path write(java.nio.file.Path, Iterable<? extends java.lang.CharSequence>, java.nio.charset.Charset, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public static java.nio.file.Path write(java.nio.file.Path, Iterable<? extends java.lang.CharSequence>, java.nio.file.OpenOption...) throws java.io.IOException;
+  }
+
+  public class InvalidPathException extends java.lang.IllegalArgumentException {
+    ctor public InvalidPathException(String, String, int);
+    ctor public InvalidPathException(String, String);
+    method public int getIndex();
+    method public String getInput();
+    method public String getReason();
+  }
+
+  public enum LinkOption implements java.nio.file.CopyOption java.nio.file.OpenOption {
+    enum_constant public static final java.nio.file.LinkOption NOFOLLOW_LINKS;
+  }
+
+  public final class LinkPermission extends java.security.BasicPermission {
+    ctor public LinkPermission(String);
+    ctor public LinkPermission(String, String);
+  }
+
+  public class NoSuchFileException extends java.nio.file.FileSystemException {
+    ctor public NoSuchFileException(String);
+    ctor public NoSuchFileException(String, String, String);
+  }
+
+  public class NotDirectoryException extends java.nio.file.FileSystemException {
+    ctor public NotDirectoryException(String);
+  }
+
+  public class NotLinkException extends java.nio.file.FileSystemException {
+    ctor public NotLinkException(String);
+    ctor public NotLinkException(String, String, String);
+  }
+
+  public interface OpenOption {
+  }
+
+  public interface Path extends java.lang.Comparable<java.nio.file.Path> java.lang.Iterable<java.nio.file.Path> java.nio.file.Watchable {
+    method public int compareTo(java.nio.file.Path);
+    method public boolean endsWith(java.nio.file.Path);
+    method public boolean endsWith(String);
+    method public boolean equals(Object);
+    method public java.nio.file.Path getFileName();
+    method public java.nio.file.FileSystem getFileSystem();
+    method public java.nio.file.Path getName(int);
+    method public int getNameCount();
+    method public java.nio.file.Path getParent();
+    method public java.nio.file.Path getRoot();
+    method public int hashCode();
+    method public boolean isAbsolute();
+    method public java.util.Iterator<java.nio.file.Path> iterator();
+    method public java.nio.file.Path normalize();
+    method public java.nio.file.Path relativize(java.nio.file.Path);
+    method public java.nio.file.Path resolve(java.nio.file.Path);
+    method public java.nio.file.Path resolve(String);
+    method public java.nio.file.Path resolveSibling(java.nio.file.Path);
+    method public java.nio.file.Path resolveSibling(String);
+    method public boolean startsWith(java.nio.file.Path);
+    method public boolean startsWith(String);
+    method public java.nio.file.Path subpath(int, int);
+    method public java.nio.file.Path toAbsolutePath();
+    method public java.io.File toFile();
+    method public java.nio.file.Path toRealPath(java.nio.file.LinkOption...) throws java.io.IOException;
+    method public String toString();
+    method public java.net.URI toUri();
+  }
+
+  @java.lang.FunctionalInterface public interface PathMatcher {
+    method public boolean matches(java.nio.file.Path);
+  }
+
+  public final class Paths {
+    method public static java.nio.file.Path get(String, java.lang.String...);
+    method public static java.nio.file.Path get(java.net.URI);
+  }
+
+  public class ProviderMismatchException extends java.lang.IllegalArgumentException {
+    ctor public ProviderMismatchException();
+    ctor public ProviderMismatchException(String);
+  }
+
+  public class ProviderNotFoundException extends java.lang.RuntimeException {
+    ctor public ProviderNotFoundException();
+    ctor public ProviderNotFoundException(String);
+  }
+
+  public class ReadOnlyFileSystemException extends java.lang.UnsupportedOperationException {
+    ctor public ReadOnlyFileSystemException();
+  }
+
+  public interface SecureDirectoryStream<T> extends java.nio.file.DirectoryStream<T> {
+    method public void deleteDirectory(T) throws java.io.IOException;
+    method public void deleteFile(T) throws java.io.IOException;
+    method public <V extends java.nio.file.attribute.FileAttributeView> V getFileAttributeView(Class<V>);
+    method public <V extends java.nio.file.attribute.FileAttributeView> V getFileAttributeView(T, Class<V>, java.nio.file.LinkOption...);
+    method public void move(T, java.nio.file.SecureDirectoryStream<T>, T) throws java.io.IOException;
+    method public java.nio.channels.SeekableByteChannel newByteChannel(T, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public java.nio.file.SecureDirectoryStream<T> newDirectoryStream(T, java.nio.file.LinkOption...) throws java.io.IOException;
+  }
+
+  public class SimpleFileVisitor<T> implements java.nio.file.FileVisitor<T> {
+    ctor protected SimpleFileVisitor();
+    method public java.nio.file.FileVisitResult postVisitDirectory(T, java.io.IOException) throws java.io.IOException;
+    method public java.nio.file.FileVisitResult preVisitDirectory(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+    method public java.nio.file.FileVisitResult visitFile(T, java.nio.file.attribute.BasicFileAttributes) throws java.io.IOException;
+    method public java.nio.file.FileVisitResult visitFileFailed(T, java.io.IOException) throws java.io.IOException;
+  }
+
+  public enum StandardCopyOption implements java.nio.file.CopyOption {
+    enum_constant public static final java.nio.file.StandardCopyOption ATOMIC_MOVE;
+    enum_constant public static final java.nio.file.StandardCopyOption COPY_ATTRIBUTES;
+    enum_constant public static final java.nio.file.StandardCopyOption REPLACE_EXISTING;
+  }
+
+  public enum StandardOpenOption implements java.nio.file.OpenOption {
+    enum_constant public static final java.nio.file.StandardOpenOption APPEND;
+    enum_constant public static final java.nio.file.StandardOpenOption CREATE;
+    enum_constant public static final java.nio.file.StandardOpenOption CREATE_NEW;
+    enum_constant public static final java.nio.file.StandardOpenOption DELETE_ON_CLOSE;
+    enum_constant public static final java.nio.file.StandardOpenOption DSYNC;
+    enum_constant public static final java.nio.file.StandardOpenOption READ;
+    enum_constant public static final java.nio.file.StandardOpenOption SPARSE;
+    enum_constant public static final java.nio.file.StandardOpenOption SYNC;
+    enum_constant public static final java.nio.file.StandardOpenOption TRUNCATE_EXISTING;
+    enum_constant public static final java.nio.file.StandardOpenOption WRITE;
+  }
+
+  public final class StandardWatchEventKinds {
+    field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_CREATE;
+    field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_DELETE;
+    field public static final java.nio.file.WatchEvent.Kind<java.nio.file.Path> ENTRY_MODIFY;
+    field public static final java.nio.file.WatchEvent.Kind<java.lang.Object> OVERFLOW;
+  }
+
+  public interface WatchEvent<T> {
+    method public T context();
+    method public int count();
+    method public java.nio.file.WatchEvent.Kind<T> kind();
+  }
+
+  public static interface WatchEvent.Kind<T> {
+    method public String name();
+    method public Class<T> type();
+  }
+
+  public static interface WatchEvent.Modifier {
+    method public String name();
+  }
+
+  public interface WatchKey {
+    method public void cancel();
+    method public boolean isValid();
+    method public java.util.List<java.nio.file.WatchEvent<?>> pollEvents();
+    method public boolean reset();
+    method public java.nio.file.Watchable watchable();
+  }
+
+  public interface WatchService extends java.io.Closeable {
+    method public java.nio.file.WatchKey poll();
+    method public java.nio.file.WatchKey poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public java.nio.file.WatchKey take() throws java.lang.InterruptedException;
+  }
+
+  public interface Watchable {
+    method public java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>[], java.nio.file.WatchEvent.Modifier...) throws java.io.IOException;
+    method public java.nio.file.WatchKey register(java.nio.file.WatchService, java.nio.file.WatchEvent.Kind<?>...) throws java.io.IOException;
+  }
+
+}
+
+package java.nio.file.attribute {
+
+  public final class AclEntry {
+    method public java.util.Set<java.nio.file.attribute.AclEntryFlag> flags();
+    method public static java.nio.file.attribute.AclEntry.Builder newBuilder();
+    method public static java.nio.file.attribute.AclEntry.Builder newBuilder(java.nio.file.attribute.AclEntry);
+    method public java.util.Set<java.nio.file.attribute.AclEntryPermission> permissions();
+    method public java.nio.file.attribute.UserPrincipal principal();
+    method public java.nio.file.attribute.AclEntryType type();
+  }
+
+  public static final class AclEntry.Builder {
+    method public java.nio.file.attribute.AclEntry build();
+    method public java.nio.file.attribute.AclEntry.Builder setFlags(java.util.Set<java.nio.file.attribute.AclEntryFlag>);
+    method public java.nio.file.attribute.AclEntry.Builder setFlags(java.nio.file.attribute.AclEntryFlag...);
+    method public java.nio.file.attribute.AclEntry.Builder setPermissions(java.util.Set<java.nio.file.attribute.AclEntryPermission>);
+    method public java.nio.file.attribute.AclEntry.Builder setPermissions(java.nio.file.attribute.AclEntryPermission...);
+    method public java.nio.file.attribute.AclEntry.Builder setPrincipal(java.nio.file.attribute.UserPrincipal);
+    method public java.nio.file.attribute.AclEntry.Builder setType(java.nio.file.attribute.AclEntryType);
+  }
+
+  public enum AclEntryFlag {
+    enum_constant public static final java.nio.file.attribute.AclEntryFlag DIRECTORY_INHERIT;
+    enum_constant public static final java.nio.file.attribute.AclEntryFlag FILE_INHERIT;
+    enum_constant public static final java.nio.file.attribute.AclEntryFlag INHERIT_ONLY;
+    enum_constant public static final java.nio.file.attribute.AclEntryFlag NO_PROPAGATE_INHERIT;
+  }
+
+  public enum AclEntryPermission {
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission APPEND_DATA;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission DELETE_CHILD;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission EXECUTE;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ACL;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_ATTRIBUTES;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_DATA;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission READ_NAMED_ATTRS;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission SYNCHRONIZE;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ACL;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_ATTRIBUTES;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_DATA;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_NAMED_ATTRS;
+    enum_constant public static final java.nio.file.attribute.AclEntryPermission WRITE_OWNER;
+    field public static final java.nio.file.attribute.AclEntryPermission ADD_FILE;
+    field public static final java.nio.file.attribute.AclEntryPermission ADD_SUBDIRECTORY;
+    field public static final java.nio.file.attribute.AclEntryPermission LIST_DIRECTORY;
+  }
+
+  public enum AclEntryType {
+    enum_constant public static final java.nio.file.attribute.AclEntryType ALARM;
+    enum_constant public static final java.nio.file.attribute.AclEntryType ALLOW;
+    enum_constant public static final java.nio.file.attribute.AclEntryType AUDIT;
+    enum_constant public static final java.nio.file.attribute.AclEntryType DENY;
+  }
+
+  public interface AclFileAttributeView extends java.nio.file.attribute.FileOwnerAttributeView {
+    method public java.util.List<java.nio.file.attribute.AclEntry> getAcl() throws java.io.IOException;
+    method public void setAcl(java.util.List<java.nio.file.attribute.AclEntry>) throws java.io.IOException;
+  }
+
+  public interface AttributeView {
+    method public String name();
+  }
+
+  public interface BasicFileAttributeView extends java.nio.file.attribute.FileAttributeView {
+    method public java.nio.file.attribute.BasicFileAttributes readAttributes() throws java.io.IOException;
+    method public void setTimes(java.nio.file.attribute.FileTime, java.nio.file.attribute.FileTime, java.nio.file.attribute.FileTime) throws java.io.IOException;
+  }
+
+  public interface BasicFileAttributes {
+    method public java.nio.file.attribute.FileTime creationTime();
+    method public Object fileKey();
+    method public boolean isDirectory();
+    method public boolean isOther();
+    method public boolean isRegularFile();
+    method public boolean isSymbolicLink();
+    method public java.nio.file.attribute.FileTime lastAccessTime();
+    method public java.nio.file.attribute.FileTime lastModifiedTime();
+    method public long size();
+  }
+
+  public interface DosFileAttributeView extends java.nio.file.attribute.BasicFileAttributeView {
+    method public java.nio.file.attribute.DosFileAttributes readAttributes() throws java.io.IOException;
+    method public void setArchive(boolean) throws java.io.IOException;
+    method public void setHidden(boolean) throws java.io.IOException;
+    method public void setReadOnly(boolean) throws java.io.IOException;
+    method public void setSystem(boolean) throws java.io.IOException;
+  }
+
+  public interface DosFileAttributes extends java.nio.file.attribute.BasicFileAttributes {
+    method public boolean isArchive();
+    method public boolean isHidden();
+    method public boolean isReadOnly();
+    method public boolean isSystem();
+  }
+
+  public interface FileAttribute<T> {
+    method public String name();
+    method public T value();
+  }
+
+  public interface FileAttributeView extends java.nio.file.attribute.AttributeView {
+  }
+
+  public interface FileOwnerAttributeView extends java.nio.file.attribute.FileAttributeView {
+    method public java.nio.file.attribute.UserPrincipal getOwner() throws java.io.IOException;
+    method public void setOwner(java.nio.file.attribute.UserPrincipal) throws java.io.IOException;
+  }
+
+  public interface FileStoreAttributeView extends java.nio.file.attribute.AttributeView {
+  }
+
+  public final class FileTime implements java.lang.Comparable<java.nio.file.attribute.FileTime> {
+    method public int compareTo(java.nio.file.attribute.FileTime);
+    method public static java.nio.file.attribute.FileTime from(long, java.util.concurrent.TimeUnit);
+    method public static java.nio.file.attribute.FileTime from(java.time.Instant);
+    method public static java.nio.file.attribute.FileTime fromMillis(long);
+    method public long to(java.util.concurrent.TimeUnit);
+    method public java.time.Instant toInstant();
+    method public long toMillis();
+  }
+
+  public interface GroupPrincipal extends java.nio.file.attribute.UserPrincipal {
+  }
+
+  public interface PosixFileAttributeView extends java.nio.file.attribute.BasicFileAttributeView java.nio.file.attribute.FileOwnerAttributeView {
+    method public java.nio.file.attribute.PosixFileAttributes readAttributes() throws java.io.IOException;
+    method public void setGroup(java.nio.file.attribute.GroupPrincipal) throws java.io.IOException;
+    method public void setPermissions(java.util.Set<java.nio.file.attribute.PosixFilePermission>) throws java.io.IOException;
+  }
+
+  public interface PosixFileAttributes extends java.nio.file.attribute.BasicFileAttributes {
+    method public java.nio.file.attribute.GroupPrincipal group();
+    method public java.nio.file.attribute.UserPrincipal owner();
+    method public java.util.Set<java.nio.file.attribute.PosixFilePermission> permissions();
+  }
+
+  public enum PosixFilePermission {
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_EXECUTE;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_READ;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission GROUP_WRITE;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_EXECUTE;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_READ;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission OTHERS_WRITE;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_EXECUTE;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_READ;
+    enum_constant public static final java.nio.file.attribute.PosixFilePermission OWNER_WRITE;
+  }
+
+  public final class PosixFilePermissions {
+    method public static java.nio.file.attribute.FileAttribute<java.util.Set<java.nio.file.attribute.PosixFilePermission>> asFileAttribute(java.util.Set<java.nio.file.attribute.PosixFilePermission>);
+    method public static java.util.Set<java.nio.file.attribute.PosixFilePermission> fromString(String);
+    method public static String toString(java.util.Set<java.nio.file.attribute.PosixFilePermission>);
+  }
+
+  public interface UserDefinedFileAttributeView extends java.nio.file.attribute.FileAttributeView {
+    method public void delete(String) throws java.io.IOException;
+    method public java.util.List<java.lang.String> list() throws java.io.IOException;
+    method public int read(String, java.nio.ByteBuffer) throws java.io.IOException;
+    method public int size(String) throws java.io.IOException;
+    method public int write(String, java.nio.ByteBuffer) throws java.io.IOException;
+  }
+
+  public interface UserPrincipal extends java.security.Principal {
+  }
+
+  public abstract class UserPrincipalLookupService {
+    ctor protected UserPrincipalLookupService();
+    method public abstract java.nio.file.attribute.GroupPrincipal lookupPrincipalByGroupName(String) throws java.io.IOException;
+    method public abstract java.nio.file.attribute.UserPrincipal lookupPrincipalByName(String) throws java.io.IOException;
+  }
+
+  public class UserPrincipalNotFoundException extends java.io.IOException {
+    ctor public UserPrincipalNotFoundException(String);
+    method public String getName();
+  }
+
+}
+
+package java.nio.file.spi {
+
+  public abstract class FileSystemProvider {
+    ctor protected FileSystemProvider();
+    method public abstract void checkAccess(java.nio.file.Path, java.nio.file.AccessMode...) throws java.io.IOException;
+    method public abstract void copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+    method public abstract void createDirectory(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public void createLink(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+    method public void createSymbolicLink(java.nio.file.Path, java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public abstract void delete(java.nio.file.Path) throws java.io.IOException;
+    method public boolean deleteIfExists(java.nio.file.Path) throws java.io.IOException;
+    method public abstract <V extends java.nio.file.attribute.FileAttributeView> V getFileAttributeView(java.nio.file.Path, Class<V>, java.nio.file.LinkOption...);
+    method public abstract java.nio.file.FileStore getFileStore(java.nio.file.Path) throws java.io.IOException;
+    method public abstract java.nio.file.FileSystem getFileSystem(java.net.URI);
+    method public abstract java.nio.file.Path getPath(java.net.URI);
+    method public abstract String getScheme();
+    method public static java.util.List<java.nio.file.spi.FileSystemProvider> installedProviders();
+    method public abstract boolean isHidden(java.nio.file.Path) throws java.io.IOException;
+    method public abstract boolean isSameFile(java.nio.file.Path, java.nio.file.Path) throws java.io.IOException;
+    method public abstract void move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) throws java.io.IOException;
+    method public java.nio.channels.AsynchronousFileChannel newAsynchronousFileChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.util.concurrent.ExecutorService, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public abstract java.nio.channels.SeekableByteChannel newByteChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public abstract java.nio.file.DirectoryStream<java.nio.file.Path> newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream.Filter<? super java.nio.file.Path>) throws java.io.IOException;
+    method public java.nio.channels.FileChannel newFileChannel(java.nio.file.Path, java.util.Set<? extends java.nio.file.OpenOption>, java.nio.file.attribute.FileAttribute<?>...) throws java.io.IOException;
+    method public abstract java.nio.file.FileSystem newFileSystem(java.net.URI, java.util.Map<java.lang.String,?>) throws java.io.IOException;
+    method public java.nio.file.FileSystem newFileSystem(java.nio.file.Path, java.util.Map<java.lang.String,?>) throws java.io.IOException;
+    method public java.io.InputStream newInputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public java.io.OutputStream newOutputStream(java.nio.file.Path, java.nio.file.OpenOption...) throws java.io.IOException;
+    method public abstract <A extends java.nio.file.attribute.BasicFileAttributes> A readAttributes(java.nio.file.Path, Class<A>, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public abstract java.util.Map<java.lang.String,java.lang.Object> readAttributes(java.nio.file.Path, String, java.nio.file.LinkOption...) throws java.io.IOException;
+    method public java.nio.file.Path readSymbolicLink(java.nio.file.Path) throws java.io.IOException;
+    method public abstract void setAttribute(java.nio.file.Path, String, Object, java.nio.file.LinkOption...) throws java.io.IOException;
+  }
+
+  public abstract class FileTypeDetector {
+    ctor protected FileTypeDetector();
+    method public abstract String probeContentType(java.nio.file.Path) throws java.io.IOException;
+  }
+
+}
+
+package java.security {
+
+  public final class AccessControlContext {
+    ctor public AccessControlContext(java.security.ProtectionDomain[]);
+    ctor public AccessControlContext(java.security.AccessControlContext, java.security.DomainCombiner);
+    method public void checkPermission(java.security.Permission) throws java.security.AccessControlException;
+    method public java.security.DomainCombiner getDomainCombiner();
+  }
+
+  public class AccessControlException extends java.lang.SecurityException {
+    ctor public AccessControlException(String);
+    ctor public AccessControlException(String, java.security.Permission);
+    method public java.security.Permission getPermission();
+  }
+
+  public final class AccessController {
+    method public static void checkPermission(java.security.Permission) throws java.security.AccessControlException;
+    method public static <T> T doPrivileged(java.security.PrivilegedAction<T>);
+    method public static <T> T doPrivileged(java.security.PrivilegedAction<T>, java.security.AccessControlContext);
+    method public static <T> T doPrivileged(java.security.PrivilegedExceptionAction<T>) throws java.security.PrivilegedActionException;
+    method public static <T> T doPrivileged(java.security.PrivilegedExceptionAction<T>, java.security.AccessControlContext) throws java.security.PrivilegedActionException;
+    method public static <T> T doPrivilegedWithCombiner(java.security.PrivilegedAction<T>);
+    method public static <T> T doPrivilegedWithCombiner(java.security.PrivilegedExceptionAction<T>) throws java.security.PrivilegedActionException;
+    method public static java.security.AccessControlContext getContext();
+  }
+
+  public interface AlgorithmConstraints {
+    method public boolean permits(java.util.Set<java.security.CryptoPrimitive>, String, java.security.AlgorithmParameters);
+    method public boolean permits(java.util.Set<java.security.CryptoPrimitive>, java.security.Key);
+    method public boolean permits(java.util.Set<java.security.CryptoPrimitive>, String, java.security.Key, java.security.AlgorithmParameters);
+  }
+
+  public class AlgorithmParameterGenerator {
+    ctor protected AlgorithmParameterGenerator(java.security.AlgorithmParameterGeneratorSpi, java.security.Provider, String);
+    method public final java.security.AlgorithmParameters generateParameters();
+    method public final String getAlgorithm();
+    method public static java.security.AlgorithmParameterGenerator getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.AlgorithmParameterGenerator getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.AlgorithmParameterGenerator getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public final void init(int);
+    method public final void init(int, java.security.SecureRandom);
+    method public final void init(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
+    method public final void init(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public abstract class AlgorithmParameterGeneratorSpi {
+    ctor public AlgorithmParameterGeneratorSpi();
+    method protected abstract java.security.AlgorithmParameters engineGenerateParameters();
+    method protected abstract void engineInit(int, java.security.SecureRandom);
+    method protected abstract void engineInit(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public class AlgorithmParameters {
+    ctor protected AlgorithmParameters(java.security.AlgorithmParametersSpi, java.security.Provider, String);
+    method public final String getAlgorithm();
+    method public final byte[] getEncoded() throws java.io.IOException;
+    method public final byte[] getEncoded(String) throws java.io.IOException;
+    method public static java.security.AlgorithmParameters getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.AlgorithmParameters getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.AlgorithmParameters getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final <T extends java.security.spec.AlgorithmParameterSpec> T getParameterSpec(Class<T>) throws java.security.spec.InvalidParameterSpecException;
+    method public final java.security.Provider getProvider();
+    method public final void init(java.security.spec.AlgorithmParameterSpec) throws java.security.spec.InvalidParameterSpecException;
+    method public final void init(byte[]) throws java.io.IOException;
+    method public final void init(byte[], String) throws java.io.IOException;
+    method public final String toString();
+  }
+
+  public abstract class AlgorithmParametersSpi {
+    ctor public AlgorithmParametersSpi();
+    method protected abstract byte[] engineGetEncoded() throws java.io.IOException;
+    method protected abstract byte[] engineGetEncoded(String) throws java.io.IOException;
+    method protected abstract <T extends java.security.spec.AlgorithmParameterSpec> T engineGetParameterSpec(Class<T>) throws java.security.spec.InvalidParameterSpecException;
+    method protected abstract void engineInit(java.security.spec.AlgorithmParameterSpec) throws java.security.spec.InvalidParameterSpecException;
+    method protected abstract void engineInit(byte[]) throws java.io.IOException;
+    method protected abstract void engineInit(byte[], String) throws java.io.IOException;
+    method protected abstract String engineToString();
+  }
+
+  public final class AllPermission extends java.security.Permission {
+    ctor public AllPermission();
+    ctor public AllPermission(String, String);
+    method public String getActions();
+    method public boolean implies(java.security.Permission);
+  }
+
+  public abstract class AuthProvider extends java.security.Provider {
+    ctor protected AuthProvider(String, double, String);
+    method public abstract void login(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler) throws javax.security.auth.login.LoginException;
+    method public abstract void logout() throws javax.security.auth.login.LoginException;
+    method public abstract void setCallbackHandler(javax.security.auth.callback.CallbackHandler);
+  }
+
+  public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
+    ctor public BasicPermission(String);
+    ctor public BasicPermission(String, String);
+    method public String getActions();
+    method public boolean implies(java.security.Permission);
+  }
+
+  @Deprecated public interface Certificate {
+    method @Deprecated public void decode(java.io.InputStream) throws java.io.IOException, java.security.KeyException;
+    method @Deprecated public void encode(java.io.OutputStream) throws java.io.IOException, java.security.KeyException;
+    method @Deprecated public String getFormat();
+    method @Deprecated public java.security.Principal getGuarantor();
+    method @Deprecated public java.security.Principal getPrincipal();
+    method @Deprecated public java.security.PublicKey getPublicKey();
+    method @Deprecated public String toString(boolean);
+  }
+
+  public final class CodeSigner implements java.io.Serializable {
+    ctor public CodeSigner(java.security.cert.CertPath, java.security.Timestamp);
+    method public java.security.cert.CertPath getSignerCertPath();
+    method public java.security.Timestamp getTimestamp();
+  }
+
+  public class CodeSource implements java.io.Serializable {
+    ctor public CodeSource(java.net.URL, java.security.cert.Certificate[]);
+    ctor public CodeSource(java.net.URL, java.security.CodeSigner[]);
+    method public final java.security.cert.Certificate[] getCertificates();
+    method public final java.security.CodeSigner[] getCodeSigners();
+    method public final java.net.URL getLocation();
+    method public boolean implies(java.security.CodeSource);
+  }
+
+  public enum CryptoPrimitive {
+    enum_constant public static final java.security.CryptoPrimitive BLOCK_CIPHER;
+    enum_constant public static final java.security.CryptoPrimitive KEY_AGREEMENT;
+    enum_constant public static final java.security.CryptoPrimitive KEY_ENCAPSULATION;
+    enum_constant public static final java.security.CryptoPrimitive KEY_WRAP;
+    enum_constant public static final java.security.CryptoPrimitive MAC;
+    enum_constant public static final java.security.CryptoPrimitive MESSAGE_DIGEST;
+    enum_constant public static final java.security.CryptoPrimitive PUBLIC_KEY_ENCRYPTION;
+    enum_constant public static final java.security.CryptoPrimitive SECURE_RANDOM;
+    enum_constant public static final java.security.CryptoPrimitive SIGNATURE;
+    enum_constant public static final java.security.CryptoPrimitive STREAM_CIPHER;
+  }
+
+  public class DigestException extends java.security.GeneralSecurityException {
+    ctor public DigestException();
+    ctor public DigestException(String);
+    ctor public DigestException(String, Throwable);
+    ctor public DigestException(Throwable);
+  }
+
+  public class DigestInputStream extends java.io.FilterInputStream {
+    ctor public DigestInputStream(java.io.InputStream, java.security.MessageDigest);
+    method public java.security.MessageDigest getMessageDigest();
+    method public void on(boolean);
+    method public void setMessageDigest(java.security.MessageDigest);
+    field protected java.security.MessageDigest digest;
+  }
+
+  public class DigestOutputStream extends java.io.FilterOutputStream {
+    ctor public DigestOutputStream(java.io.OutputStream, java.security.MessageDigest);
+    method public java.security.MessageDigest getMessageDigest();
+    method public void on(boolean);
+    method public void setMessageDigest(java.security.MessageDigest);
+    field protected java.security.MessageDigest digest;
+  }
+
+  public interface DomainCombiner {
+    method public java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]);
+  }
+
+  public final class DomainLoadStoreParameter implements java.security.KeyStore.LoadStoreParameter {
+    ctor public DomainLoadStoreParameter(java.net.URI, java.util.Map<java.lang.String,java.security.KeyStore.ProtectionParameter>);
+    method public java.net.URI getConfiguration();
+    method public java.security.KeyStore.ProtectionParameter getProtectionParameter();
+    method public java.util.Map<java.lang.String,java.security.KeyStore.ProtectionParameter> getProtectionParams();
+  }
+
+  public class GeneralSecurityException extends java.lang.Exception {
+    ctor public GeneralSecurityException();
+    ctor public GeneralSecurityException(String);
+    ctor public GeneralSecurityException(String, Throwable);
+    ctor public GeneralSecurityException(Throwable);
+  }
+
+  public interface Guard {
+    method public void checkGuard(Object) throws java.lang.SecurityException;
+  }
+
+  public class GuardedObject implements java.io.Serializable {
+    ctor public GuardedObject(Object, java.security.Guard);
+    method public Object getObject() throws java.lang.SecurityException;
+  }
+
+  @Deprecated public abstract class Identity implements java.security.Principal java.io.Serializable {
+    ctor @Deprecated protected Identity();
+    ctor @Deprecated public Identity(String, java.security.IdentityScope) throws java.security.KeyManagementException;
+    ctor @Deprecated public Identity(String);
+    method @Deprecated public void addCertificate(java.security.Certificate) throws java.security.KeyManagementException;
+    method @Deprecated public java.security.Certificate[] certificates();
+    method @Deprecated public final boolean equals(Object);
+    method @Deprecated public String getInfo();
+    method @Deprecated public final String getName();
+    method @Deprecated public java.security.PublicKey getPublicKey();
+    method @Deprecated public final java.security.IdentityScope getScope();
+    method @Deprecated protected boolean identityEquals(java.security.Identity);
+    method @Deprecated public void removeCertificate(java.security.Certificate) throws java.security.KeyManagementException;
+    method @Deprecated public void setInfo(String);
+    method @Deprecated public void setPublicKey(java.security.PublicKey) throws java.security.KeyManagementException;
+    method @Deprecated public String toString(boolean);
+  }
+
+  @Deprecated public abstract class IdentityScope extends java.security.Identity {
+    ctor @Deprecated protected IdentityScope();
+    ctor @Deprecated public IdentityScope(String);
+    ctor @Deprecated public IdentityScope(String, java.security.IdentityScope) throws java.security.KeyManagementException;
+    method @Deprecated public abstract void addIdentity(java.security.Identity) throws java.security.KeyManagementException;
+    method @Deprecated public abstract java.security.Identity getIdentity(String);
+    method @Deprecated public java.security.Identity getIdentity(java.security.Principal);
+    method @Deprecated public abstract java.security.Identity getIdentity(java.security.PublicKey);
+    method @Deprecated public static java.security.IdentityScope getSystemScope();
+    method @Deprecated public abstract java.util.Enumeration<java.security.Identity> identities();
+    method @Deprecated public abstract void removeIdentity(java.security.Identity) throws java.security.KeyManagementException;
+    method @Deprecated protected static void setSystemScope(java.security.IdentityScope);
+    method @Deprecated public abstract int size();
+  }
+
+  public class InvalidAlgorithmParameterException extends java.security.GeneralSecurityException {
+    ctor public InvalidAlgorithmParameterException();
+    ctor public InvalidAlgorithmParameterException(String);
+    ctor public InvalidAlgorithmParameterException(String, Throwable);
+    ctor public InvalidAlgorithmParameterException(Throwable);
+  }
+
+  public class InvalidKeyException extends java.security.KeyException {
+    ctor public InvalidKeyException();
+    ctor public InvalidKeyException(String);
+    ctor public InvalidKeyException(String, Throwable);
+    ctor public InvalidKeyException(Throwable);
+  }
+
+  public class InvalidParameterException extends java.lang.IllegalArgumentException {
+    ctor public InvalidParameterException();
+    ctor public InvalidParameterException(String);
+  }
+
+  public interface Key extends java.io.Serializable {
+    method public String getAlgorithm();
+    method public byte[] getEncoded();
+    method public String getFormat();
+    field public static final long serialVersionUID = 6603384152749567654L; // 0x5ba3eee69414eea6L
+  }
+
+  public class KeyException extends java.security.GeneralSecurityException {
+    ctor public KeyException();
+    ctor public KeyException(String);
+    ctor public KeyException(String, Throwable);
+    ctor public KeyException(Throwable);
+  }
+
+  public class KeyFactory {
+    ctor protected KeyFactory(java.security.KeyFactorySpi, java.security.Provider, String);
+    method public final java.security.PrivateKey generatePrivate(java.security.spec.KeySpec) throws java.security.spec.InvalidKeySpecException;
+    method public final java.security.PublicKey generatePublic(java.security.spec.KeySpec) throws java.security.spec.InvalidKeySpecException;
+    method public final String getAlgorithm();
+    method public static java.security.KeyFactory getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.KeyFactory getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.KeyFactory getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final <T extends java.security.spec.KeySpec> T getKeySpec(java.security.Key, Class<T>) throws java.security.spec.InvalidKeySpecException;
+    method public final java.security.Provider getProvider();
+    method public final java.security.Key translateKey(java.security.Key) throws java.security.InvalidKeyException;
+  }
+
+  public abstract class KeyFactorySpi {
+    ctor public KeyFactorySpi();
+    method protected abstract java.security.PrivateKey engineGeneratePrivate(java.security.spec.KeySpec) throws java.security.spec.InvalidKeySpecException;
+    method protected abstract java.security.PublicKey engineGeneratePublic(java.security.spec.KeySpec) throws java.security.spec.InvalidKeySpecException;
+    method protected abstract <T extends java.security.spec.KeySpec> T engineGetKeySpec(java.security.Key, Class<T>) throws java.security.spec.InvalidKeySpecException;
+    method protected abstract java.security.Key engineTranslateKey(java.security.Key) throws java.security.InvalidKeyException;
+  }
+
+  public class KeyManagementException extends java.security.KeyException {
+    ctor public KeyManagementException();
+    ctor public KeyManagementException(String);
+    ctor public KeyManagementException(String, Throwable);
+    ctor public KeyManagementException(Throwable);
+  }
+
+  public final class KeyPair implements java.io.Serializable {
+    ctor public KeyPair(java.security.PublicKey, java.security.PrivateKey);
+    method public java.security.PrivateKey getPrivate();
+    method public java.security.PublicKey getPublic();
+  }
+
+  public abstract class KeyPairGenerator extends java.security.KeyPairGeneratorSpi {
+    ctor protected KeyPairGenerator(String);
+    method public final java.security.KeyPair genKeyPair();
+    method public java.security.KeyPair generateKeyPair();
+    method public String getAlgorithm();
+    method public static java.security.KeyPairGenerator getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.KeyPairGenerator getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.KeyPairGenerator getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public void initialize(int);
+    method public void initialize(int, java.security.SecureRandom);
+    method public void initialize(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public abstract class KeyPairGeneratorSpi {
+    ctor public KeyPairGeneratorSpi();
+    method public abstract java.security.KeyPair generateKeyPair();
+    method public abstract void initialize(int, java.security.SecureRandom);
+    method public void initialize(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public class KeyRep implements java.io.Serializable {
+    ctor public KeyRep(java.security.KeyRep.Type, String, String, byte[]);
+    method protected Object readResolve() throws java.io.ObjectStreamException;
+  }
+
+  public enum KeyRep.Type {
+    enum_constant public static final java.security.KeyRep.Type PRIVATE;
+    enum_constant public static final java.security.KeyRep.Type PUBLIC;
+    enum_constant public static final java.security.KeyRep.Type SECRET;
+  }
+
+  public class KeyStore {
+    ctor protected KeyStore(java.security.KeyStoreSpi, java.security.Provider, String);
+    method public final java.util.Enumeration<java.lang.String> aliases() throws java.security.KeyStoreException;
+    method public final boolean containsAlias(String) throws java.security.KeyStoreException;
+    method public final void deleteEntry(String) throws java.security.KeyStoreException;
+    method public final boolean entryInstanceOf(String, Class<? extends java.security.KeyStore.Entry>) throws java.security.KeyStoreException;
+    method public final java.security.cert.Certificate getCertificate(String) throws java.security.KeyStoreException;
+    method public final String getCertificateAlias(java.security.cert.Certificate) throws java.security.KeyStoreException;
+    method public final java.security.cert.Certificate[] getCertificateChain(String) throws java.security.KeyStoreException;
+    method public final java.util.Date getCreationDate(String) throws java.security.KeyStoreException;
+    method public static final String getDefaultType();
+    method public final java.security.KeyStore.Entry getEntry(String, java.security.KeyStore.ProtectionParameter) throws java.security.KeyStoreException, java.security.NoSuchAlgorithmException, java.security.UnrecoverableEntryException;
+    method public static java.security.KeyStore getInstance(String) throws java.security.KeyStoreException;
+    method public static java.security.KeyStore getInstance(String, String) throws java.security.KeyStoreException, java.security.NoSuchProviderException;
+    method public static java.security.KeyStore getInstance(String, java.security.Provider) throws java.security.KeyStoreException;
+    method public final java.security.Key getKey(String, char[]) throws java.security.KeyStoreException, java.security.NoSuchAlgorithmException, java.security.UnrecoverableKeyException;
+    method public final java.security.Provider getProvider();
+    method public final String getType();
+    method public final boolean isCertificateEntry(String) throws java.security.KeyStoreException;
+    method public final boolean isKeyEntry(String) throws java.security.KeyStoreException;
+    method public final void load(java.io.InputStream, char[]) throws java.security.cert.CertificateException, java.io.IOException, java.security.NoSuchAlgorithmException;
+    method public final void load(java.security.KeyStore.LoadStoreParameter) throws java.security.cert.CertificateException, java.io.IOException, java.security.NoSuchAlgorithmException;
+    method public final void setCertificateEntry(String, java.security.cert.Certificate) throws java.security.KeyStoreException;
+    method public final void setEntry(String, java.security.KeyStore.Entry, java.security.KeyStore.ProtectionParameter) throws java.security.KeyStoreException;
+    method public final void setKeyEntry(String, java.security.Key, char[], java.security.cert.Certificate[]) throws java.security.KeyStoreException;
+    method public final void setKeyEntry(String, byte[], java.security.cert.Certificate[]) throws java.security.KeyStoreException;
+    method public final int size() throws java.security.KeyStoreException;
+    method public final void store(java.io.OutputStream, char[]) throws java.security.cert.CertificateException, java.io.IOException, java.security.KeyStoreException, java.security.NoSuchAlgorithmException;
+    method public final void store(java.security.KeyStore.LoadStoreParameter) throws java.security.cert.CertificateException, java.io.IOException, java.security.KeyStoreException, java.security.NoSuchAlgorithmException;
+  }
+
+  public abstract static class KeyStore.Builder {
+    ctor protected KeyStore.Builder();
+    method public abstract java.security.KeyStore getKeyStore() throws java.security.KeyStoreException;
+    method public abstract java.security.KeyStore.ProtectionParameter getProtectionParameter(String) throws java.security.KeyStoreException;
+    method public static java.security.KeyStore.Builder newInstance(java.security.KeyStore, java.security.KeyStore.ProtectionParameter);
+    method public static java.security.KeyStore.Builder newInstance(String, java.security.Provider, java.io.File, java.security.KeyStore.ProtectionParameter);
+    method public static java.security.KeyStore.Builder newInstance(String, java.security.Provider, java.security.KeyStore.ProtectionParameter);
+  }
+
+  public static class KeyStore.CallbackHandlerProtection implements java.security.KeyStore.ProtectionParameter {
+    ctor public KeyStore.CallbackHandlerProtection(javax.security.auth.callback.CallbackHandler);
+    method public javax.security.auth.callback.CallbackHandler getCallbackHandler();
+  }
+
+  public static interface KeyStore.Entry {
+    method public default java.util.Set<java.security.KeyStore.Entry.Attribute> getAttributes();
+  }
+
+  public static interface KeyStore.Entry.Attribute {
+    method public String getName();
+    method public String getValue();
+  }
+
+  public static interface KeyStore.LoadStoreParameter {
+    method public java.security.KeyStore.ProtectionParameter getProtectionParameter();
+  }
+
+  public static class KeyStore.PasswordProtection implements javax.security.auth.Destroyable java.security.KeyStore.ProtectionParameter {
+    ctor public KeyStore.PasswordProtection(char[]);
+    ctor public KeyStore.PasswordProtection(char[], String, java.security.spec.AlgorithmParameterSpec);
+    method public char[] getPassword();
+    method public String getProtectionAlgorithm();
+    method public java.security.spec.AlgorithmParameterSpec getProtectionParameters();
+  }
+
+  public static final class KeyStore.PrivateKeyEntry implements java.security.KeyStore.Entry {
+    ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[]);
+    ctor public KeyStore.PrivateKeyEntry(java.security.PrivateKey, java.security.cert.Certificate[], java.util.Set<java.security.KeyStore.Entry.Attribute>);
+    method public java.security.cert.Certificate getCertificate();
+    method public java.security.cert.Certificate[] getCertificateChain();
+    method public java.security.PrivateKey getPrivateKey();
+  }
+
+  public static interface KeyStore.ProtectionParameter {
+  }
+
+  public static final class KeyStore.SecretKeyEntry implements java.security.KeyStore.Entry {
+    ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey);
+    ctor public KeyStore.SecretKeyEntry(javax.crypto.SecretKey, java.util.Set<java.security.KeyStore.Entry.Attribute>);
+    method public javax.crypto.SecretKey getSecretKey();
+  }
+
+  public static final class KeyStore.TrustedCertificateEntry implements java.security.KeyStore.Entry {
+    ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate);
+    ctor public KeyStore.TrustedCertificateEntry(java.security.cert.Certificate, java.util.Set<java.security.KeyStore.Entry.Attribute>);
+    method public java.security.cert.Certificate getTrustedCertificate();
+  }
+
+  public class KeyStoreException extends java.security.GeneralSecurityException {
+    ctor public KeyStoreException();
+    ctor public KeyStoreException(String);
+    ctor public KeyStoreException(String, Throwable);
+    ctor public KeyStoreException(Throwable);
+  }
+
+  public abstract class KeyStoreSpi {
+    ctor public KeyStoreSpi();
+    method public abstract java.util.Enumeration<java.lang.String> engineAliases();
+    method public abstract boolean engineContainsAlias(String);
+    method public abstract void engineDeleteEntry(String) throws java.security.KeyStoreException;
+    method public boolean engineEntryInstanceOf(String, Class<? extends java.security.KeyStore.Entry>);
+    method public abstract java.security.cert.Certificate engineGetCertificate(String);
+    method public abstract String engineGetCertificateAlias(java.security.cert.Certificate);
+    method public abstract java.security.cert.Certificate[] engineGetCertificateChain(String);
+    method public abstract java.util.Date engineGetCreationDate(String);
+    method public java.security.KeyStore.Entry engineGetEntry(String, java.security.KeyStore.ProtectionParameter) throws java.security.KeyStoreException, java.security.NoSuchAlgorithmException, java.security.UnrecoverableEntryException;
+    method public abstract java.security.Key engineGetKey(String, char[]) throws java.security.NoSuchAlgorithmException, java.security.UnrecoverableKeyException;
+    method public abstract boolean engineIsCertificateEntry(String);
+    method public abstract boolean engineIsKeyEntry(String);
+    method public abstract void engineLoad(java.io.InputStream, char[]) throws java.security.cert.CertificateException, java.io.IOException, java.security.NoSuchAlgorithmException;
+    method public void engineLoad(java.security.KeyStore.LoadStoreParameter) throws java.security.cert.CertificateException, java.io.IOException, java.security.NoSuchAlgorithmException;
+    method public abstract void engineSetCertificateEntry(String, java.security.cert.Certificate) throws java.security.KeyStoreException;
+    method public void engineSetEntry(String, java.security.KeyStore.Entry, java.security.KeyStore.ProtectionParameter) throws java.security.KeyStoreException;
+    method public abstract void engineSetKeyEntry(String, java.security.Key, char[], java.security.cert.Certificate[]) throws java.security.KeyStoreException;
+    method public abstract void engineSetKeyEntry(String, byte[], java.security.cert.Certificate[]) throws java.security.KeyStoreException;
+    method public abstract int engineSize();
+    method public abstract void engineStore(java.io.OutputStream, char[]) throws java.security.cert.CertificateException, java.io.IOException, java.security.NoSuchAlgorithmException;
+    method public void engineStore(java.security.KeyStore.LoadStoreParameter) throws java.security.cert.CertificateException, java.io.IOException, java.security.NoSuchAlgorithmException;
+  }
+
+  public abstract class MessageDigest extends java.security.MessageDigestSpi {
+    ctor protected MessageDigest(@NonNull String);
+    method @NonNull public byte[] digest();
+    method public int digest(@NonNull byte[], int, int) throws java.security.DigestException;
+    method @NonNull public byte[] digest(@NonNull byte[]);
+    method @NonNull public final String getAlgorithm();
+    method public final int getDigestLength();
+    method @NonNull public static java.security.MessageDigest getInstance(@NonNull String) throws java.security.NoSuchAlgorithmException;
+    method @NonNull public static java.security.MessageDigest getInstance(@NonNull String, @NonNull String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method @NonNull public static java.security.MessageDigest getInstance(@NonNull String, @NonNull java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method @NonNull public final java.security.Provider getProvider();
+    method public static boolean isEqual(@Nullable byte[], @Nullable byte[]);
+    method public void reset();
+    method public void update(byte);
+    method public void update(@NonNull byte[], int, int);
+    method public void update(@NonNull byte[]);
+    method public final void update(@NonNull java.nio.ByteBuffer);
+  }
+
+  public abstract class MessageDigestSpi {
+    ctor public MessageDigestSpi();
+    method public Object clone() throws java.lang.CloneNotSupportedException;
+    method protected abstract byte[] engineDigest();
+    method protected int engineDigest(byte[], int, int) throws java.security.DigestException;
+    method protected int engineGetDigestLength();
+    method protected abstract void engineReset();
+    method protected abstract void engineUpdate(byte);
+    method protected abstract void engineUpdate(byte[], int, int);
+    method protected void engineUpdate(java.nio.ByteBuffer);
+  }
+
+  public class NoSuchAlgorithmException extends java.security.GeneralSecurityException {
+    ctor public NoSuchAlgorithmException();
+    ctor public NoSuchAlgorithmException(String);
+    ctor public NoSuchAlgorithmException(String, Throwable);
+    ctor public NoSuchAlgorithmException(Throwable);
+  }
+
+  public class NoSuchProviderException extends java.security.GeneralSecurityException {
+    ctor public NoSuchProviderException();
+    ctor public NoSuchProviderException(String);
+  }
+
+  public final class PKCS12Attribute implements java.security.KeyStore.Entry.Attribute {
+    ctor public PKCS12Attribute(String, String);
+    ctor public PKCS12Attribute(byte[]);
+    method public byte[] getEncoded();
+    method public String getName();
+    method public String getValue();
+  }
+
+  public abstract class Permission implements java.security.Guard java.io.Serializable {
+    ctor public Permission(String);
+    method public void checkGuard(Object) throws java.lang.SecurityException;
+    method public abstract String getActions();
+    method public final String getName();
+    method public abstract boolean implies(java.security.Permission);
+    method public java.security.PermissionCollection newPermissionCollection();
+  }
+
+  public abstract class PermissionCollection implements java.io.Serializable {
+    ctor public PermissionCollection();
+    method public abstract void add(java.security.Permission);
+    method public abstract java.util.Enumeration<java.security.Permission> elements();
+    method public abstract boolean implies(java.security.Permission);
+    method public boolean isReadOnly();
+    method public void setReadOnly();
+  }
+
+  public final class Permissions extends java.security.PermissionCollection implements java.io.Serializable {
+    ctor public Permissions();
+    method public void add(java.security.Permission);
+    method public java.util.Enumeration<java.security.Permission> elements();
+    method public boolean implies(java.security.Permission);
+  }
+
+  public abstract class Policy {
+    ctor public Policy();
+    method public static java.security.Policy getInstance(String, java.security.Policy.Parameters) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.Policy getInstance(String, java.security.Policy.Parameters, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.Policy getInstance(String, java.security.Policy.Parameters, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public java.security.Policy.Parameters getParameters();
+    method public java.security.PermissionCollection getPermissions(java.security.CodeSource);
+    method public java.security.PermissionCollection getPermissions(java.security.ProtectionDomain);
+    method public static java.security.Policy getPolicy();
+    method public java.security.Provider getProvider();
+    method public String getType();
+    method public boolean implies(java.security.ProtectionDomain, java.security.Permission);
+    method public void refresh();
+    method public static void setPolicy(java.security.Policy);
+    field public static final java.security.PermissionCollection UNSUPPORTED_EMPTY_COLLECTION;
+  }
+
+  public static interface Policy.Parameters {
+  }
+
+  public abstract class PolicySpi {
+    ctor public PolicySpi();
+    method protected java.security.PermissionCollection engineGetPermissions(java.security.CodeSource);
+    method protected java.security.PermissionCollection engineGetPermissions(java.security.ProtectionDomain);
+    method protected abstract boolean engineImplies(java.security.ProtectionDomain, java.security.Permission);
+    method protected void engineRefresh();
+  }
+
+  public interface Principal {
+    method public boolean equals(Object);
+    method public String getName();
+    method public int hashCode();
+    method public default boolean implies(javax.security.auth.Subject);
+    method public String toString();
+  }
+
+  public interface PrivateKey extends java.security.Key javax.security.auth.Destroyable {
+    field public static final long serialVersionUID = 6034044314589513430L; // 0x53bd3b559a12c6d6L
+  }
+
+  public interface PrivilegedAction<T> {
+    method public T run();
+  }
+
+  public class PrivilegedActionException extends java.lang.Exception {
+    ctor public PrivilegedActionException(Exception);
+    method public Exception getException();
+  }
+
+  public interface PrivilegedExceptionAction<T> {
+    method public T run() throws java.lang.Exception;
+  }
+
+  public class ProtectionDomain {
+    ctor public ProtectionDomain(java.security.CodeSource, java.security.PermissionCollection);
+    ctor public ProtectionDomain(java.security.CodeSource, java.security.PermissionCollection, ClassLoader, java.security.Principal[]);
+    method public final ClassLoader getClassLoader();
+    method public final java.security.CodeSource getCodeSource();
+    method public final java.security.PermissionCollection getPermissions();
+    method public final java.security.Principal[] getPrincipals();
+    method public boolean implies(java.security.Permission);
+  }
+
+  public abstract class Provider extends java.util.Properties {
+    ctor protected Provider(String, double, String);
+    method public Object compute(Object, java.util.function.BiFunction<? super java.lang.Object,? super java.lang.Object,?>);
+    method public Object computeIfAbsent(Object, java.util.function.Function<? super java.lang.Object,?>);
+    method public Object computeIfPresent(Object, java.util.function.BiFunction<? super java.lang.Object,? super java.lang.Object,?>);
+    method public java.util.Enumeration<java.lang.Object> elements();
+    method public java.util.Set<java.util.Map.Entry<java.lang.Object,java.lang.Object>> entrySet();
+    method public void forEach(java.util.function.BiConsumer<? super java.lang.Object,? super java.lang.Object>);
+    method public Object get(Object);
+    method public String getInfo();
+    method public String getName();
+    method public Object getOrDefault(Object, Object);
+    method public java.security.Provider.Service getService(String, String);
+    method public java.util.Set<java.security.Provider.Service> getServices();
+    method public double getVersion();
+    method public java.util.Set<java.lang.Object> keySet();
+    method public java.util.Enumeration<java.lang.Object> keys();
+    method public Object merge(Object, Object, java.util.function.BiFunction<? super java.lang.Object,? super java.lang.Object,?>);
+    method public Object put(Object, Object);
+    method public void putAll(java.util.Map<?,?>);
+    method public Object putIfAbsent(Object, Object);
+    method protected void putService(java.security.Provider.Service);
+    method public Object remove(Object);
+    method protected void removeService(java.security.Provider.Service);
+    method public boolean replace(Object, Object, Object);
+    method public Object replace(Object, Object);
+    method public void replaceAll(java.util.function.BiFunction<? super java.lang.Object,? super java.lang.Object,?>);
+    method public java.util.Collection<java.lang.Object> values();
+  }
+
+  public static class Provider.Service {
+    ctor public Provider.Service(java.security.Provider, String, String, String, java.util.List<java.lang.String>, java.util.Map<java.lang.String,java.lang.String>);
+    method public final String getAlgorithm();
+    method public final String getAttribute(String);
+    method public final String getClassName();
+    method public final java.security.Provider getProvider();
+    method public final String getType();
+    method public Object newInstance(Object) throws java.security.NoSuchAlgorithmException;
+    method public boolean supportsParameter(Object);
+  }
+
+  public class ProviderException extends java.lang.RuntimeException {
+    ctor public ProviderException();
+    ctor public ProviderException(String);
+    ctor public ProviderException(String, Throwable);
+    ctor public ProviderException(Throwable);
+  }
+
+  public interface PublicKey extends java.security.Key {
+    field public static final long serialVersionUID = 7187392471159151072L; // 0x63bebf5f40c219e0L
+  }
+
+  public class SecureClassLoader extends java.lang.ClassLoader {
+    ctor protected SecureClassLoader(ClassLoader);
+    ctor protected SecureClassLoader();
+    method protected final Class<?> defineClass(String, byte[], int, int, java.security.CodeSource);
+    method protected final Class<?> defineClass(String, java.nio.ByteBuffer, java.security.CodeSource);
+    method protected java.security.PermissionCollection getPermissions(java.security.CodeSource);
+  }
+
+  public class SecureRandom extends java.util.Random {
+    ctor public SecureRandom();
+    ctor public SecureRandom(byte[]);
+    ctor protected SecureRandom(java.security.SecureRandomSpi, java.security.Provider);
+    method public byte[] generateSeed(int);
+    method public String getAlgorithm();
+    method public static java.security.SecureRandom getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.SecureRandom getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.SecureRandom getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.SecureRandom getInstanceStrong() throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public static byte[] getSeed(int);
+    method protected final int next(int);
+    method public void setSeed(byte[]);
+  }
+
+  public abstract class SecureRandomSpi implements java.io.Serializable {
+    ctor public SecureRandomSpi();
+    method protected abstract byte[] engineGenerateSeed(int);
+    method protected abstract void engineNextBytes(byte[]);
+    method protected abstract void engineSetSeed(byte[]);
+  }
+
+  public final class Security {
+    method public static int addProvider(java.security.Provider);
+    method @Deprecated public static String getAlgorithmProperty(String, String);
+    method public static java.util.Set<java.lang.String> getAlgorithms(String);
+    method public static String getProperty(String);
+    method public static java.security.Provider getProvider(String);
+    method public static java.security.Provider[] getProviders();
+    method public static java.security.Provider[] getProviders(String);
+    method public static java.security.Provider[] getProviders(java.util.Map<java.lang.String,java.lang.String>);
+    method public static int insertProviderAt(java.security.Provider, int);
+    method public static void removeProvider(String);
+    method public static void setProperty(String, String);
+  }
+
+  public final class SecurityPermission extends java.security.BasicPermission {
+    ctor public SecurityPermission(String);
+    ctor public SecurityPermission(String, String);
+  }
+
+  public abstract class Signature extends java.security.SignatureSpi {
+    ctor protected Signature(String);
+    method public final String getAlgorithm();
+    method public static java.security.Signature getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.Signature getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.Signature getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method @Deprecated public final Object getParameter(String) throws java.security.InvalidParameterException;
+    method public final java.security.AlgorithmParameters getParameters();
+    method public final java.security.Provider getProvider();
+    method public final void initSign(java.security.PrivateKey) throws java.security.InvalidKeyException;
+    method public final void initSign(java.security.PrivateKey, java.security.SecureRandom) throws java.security.InvalidKeyException;
+    method public final void initVerify(java.security.PublicKey) throws java.security.InvalidKeyException;
+    method public final void initVerify(java.security.cert.Certificate) throws java.security.InvalidKeyException;
+    method @Deprecated public final void setParameter(String, Object) throws java.security.InvalidParameterException;
+    method public final void setParameter(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
+    method public final byte[] sign() throws java.security.SignatureException;
+    method public final int sign(byte[], int, int) throws java.security.SignatureException;
+    method public final void update(byte) throws java.security.SignatureException;
+    method public final void update(byte[]) throws java.security.SignatureException;
+    method public final void update(byte[], int, int) throws java.security.SignatureException;
+    method public final void update(java.nio.ByteBuffer) throws java.security.SignatureException;
+    method public final boolean verify(byte[]) throws java.security.SignatureException;
+    method public final boolean verify(byte[], int, int) throws java.security.SignatureException;
+    field protected static final int SIGN = 2; // 0x2
+    field protected static final int UNINITIALIZED = 0; // 0x0
+    field protected static final int VERIFY = 3; // 0x3
+    field protected int state;
+  }
+
+  public class SignatureException extends java.security.GeneralSecurityException {
+    ctor public SignatureException();
+    ctor public SignatureException(String);
+    ctor public SignatureException(String, Throwable);
+    ctor public SignatureException(Throwable);
+  }
+
+  public abstract class SignatureSpi {
+    ctor public SignatureSpi();
+    method public Object clone() throws java.lang.CloneNotSupportedException;
+    method @Deprecated protected abstract Object engineGetParameter(String) throws java.security.InvalidParameterException;
+    method protected java.security.AlgorithmParameters engineGetParameters();
+    method protected abstract void engineInitSign(java.security.PrivateKey) throws java.security.InvalidKeyException;
+    method protected void engineInitSign(java.security.PrivateKey, java.security.SecureRandom) throws java.security.InvalidKeyException;
+    method protected abstract void engineInitVerify(java.security.PublicKey) throws java.security.InvalidKeyException;
+    method @Deprecated protected abstract void engineSetParameter(String, Object) throws java.security.InvalidParameterException;
+    method protected void engineSetParameter(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
+    method protected abstract byte[] engineSign() throws java.security.SignatureException;
+    method protected int engineSign(byte[], int, int) throws java.security.SignatureException;
+    method protected abstract void engineUpdate(byte) throws java.security.SignatureException;
+    method protected abstract void engineUpdate(byte[], int, int) throws java.security.SignatureException;
+    method protected void engineUpdate(java.nio.ByteBuffer);
+    method protected abstract boolean engineVerify(byte[]) throws java.security.SignatureException;
+    method protected boolean engineVerify(byte[], int, int) throws java.security.SignatureException;
+    field protected java.security.SecureRandom appRandom;
+  }
+
+  public final class SignedObject implements java.io.Serializable {
+    ctor public SignedObject(java.io.Serializable, java.security.PrivateKey, java.security.Signature) throws java.io.IOException, java.security.InvalidKeyException, java.security.SignatureException;
+    method public String getAlgorithm();
+    method public Object getObject() throws java.lang.ClassNotFoundException, java.io.IOException;
+    method public byte[] getSignature();
+    method public boolean verify(java.security.PublicKey, java.security.Signature) throws java.security.InvalidKeyException, java.security.SignatureException;
+  }
+
+  @Deprecated public abstract class Signer extends java.security.Identity {
+    ctor @Deprecated protected Signer();
+    ctor @Deprecated public Signer(String);
+    ctor @Deprecated public Signer(String, java.security.IdentityScope) throws java.security.KeyManagementException;
+    method @Deprecated public java.security.PrivateKey getPrivateKey();
+    method @Deprecated public final void setKeyPair(java.security.KeyPair) throws java.security.InvalidParameterException, java.security.KeyException;
+  }
+
+  public final class Timestamp implements java.io.Serializable {
+    ctor public Timestamp(java.util.Date, java.security.cert.CertPath);
+    method public java.security.cert.CertPath getSignerCertPath();
+    method public java.util.Date getTimestamp();
+  }
+
+  public class UnrecoverableEntryException extends java.security.GeneralSecurityException {
+    ctor public UnrecoverableEntryException();
+    ctor public UnrecoverableEntryException(String);
+  }
+
+  public class UnrecoverableKeyException extends java.security.UnrecoverableEntryException {
+    ctor public UnrecoverableKeyException();
+    ctor public UnrecoverableKeyException(String);
+  }
+
+  public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
+    ctor public UnresolvedPermission(String, String, String, java.security.cert.Certificate[]);
+    method public String getActions();
+    method public String getUnresolvedActions();
+    method public java.security.cert.Certificate[] getUnresolvedCerts();
+    method public String getUnresolvedName();
+    method public String getUnresolvedType();
+    method public boolean implies(java.security.Permission);
+  }
+
+}
+
+package java.security.acl {
+
+  public interface Acl extends java.security.acl.Owner {
+    method public boolean addEntry(java.security.Principal, java.security.acl.AclEntry) throws java.security.acl.NotOwnerException;
+    method public boolean checkPermission(java.security.Principal, java.security.acl.Permission);
+    method public java.util.Enumeration<java.security.acl.AclEntry> entries();
+    method public String getName();
+    method public java.util.Enumeration<java.security.acl.Permission> getPermissions(java.security.Principal);
+    method public boolean removeEntry(java.security.Principal, java.security.acl.AclEntry) throws java.security.acl.NotOwnerException;
+    method public void setName(java.security.Principal, String) throws java.security.acl.NotOwnerException;
+    method public String toString();
+  }
+
+  public interface AclEntry extends java.lang.Cloneable {
+    method public boolean addPermission(java.security.acl.Permission);
+    method public boolean checkPermission(java.security.acl.Permission);
+    method public Object clone();
+    method public java.security.Principal getPrincipal();
+    method public boolean isNegative();
+    method public java.util.Enumeration<java.security.acl.Permission> permissions();
+    method public boolean removePermission(java.security.acl.Permission);
+    method public void setNegativePermissions();
+    method public boolean setPrincipal(java.security.Principal);
+    method public String toString();
+  }
+
+  public class AclNotFoundException extends java.lang.Exception {
+    ctor public AclNotFoundException();
+  }
+
+  public interface Group extends java.security.Principal {
+    method public boolean addMember(java.security.Principal);
+    method public boolean isMember(java.security.Principal);
+    method public java.util.Enumeration<? extends java.security.Principal> members();
+    method public boolean removeMember(java.security.Principal);
+  }
+
+  public class LastOwnerException extends java.lang.Exception {
+    ctor public LastOwnerException();
+  }
+
+  public class NotOwnerException extends java.lang.Exception {
+    ctor public NotOwnerException();
+  }
+
+  public interface Owner {
+    method public boolean addOwner(java.security.Principal, java.security.Principal) throws java.security.acl.NotOwnerException;
+    method public boolean deleteOwner(java.security.Principal, java.security.Principal) throws java.security.acl.LastOwnerException, java.security.acl.NotOwnerException;
+    method public boolean isOwner(java.security.Principal);
+  }
+
+  public interface Permission {
+  }
+
+}
+
+package java.security.cert {
+
+  public abstract class CRL {
+    ctor protected CRL(String);
+    method public final String getType();
+    method public abstract boolean isRevoked(java.security.cert.Certificate);
+    method public abstract String toString();
+  }
+
+  public class CRLException extends java.security.GeneralSecurityException {
+    ctor public CRLException();
+    ctor public CRLException(String);
+    ctor public CRLException(String, Throwable);
+    ctor public CRLException(Throwable);
+  }
+
+  public enum CRLReason {
+    enum_constant public static final java.security.cert.CRLReason AA_COMPROMISE;
+    enum_constant public static final java.security.cert.CRLReason AFFILIATION_CHANGED;
+    enum_constant public static final java.security.cert.CRLReason CA_COMPROMISE;
+    enum_constant public static final java.security.cert.CRLReason CERTIFICATE_HOLD;
+    enum_constant public static final java.security.cert.CRLReason CESSATION_OF_OPERATION;
+    enum_constant public static final java.security.cert.CRLReason KEY_COMPROMISE;
+    enum_constant public static final java.security.cert.CRLReason PRIVILEGE_WITHDRAWN;
+    enum_constant public static final java.security.cert.CRLReason REMOVE_FROM_CRL;
+    enum_constant public static final java.security.cert.CRLReason SUPERSEDED;
+    enum_constant public static final java.security.cert.CRLReason UNSPECIFIED;
+    enum_constant public static final java.security.cert.CRLReason UNUSED;
+  }
+
+  public interface CRLSelector extends java.lang.Cloneable {
+    method public Object clone();
+    method public boolean match(java.security.cert.CRL);
+  }
+
+  public abstract class CertPath implements java.io.Serializable {
+    ctor protected CertPath(String);
+    method public abstract java.util.List<? extends java.security.cert.Certificate> getCertificates();
+    method public abstract byte[] getEncoded() throws java.security.cert.CertificateEncodingException;
+    method public abstract byte[] getEncoded(String) throws java.security.cert.CertificateEncodingException;
+    method public abstract java.util.Iterator<java.lang.String> getEncodings();
+    method public String getType();
+    method protected Object writeReplace() throws java.io.ObjectStreamException;
+  }
+
+  protected static class CertPath.CertPathRep implements java.io.Serializable {
+    ctor protected CertPath.CertPathRep(String, byte[]);
+    method protected Object readResolve() throws java.io.ObjectStreamException;
+  }
+
+  public class CertPathBuilder {
+    ctor protected CertPathBuilder(java.security.cert.CertPathBuilderSpi, java.security.Provider, String);
+    method public final java.security.cert.CertPathBuilderResult build(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public final String getAlgorithm();
+    method public static final String getDefaultType();
+    method public static java.security.cert.CertPathBuilder getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.cert.CertPathBuilder getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.cert.CertPathBuilder getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
+  }
+
+  public class CertPathBuilderException extends java.security.GeneralSecurityException {
+    ctor public CertPathBuilderException();
+    ctor public CertPathBuilderException(String);
+    ctor public CertPathBuilderException(Throwable);
+    ctor public CertPathBuilderException(String, Throwable);
+  }
+
+  public interface CertPathBuilderResult extends java.lang.Cloneable {
+    method public Object clone();
+    method public java.security.cert.CertPath getCertPath();
+  }
+
+  public abstract class CertPathBuilderSpi {
+    ctor public CertPathBuilderSpi();
+    method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+  }
+
+  public interface CertPathChecker {
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public void init(boolean) throws java.security.cert.CertPathValidatorException;
+    method public boolean isForwardCheckingSupported();
+  }
+
+  public interface CertPathParameters extends java.lang.Cloneable {
+    method public Object clone();
+  }
+
+  public class CertPathValidator {
+    ctor protected CertPathValidator(java.security.cert.CertPathValidatorSpi, java.security.Provider, String);
+    method public final String getAlgorithm();
+    method public static final String getDefaultType();
+    method public static java.security.cert.CertPathValidator getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static java.security.cert.CertPathValidator getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.cert.CertPathValidator getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public final java.security.cert.CertPathChecker getRevocationChecker();
+    method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
+  }
+
+  public class CertPathValidatorException extends java.security.GeneralSecurityException {
+    ctor public CertPathValidatorException();
+    ctor public CertPathValidatorException(String);
+    ctor public CertPathValidatorException(Throwable);
+    ctor public CertPathValidatorException(String, Throwable);
+    ctor public CertPathValidatorException(String, Throwable, java.security.cert.CertPath, int);
+    ctor public CertPathValidatorException(String, Throwable, java.security.cert.CertPath, int, java.security.cert.CertPathValidatorException.Reason);
+    method public java.security.cert.CertPath getCertPath();
+    method public int getIndex();
+    method public java.security.cert.CertPathValidatorException.Reason getReason();
+  }
+
+  public enum CertPathValidatorException.BasicReason implements java.security.cert.CertPathValidatorException.Reason {
+    enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason ALGORITHM_CONSTRAINED;
+    enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason EXPIRED;
+    enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason INVALID_SIGNATURE;
+    enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason NOT_YET_VALID;
+    enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason REVOKED;
+    enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason UNDETERMINED_REVOCATION_STATUS;
+    enum_constant public static final java.security.cert.CertPathValidatorException.BasicReason UNSPECIFIED;
+  }
+
+  public static interface CertPathValidatorException.Reason extends java.io.Serializable {
+  }
+
+  public interface CertPathValidatorResult extends java.lang.Cloneable {
+    method public Object clone();
+  }
+
+  public abstract class CertPathValidatorSpi {
+    ctor public CertPathValidatorSpi();
+    method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+    method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
+  }
+
+  public interface CertSelector extends java.lang.Cloneable {
+    method public Object clone();
+    method public boolean match(java.security.cert.Certificate);
+  }
+
+  public class CertStore {
+    ctor protected CertStore(java.security.cert.CertStoreSpi, java.security.Provider, String, java.security.cert.CertStoreParameters);
+    method public final java.util.Collection<? extends java.security.cert.CRL> getCRLs(java.security.cert.CRLSelector) throws java.security.cert.CertStoreException;
+    method public final java.security.cert.CertStoreParameters getCertStoreParameters();
+    method public final java.util.Collection<? extends java.security.cert.Certificate> getCertificates(java.security.cert.CertSelector) throws java.security.cert.CertStoreException;
+    method public static final String getDefaultType();
+    method public static java.security.cert.CertStore getInstance(String, java.security.cert.CertStoreParameters) throws java.security.InvalidAlgorithmParameterException, java.security.NoSuchAlgorithmException;
+    method public static java.security.cert.CertStore getInstance(String, java.security.cert.CertStoreParameters, String) throws java.security.InvalidAlgorithmParameterException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static java.security.cert.CertStore getInstance(String, java.security.cert.CertStoreParameters, java.security.Provider) throws java.security.InvalidAlgorithmParameterException, java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public final String getType();
+  }
+
+  public class CertStoreException extends java.security.GeneralSecurityException {
+    ctor public CertStoreException();
+    ctor public CertStoreException(String);
+    ctor public CertStoreException(Throwable);
+    ctor public CertStoreException(String, Throwable);
+  }
+
+  public interface CertStoreParameters extends java.lang.Cloneable {
+    method public Object clone();
+  }
+
+  public abstract class CertStoreSpi {
+    ctor public CertStoreSpi(java.security.cert.CertStoreParameters) throws java.security.InvalidAlgorithmParameterException;
+    method public abstract java.util.Collection<? extends java.security.cert.CRL> engineGetCRLs(java.security.cert.CRLSelector) throws java.security.cert.CertStoreException;
+    method public abstract java.util.Collection<? extends java.security.cert.Certificate> engineGetCertificates(java.security.cert.CertSelector) throws java.security.cert.CertStoreException;
+  }
+
+  public abstract class Certificate implements java.io.Serializable {
+    ctor protected Certificate(String);
+    method public abstract byte[] getEncoded() throws java.security.cert.CertificateEncodingException;
+    method public abstract java.security.PublicKey getPublicKey();
+    method public final String getType();
+    method public abstract String toString();
+    method public abstract void verify(java.security.PublicKey) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public abstract void verify(java.security.PublicKey, String) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
+    method protected Object writeReplace() throws java.io.ObjectStreamException;
+  }
+
+  protected static class Certificate.CertificateRep implements java.io.Serializable {
+    ctor protected Certificate.CertificateRep(String, byte[]);
+    method protected Object readResolve() throws java.io.ObjectStreamException;
+  }
+
+  public class CertificateEncodingException extends java.security.cert.CertificateException {
+    ctor public CertificateEncodingException();
+    ctor public CertificateEncodingException(String);
+    ctor public CertificateEncodingException(String, Throwable);
+    ctor public CertificateEncodingException(Throwable);
+  }
+
+  public class CertificateException extends java.security.GeneralSecurityException {
+    ctor public CertificateException();
+    ctor public CertificateException(String);
+    ctor public CertificateException(String, Throwable);
+    ctor public CertificateException(Throwable);
+  }
+
+  public class CertificateExpiredException extends java.security.cert.CertificateException {
+    ctor public CertificateExpiredException();
+    ctor public CertificateExpiredException(String);
+  }
+
+  public class CertificateFactory {
+    ctor protected CertificateFactory(java.security.cert.CertificateFactorySpi, java.security.Provider, String);
+    method public final java.security.cert.CRL generateCRL(java.io.InputStream) throws java.security.cert.CRLException;
+    method public final java.util.Collection<? extends java.security.cert.CRL> generateCRLs(java.io.InputStream) throws java.security.cert.CRLException;
+    method public final java.security.cert.CertPath generateCertPath(java.io.InputStream) throws java.security.cert.CertificateException;
+    method public final java.security.cert.CertPath generateCertPath(java.io.InputStream, String) throws java.security.cert.CertificateException;
+    method public final java.security.cert.CertPath generateCertPath(java.util.List<? extends java.security.cert.Certificate>) throws java.security.cert.CertificateException;
+    method public final java.security.cert.Certificate generateCertificate(java.io.InputStream) throws java.security.cert.CertificateException;
+    method public final java.util.Collection<? extends java.security.cert.Certificate> generateCertificates(java.io.InputStream) throws java.security.cert.CertificateException;
+    method public final java.util.Iterator<java.lang.String> getCertPathEncodings();
+    method public static final java.security.cert.CertificateFactory getInstance(String) throws java.security.cert.CertificateException;
+    method public static final java.security.cert.CertificateFactory getInstance(String, String) throws java.security.cert.CertificateException, java.security.NoSuchProviderException;
+    method public static final java.security.cert.CertificateFactory getInstance(String, java.security.Provider) throws java.security.cert.CertificateException;
+    method public final java.security.Provider getProvider();
+    method public final String getType();
+  }
+
+  public abstract class CertificateFactorySpi {
+    ctor public CertificateFactorySpi();
+    method public abstract java.security.cert.CRL engineGenerateCRL(java.io.InputStream) throws java.security.cert.CRLException;
+    method public abstract java.util.Collection<? extends java.security.cert.CRL> engineGenerateCRLs(java.io.InputStream) throws java.security.cert.CRLException;
+    method public java.security.cert.CertPath engineGenerateCertPath(java.io.InputStream) throws java.security.cert.CertificateException;
+    method public java.security.cert.CertPath engineGenerateCertPath(java.io.InputStream, String) throws java.security.cert.CertificateException;
+    method public java.security.cert.CertPath engineGenerateCertPath(java.util.List<? extends java.security.cert.Certificate>) throws java.security.cert.CertificateException;
+    method public abstract java.security.cert.Certificate engineGenerateCertificate(java.io.InputStream) throws java.security.cert.CertificateException;
+    method public abstract java.util.Collection<? extends java.security.cert.Certificate> engineGenerateCertificates(java.io.InputStream) throws java.security.cert.CertificateException;
+    method public java.util.Iterator<java.lang.String> engineGetCertPathEncodings();
+  }
+
+  public class CertificateNotYetValidException extends java.security.cert.CertificateException {
+    ctor public CertificateNotYetValidException();
+    ctor public CertificateNotYetValidException(String);
+  }
+
+  public class CertificateParsingException extends java.security.cert.CertificateException {
+    ctor public CertificateParsingException();
+    ctor public CertificateParsingException(String);
+    ctor public CertificateParsingException(String, Throwable);
+    ctor public CertificateParsingException(Throwable);
+  }
+
+  public class CertificateRevokedException extends java.security.cert.CertificateException {
+    ctor public CertificateRevokedException(java.util.Date, java.security.cert.CRLReason, javax.security.auth.x500.X500Principal, java.util.Map<java.lang.String,java.security.cert.Extension>);
+    method public javax.security.auth.x500.X500Principal getAuthorityName();
+    method public java.util.Map<java.lang.String,java.security.cert.Extension> getExtensions();
+    method public java.util.Date getInvalidityDate();
+    method public java.util.Date getRevocationDate();
+    method public java.security.cert.CRLReason getRevocationReason();
+  }
+
+  public class CollectionCertStoreParameters implements java.security.cert.CertStoreParameters {
+    ctor public CollectionCertStoreParameters(java.util.Collection<?>);
+    ctor public CollectionCertStoreParameters();
+    method public Object clone();
+    method public java.util.Collection<?> getCollection();
+  }
+
+  public interface Extension {
+    method public void encode(java.io.OutputStream) throws java.io.IOException;
+    method public String getId();
+    method public byte[] getValue();
+    method public boolean isCritical();
+  }
+
+  public class LDAPCertStoreParameters implements java.security.cert.CertStoreParameters {
+    ctor public LDAPCertStoreParameters(String, int);
+    ctor public LDAPCertStoreParameters(String);
+    ctor public LDAPCertStoreParameters();
+    method public Object clone();
+    method public int getPort();
+    method public String getServerName();
+  }
+
+  public class PKIXBuilderParameters extends java.security.cert.PKIXParameters {
+    ctor public PKIXBuilderParameters(java.util.Set<java.security.cert.TrustAnchor>, java.security.cert.CertSelector) throws java.security.InvalidAlgorithmParameterException;
+    ctor public PKIXBuilderParameters(java.security.KeyStore, java.security.cert.CertSelector) throws java.security.InvalidAlgorithmParameterException, java.security.KeyStoreException;
+    method public int getMaxPathLength();
+    method public void setMaxPathLength(int);
+  }
+
+  public class PKIXCertPathBuilderResult extends java.security.cert.PKIXCertPathValidatorResult implements java.security.cert.CertPathBuilderResult {
+    ctor public PKIXCertPathBuilderResult(java.security.cert.CertPath, java.security.cert.TrustAnchor, java.security.cert.PolicyNode, java.security.PublicKey);
+    method public java.security.cert.CertPath getCertPath();
+  }
+
+  public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
+    ctor protected PKIXCertPathChecker();
+    method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+    method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+    method public Object clone();
+    method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
+  }
+
+  public class PKIXCertPathValidatorResult implements java.security.cert.CertPathValidatorResult {
+    ctor public PKIXCertPathValidatorResult(java.security.cert.TrustAnchor, java.security.cert.PolicyNode, java.security.PublicKey);
+    method public Object clone();
+    method public java.security.cert.PolicyNode getPolicyTree();
+    method public java.security.PublicKey getPublicKey();
+    method public java.security.cert.TrustAnchor getTrustAnchor();
+  }
+
+  public class PKIXParameters implements java.security.cert.CertPathParameters {
+    ctor public PKIXParameters(java.util.Set<java.security.cert.TrustAnchor>) throws java.security.InvalidAlgorithmParameterException;
+    ctor public PKIXParameters(java.security.KeyStore) throws java.security.InvalidAlgorithmParameterException, java.security.KeyStoreException;
+    method public void addCertPathChecker(java.security.cert.PKIXCertPathChecker);
+    method public void addCertStore(java.security.cert.CertStore);
+    method public Object clone();
+    method public java.util.List<java.security.cert.PKIXCertPathChecker> getCertPathCheckers();
+    method public java.util.List<java.security.cert.CertStore> getCertStores();
+    method public java.util.Date getDate();
+    method public java.util.Set<java.lang.String> getInitialPolicies();
+    method public boolean getPolicyQualifiersRejected();
+    method public String getSigProvider();
+    method public java.security.cert.CertSelector getTargetCertConstraints();
+    method public java.util.Set<java.security.cert.TrustAnchor> getTrustAnchors();
+    method public boolean isAnyPolicyInhibited();
+    method public boolean isExplicitPolicyRequired();
+    method public boolean isPolicyMappingInhibited();
+    method public boolean isRevocationEnabled();
+    method public void setAnyPolicyInhibited(boolean);
+    method public void setCertPathCheckers(java.util.List<java.security.cert.PKIXCertPathChecker>);
+    method public void setCertStores(java.util.List<java.security.cert.CertStore>);
+    method public void setDate(java.util.Date);
+    method public void setExplicitPolicyRequired(boolean);
+    method public void setInitialPolicies(java.util.Set<java.lang.String>);
+    method public void setPolicyMappingInhibited(boolean);
+    method public void setPolicyQualifiersRejected(boolean);
+    method public void setRevocationEnabled(boolean);
+    method public void setSigProvider(String);
+    method public void setTargetCertConstraints(java.security.cert.CertSelector);
+    method public void setTrustAnchors(java.util.Set<java.security.cert.TrustAnchor>) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public enum PKIXReason implements java.security.cert.CertPathValidatorException.Reason {
+    enum_constant public static final java.security.cert.PKIXReason INVALID_KEY_USAGE;
+    enum_constant public static final java.security.cert.PKIXReason INVALID_NAME;
+    enum_constant public static final java.security.cert.PKIXReason INVALID_POLICY;
+    enum_constant public static final java.security.cert.PKIXReason NAME_CHAINING;
+    enum_constant public static final java.security.cert.PKIXReason NOT_CA_CERT;
+    enum_constant public static final java.security.cert.PKIXReason NO_TRUST_ANCHOR;
+    enum_constant public static final java.security.cert.PKIXReason PATH_TOO_LONG;
+    enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
+  }
+
+  public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+    ctor protected PKIXRevocationChecker();
+    method public java.security.cert.PKIXRevocationChecker clone();
+    method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+    method public java.net.URI getOcspResponder();
+    method public java.security.cert.X509Certificate getOcspResponderCert();
+    method public java.util.Map<java.security.cert.X509Certificate,byte[]> getOcspResponses();
+    method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+    method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+    method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+    method public void setOcspResponder(java.net.URI);
+    method public void setOcspResponderCert(java.security.cert.X509Certificate);
+    method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate,byte[]>);
+    method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+  }
+
+  public enum PKIXRevocationChecker.Option {
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+    enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+  }
+
+  public interface PolicyNode {
+    method public java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
+    method public int getDepth();
+    method public java.util.Set<java.lang.String> getExpectedPolicies();
+    method public java.security.cert.PolicyNode getParent();
+    method public java.util.Set<? extends java.security.cert.PolicyQualifierInfo> getPolicyQualifiers();
+    method public String getValidPolicy();
+    method public boolean isCritical();
+  }
+
+  public class PolicyQualifierInfo {
+    ctor public PolicyQualifierInfo(byte[]) throws java.io.IOException;
+    method public final byte[] getEncoded();
+    method public final byte[] getPolicyQualifier();
+    method public final String getPolicyQualifierId();
+  }
+
+  public class TrustAnchor {
+    ctor public TrustAnchor(java.security.cert.X509Certificate, byte[]);
+    ctor public TrustAnchor(javax.security.auth.x500.X500Principal, java.security.PublicKey, byte[]);
+    ctor public TrustAnchor(String, java.security.PublicKey, byte[]);
+    method public final javax.security.auth.x500.X500Principal getCA();
+    method public final String getCAName();
+    method public final java.security.PublicKey getCAPublicKey();
+    method public final byte[] getNameConstraints();
+    method public final java.security.cert.X509Certificate getTrustedCert();
+  }
+
+  public abstract class X509CRL extends java.security.cert.CRL implements java.security.cert.X509Extension {
+    ctor protected X509CRL();
+    method public abstract byte[] getEncoded() throws java.security.cert.CRLException;
+    method public abstract java.security.Principal getIssuerDN();
+    method public javax.security.auth.x500.X500Principal getIssuerX500Principal();
+    method public abstract java.util.Date getNextUpdate();
+    method public abstract java.security.cert.X509CRLEntry getRevokedCertificate(java.math.BigInteger);
+    method public java.security.cert.X509CRLEntry getRevokedCertificate(java.security.cert.X509Certificate);
+    method public abstract java.util.Set<? extends java.security.cert.X509CRLEntry> getRevokedCertificates();
+    method public abstract String getSigAlgName();
+    method public abstract String getSigAlgOID();
+    method public abstract byte[] getSigAlgParams();
+    method public abstract byte[] getSignature();
+    method public abstract byte[] getTBSCertList() throws java.security.cert.CRLException;
+    method public abstract java.util.Date getThisUpdate();
+    method public abstract int getVersion();
+    method public abstract void verify(java.security.PublicKey) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public abstract void verify(java.security.PublicKey, String) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CRLException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
+  }
+
+  public abstract class X509CRLEntry implements java.security.cert.X509Extension {
+    ctor public X509CRLEntry();
+    method public javax.security.auth.x500.X500Principal getCertificateIssuer();
+    method public abstract byte[] getEncoded() throws java.security.cert.CRLException;
+    method public abstract java.util.Date getRevocationDate();
+    method public java.security.cert.CRLReason getRevocationReason();
+    method public abstract java.math.BigInteger getSerialNumber();
+    method public abstract boolean hasExtensions();
+    method public abstract String toString();
+  }
+
+  public class X509CRLSelector implements java.security.cert.CRLSelector {
+    ctor public X509CRLSelector();
+    method public void addIssuer(javax.security.auth.x500.X500Principal);
+    method public void addIssuerName(String) throws java.io.IOException;
+    method public void addIssuerName(byte[]) throws java.io.IOException;
+    method public Object clone();
+    method public java.security.cert.X509Certificate getCertificateChecking();
+    method public java.util.Date getDateAndTime();
+    method public java.util.Collection<java.lang.Object> getIssuerNames();
+    method public java.util.Collection<javax.security.auth.x500.X500Principal> getIssuers();
+    method public java.math.BigInteger getMaxCRL();
+    method public java.math.BigInteger getMinCRL();
+    method public boolean match(java.security.cert.CRL);
+    method public void setCertificateChecking(java.security.cert.X509Certificate);
+    method public void setDateAndTime(java.util.Date);
+    method public void setIssuerNames(java.util.Collection<?>) throws java.io.IOException;
+    method public void setIssuers(java.util.Collection<javax.security.auth.x500.X500Principal>);
+    method public void setMaxCRLNumber(java.math.BigInteger);
+    method public void setMinCRLNumber(java.math.BigInteger);
+  }
+
+  public class X509CertSelector implements java.security.cert.CertSelector {
+    ctor public X509CertSelector();
+    method public void addPathToName(int, String) throws java.io.IOException;
+    method public void addPathToName(int, byte[]) throws java.io.IOException;
+    method public void addSubjectAlternativeName(int, String) throws java.io.IOException;
+    method public void addSubjectAlternativeName(int, byte[]) throws java.io.IOException;
+    method public Object clone();
+    method public byte[] getAuthorityKeyIdentifier();
+    method public int getBasicConstraints();
+    method public java.security.cert.X509Certificate getCertificate();
+    method public java.util.Date getCertificateValid();
+    method public java.util.Set<java.lang.String> getExtendedKeyUsage();
+    method public javax.security.auth.x500.X500Principal getIssuer();
+    method public byte[] getIssuerAsBytes() throws java.io.IOException;
+    method public String getIssuerAsString();
+    method public boolean[] getKeyUsage();
+    method public boolean getMatchAllSubjectAltNames();
+    method public byte[] getNameConstraints();
+    method public java.util.Collection<java.util.List<?>> getPathToNames();
+    method public java.util.Set<java.lang.String> getPolicy();
+    method public java.util.Date getPrivateKeyValid();
+    method public java.math.BigInteger getSerialNumber();
+    method public javax.security.auth.x500.X500Principal getSubject();
+    method public java.util.Collection<java.util.List<?>> getSubjectAlternativeNames();
+    method public byte[] getSubjectAsBytes() throws java.io.IOException;
+    method public String getSubjectAsString();
+    method public byte[] getSubjectKeyIdentifier();
+    method public java.security.PublicKey getSubjectPublicKey();
+    method public String getSubjectPublicKeyAlgID();
+    method public boolean match(java.security.cert.Certificate);
+    method public void setAuthorityKeyIdentifier(byte[]);
+    method public void setBasicConstraints(int);
+    method public void setCertificate(java.security.cert.X509Certificate);
+    method public void setCertificateValid(java.util.Date);
+    method public void setExtendedKeyUsage(java.util.Set<java.lang.String>) throws java.io.IOException;
+    method public void setIssuer(javax.security.auth.x500.X500Principal);
+    method public void setIssuer(String) throws java.io.IOException;
+    method public void setIssuer(byte[]) throws java.io.IOException;
+    method public void setKeyUsage(boolean[]);
+    method public void setMatchAllSubjectAltNames(boolean);
+    method public void setNameConstraints(byte[]) throws java.io.IOException;
+    method public void setPathToNames(java.util.Collection<java.util.List<?>>) throws java.io.IOException;
+    method public void setPolicy(java.util.Set<java.lang.String>) throws java.io.IOException;
+    method public void setPrivateKeyValid(java.util.Date);
+    method public void setSerialNumber(java.math.BigInteger);
+    method public void setSubject(javax.security.auth.x500.X500Principal);
+    method public void setSubject(String) throws java.io.IOException;
+    method public void setSubject(byte[]) throws java.io.IOException;
+    method public void setSubjectAlternativeNames(java.util.Collection<java.util.List<?>>) throws java.io.IOException;
+    method public void setSubjectKeyIdentifier(byte[]);
+    method public void setSubjectPublicKey(java.security.PublicKey);
+    method public void setSubjectPublicKey(byte[]) throws java.io.IOException;
+    method public void setSubjectPublicKeyAlgID(String) throws java.io.IOException;
+  }
+
+  public abstract class X509Certificate extends java.security.cert.Certificate implements java.security.cert.X509Extension {
+    ctor protected X509Certificate();
+    method public abstract void checkValidity() throws java.security.cert.CertificateExpiredException, java.security.cert.CertificateNotYetValidException;
+    method public abstract void checkValidity(java.util.Date) throws java.security.cert.CertificateExpiredException, java.security.cert.CertificateNotYetValidException;
+    method public abstract int getBasicConstraints();
+    method public java.util.List<java.lang.String> getExtendedKeyUsage() throws java.security.cert.CertificateParsingException;
+    method public java.util.Collection<java.util.List<?>> getIssuerAlternativeNames() throws java.security.cert.CertificateParsingException;
+    method public abstract java.security.Principal getIssuerDN();
+    method public abstract boolean[] getIssuerUniqueID();
+    method public javax.security.auth.x500.X500Principal getIssuerX500Principal();
+    method public abstract boolean[] getKeyUsage();
+    method public abstract java.util.Date getNotAfter();
+    method public abstract java.util.Date getNotBefore();
+    method public abstract java.math.BigInteger getSerialNumber();
+    method public abstract String getSigAlgName();
+    method public abstract String getSigAlgOID();
+    method public abstract byte[] getSigAlgParams();
+    method public abstract byte[] getSignature();
+    method public java.util.Collection<java.util.List<?>> getSubjectAlternativeNames() throws java.security.cert.CertificateParsingException;
+    method public abstract java.security.Principal getSubjectDN();
+    method public abstract boolean[] getSubjectUniqueID();
+    method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
+    method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
+    method public abstract int getVersion();
+  }
+
+  public interface X509Extension {
+    method public java.util.Set<java.lang.String> getCriticalExtensionOIDs();
+    method public byte[] getExtensionValue(String);
+    method public java.util.Set<java.lang.String> getNonCriticalExtensionOIDs();
+    method public boolean hasUnsupportedCriticalExtension();
+  }
+
+}
+
+package java.security.interfaces {
+
+  public interface DSAKey {
+    method public java.security.interfaces.DSAParams getParams();
+  }
+
+  public interface DSAKeyPairGenerator {
+    method public void initialize(java.security.interfaces.DSAParams, java.security.SecureRandom) throws java.security.InvalidParameterException;
+    method public void initialize(int, boolean, java.security.SecureRandom) throws java.security.InvalidParameterException;
+  }
+
+  public interface DSAParams {
+    method public java.math.BigInteger getG();
+    method public java.math.BigInteger getP();
+    method public java.math.BigInteger getQ();
+  }
+
+  public interface DSAPrivateKey extends java.security.interfaces.DSAKey java.security.PrivateKey {
+    method public java.math.BigInteger getX();
+    field public static final long serialVersionUID = 7776497482533790279L; // 0x6bebab423b256247L
+  }
+
+  public interface DSAPublicKey extends java.security.interfaces.DSAKey java.security.PublicKey {
+    method public java.math.BigInteger getY();
+    field public static final long serialVersionUID = 1234526332779022332L; // 0x1121eb28ab28c7fcL
+  }
+
+  public interface ECKey {
+    method public java.security.spec.ECParameterSpec getParams();
+  }
+
+  public interface ECPrivateKey extends java.security.PrivateKey java.security.interfaces.ECKey {
+    method public java.math.BigInteger getS();
+    field public static final long serialVersionUID = -7896394956925609184L; // 0x926a5e9fa2435b20L
+  }
+
+  public interface ECPublicKey extends java.security.PublicKey java.security.interfaces.ECKey {
+    method public java.security.spec.ECPoint getW();
+    field public static final long serialVersionUID = -3314988629879632826L; // 0xd1fecb679990cc46L
+  }
+
+  public interface RSAKey {
+    method public java.math.BigInteger getModulus();
+  }
+
+  public interface RSAMultiPrimePrivateCrtKey extends java.security.interfaces.RSAPrivateKey {
+    method public java.math.BigInteger getCrtCoefficient();
+    method public java.security.spec.RSAOtherPrimeInfo[] getOtherPrimeInfo();
+    method public java.math.BigInteger getPrimeExponentP();
+    method public java.math.BigInteger getPrimeExponentQ();
+    method public java.math.BigInteger getPrimeP();
+    method public java.math.BigInteger getPrimeQ();
+    method public java.math.BigInteger getPublicExponent();
+    field public static final long serialVersionUID = 618058533534628008L; // 0x893c8f62dbaf8a8L
+  }
+
+  public interface RSAPrivateCrtKey extends java.security.interfaces.RSAPrivateKey {
+    method public java.math.BigInteger getCrtCoefficient();
+    method public java.math.BigInteger getPrimeExponentP();
+    method public java.math.BigInteger getPrimeExponentQ();
+    method public java.math.BigInteger getPrimeP();
+    method public java.math.BigInteger getPrimeQ();
+    method public java.math.BigInteger getPublicExponent();
+    field public static final long serialVersionUID = -5682214253527700368L; // 0xb124b83df8d1ec70L
+  }
+
+  public interface RSAPrivateKey extends java.security.PrivateKey java.security.interfaces.RSAKey {
+    method public java.math.BigInteger getPrivateExponent();
+    field public static final long serialVersionUID = 5187144804936595022L; // 0x47fc70b7a8c2364eL
+  }
+
+  public interface RSAPublicKey extends java.security.PublicKey java.security.interfaces.RSAKey {
+    method public java.math.BigInteger getPublicExponent();
+    field public static final long serialVersionUID = -8727434096241101194L; // 0x86e1ecedeceab676L
+  }
+
+}
+
+package java.security.spec {
+
+  public interface AlgorithmParameterSpec {
+  }
+
+  public class DSAParameterSpec implements java.security.spec.AlgorithmParameterSpec java.security.interfaces.DSAParams {
+    ctor public DSAParameterSpec(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getG();
+    method public java.math.BigInteger getP();
+    method public java.math.BigInteger getQ();
+  }
+
+  public class DSAPrivateKeySpec implements java.security.spec.KeySpec {
+    ctor public DSAPrivateKeySpec(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getG();
+    method public java.math.BigInteger getP();
+    method public java.math.BigInteger getQ();
+    method public java.math.BigInteger getX();
+  }
+
+  public class DSAPublicKeySpec implements java.security.spec.KeySpec {
+    ctor public DSAPublicKeySpec(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getG();
+    method public java.math.BigInteger getP();
+    method public java.math.BigInteger getQ();
+    method public java.math.BigInteger getY();
+  }
+
+  public interface ECField {
+    method public int getFieldSize();
+  }
+
+  public class ECFieldF2m implements java.security.spec.ECField {
+    ctor public ECFieldF2m(int);
+    ctor public ECFieldF2m(int, java.math.BigInteger);
+    ctor public ECFieldF2m(int, int[]);
+    method public int getFieldSize();
+    method public int getM();
+    method public int[] getMidTermsOfReductionPolynomial();
+    method public java.math.BigInteger getReductionPolynomial();
+  }
+
+  public class ECFieldFp implements java.security.spec.ECField {
+    ctor public ECFieldFp(java.math.BigInteger);
+    method public int getFieldSize();
+    method public java.math.BigInteger getP();
+  }
+
+  public class ECGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public ECGenParameterSpec(String);
+    method public String getName();
+  }
+
+  public class ECParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public ECParameterSpec(java.security.spec.EllipticCurve, java.security.spec.ECPoint, java.math.BigInteger, int);
+    method public int getCofactor();
+    method public java.security.spec.EllipticCurve getCurve();
+    method public java.security.spec.ECPoint getGenerator();
+    method public java.math.BigInteger getOrder();
+  }
+
+  public class ECPoint {
+    ctor public ECPoint(java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getAffineX();
+    method public java.math.BigInteger getAffineY();
+    field public static final java.security.spec.ECPoint POINT_INFINITY;
+  }
+
+  public class ECPrivateKeySpec implements java.security.spec.KeySpec {
+    ctor public ECPrivateKeySpec(java.math.BigInteger, java.security.spec.ECParameterSpec);
+    method public java.security.spec.ECParameterSpec getParams();
+    method public java.math.BigInteger getS();
+  }
+
+  public class ECPublicKeySpec implements java.security.spec.KeySpec {
+    ctor public ECPublicKeySpec(java.security.spec.ECPoint, java.security.spec.ECParameterSpec);
+    method public java.security.spec.ECParameterSpec getParams();
+    method public java.security.spec.ECPoint getW();
+  }
+
+  public class EllipticCurve {
+    ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger);
+    ctor public EllipticCurve(java.security.spec.ECField, java.math.BigInteger, java.math.BigInteger, byte[]);
+    method public java.math.BigInteger getA();
+    method public java.math.BigInteger getB();
+    method public java.security.spec.ECField getField();
+    method public byte[] getSeed();
+  }
+
+  public abstract class EncodedKeySpec implements java.security.spec.KeySpec {
+    ctor public EncodedKeySpec(byte[]);
+    method public byte[] getEncoded();
+    method public abstract String getFormat();
+  }
+
+  public class InvalidKeySpecException extends java.security.GeneralSecurityException {
+    ctor public InvalidKeySpecException();
+    ctor public InvalidKeySpecException(String);
+    ctor public InvalidKeySpecException(String, Throwable);
+    ctor public InvalidKeySpecException(Throwable);
+  }
+
+  public class InvalidParameterSpecException extends java.security.GeneralSecurityException {
+    ctor public InvalidParameterSpecException();
+    ctor public InvalidParameterSpecException(String);
+  }
+
+  public interface KeySpec {
+  }
+
+  public class MGF1ParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public MGF1ParameterSpec(String);
+    method public String getDigestAlgorithm();
+    field public static final java.security.spec.MGF1ParameterSpec SHA1;
+    field public static final java.security.spec.MGF1ParameterSpec SHA224;
+    field public static final java.security.spec.MGF1ParameterSpec SHA256;
+    field public static final java.security.spec.MGF1ParameterSpec SHA384;
+    field public static final java.security.spec.MGF1ParameterSpec SHA512;
+  }
+
+  public class PKCS8EncodedKeySpec extends java.security.spec.EncodedKeySpec {
+    ctor public PKCS8EncodedKeySpec(byte[]);
+    method public final String getFormat();
+  }
+
+  public class PSSParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public PSSParameterSpec(String, String, java.security.spec.AlgorithmParameterSpec, int, int);
+    ctor public PSSParameterSpec(int);
+    method public String getDigestAlgorithm();
+    method public String getMGFAlgorithm();
+    method public java.security.spec.AlgorithmParameterSpec getMGFParameters();
+    method public int getSaltLength();
+    method public int getTrailerField();
+    field public static final java.security.spec.PSSParameterSpec DEFAULT;
+  }
+
+  public class RSAKeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public RSAKeyGenParameterSpec(int, java.math.BigInteger);
+    method public int getKeysize();
+    method public java.math.BigInteger getPublicExponent();
+    field public static final java.math.BigInteger F0;
+    field public static final java.math.BigInteger F4;
+  }
+
+  public class RSAMultiPrimePrivateCrtKeySpec extends java.security.spec.RSAPrivateKeySpec {
+    ctor public RSAMultiPrimePrivateCrtKeySpec(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.security.spec.RSAOtherPrimeInfo[]);
+    method public java.math.BigInteger getCrtCoefficient();
+    method public java.security.spec.RSAOtherPrimeInfo[] getOtherPrimeInfo();
+    method public java.math.BigInteger getPrimeExponentP();
+    method public java.math.BigInteger getPrimeExponentQ();
+    method public java.math.BigInteger getPrimeP();
+    method public java.math.BigInteger getPrimeQ();
+    method public java.math.BigInteger getPublicExponent();
+  }
+
+  public class RSAOtherPrimeInfo {
+    ctor public RSAOtherPrimeInfo(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public final java.math.BigInteger getCrtCoefficient();
+    method public final java.math.BigInteger getExponent();
+    method public final java.math.BigInteger getPrime();
+  }
+
+  public class RSAPrivateCrtKeySpec extends java.security.spec.RSAPrivateKeySpec {
+    ctor public RSAPrivateCrtKeySpec(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getCrtCoefficient();
+    method public java.math.BigInteger getPrimeExponentP();
+    method public java.math.BigInteger getPrimeExponentQ();
+    method public java.math.BigInteger getPrimeP();
+    method public java.math.BigInteger getPrimeQ();
+    method public java.math.BigInteger getPublicExponent();
+  }
+
+  public class RSAPrivateKeySpec implements java.security.spec.KeySpec {
+    ctor public RSAPrivateKeySpec(java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getModulus();
+    method public java.math.BigInteger getPrivateExponent();
+  }
+
+  public class RSAPublicKeySpec implements java.security.spec.KeySpec {
+    ctor public RSAPublicKeySpec(java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getModulus();
+    method public java.math.BigInteger getPublicExponent();
+  }
+
+  public class X509EncodedKeySpec extends java.security.spec.EncodedKeySpec {
+    ctor public X509EncodedKeySpec(byte[]);
+    method public final String getFormat();
+  }
+
+}
+
+package java.sql {
+
+  public interface Array {
+    method public void free() throws java.sql.SQLException;
+    method public Object getArray() throws java.sql.SQLException;
+    method public Object getArray(java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public Object getArray(long, int) throws java.sql.SQLException;
+    method public Object getArray(long, int, java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public int getBaseType() throws java.sql.SQLException;
+    method public String getBaseTypeName() throws java.sql.SQLException;
+    method public java.sql.ResultSet getResultSet() throws java.sql.SQLException;
+    method public java.sql.ResultSet getResultSet(java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public java.sql.ResultSet getResultSet(long, int) throws java.sql.SQLException;
+    method public java.sql.ResultSet getResultSet(long, int, java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+  }
+
+  public class BatchUpdateException extends java.sql.SQLException {
+    ctor public BatchUpdateException(String, String, int, int[]);
+    ctor public BatchUpdateException(String, String, int[]);
+    ctor public BatchUpdateException(String, int[]);
+    ctor public BatchUpdateException(int[]);
+    ctor public BatchUpdateException();
+    ctor public BatchUpdateException(Throwable);
+    ctor public BatchUpdateException(int[], Throwable);
+    ctor public BatchUpdateException(String, int[], Throwable);
+    ctor public BatchUpdateException(String, String, int[], Throwable);
+    ctor public BatchUpdateException(String, String, int, int[], Throwable);
+    method public int[] getUpdateCounts();
+  }
+
+  public interface Blob {
+    method public void free() throws java.sql.SQLException;
+    method public java.io.InputStream getBinaryStream() throws java.sql.SQLException;
+    method public java.io.InputStream getBinaryStream(long, long) throws java.sql.SQLException;
+    method public byte[] getBytes(long, int) throws java.sql.SQLException;
+    method public long length() throws java.sql.SQLException;
+    method public long position(byte[], long) throws java.sql.SQLException;
+    method public long position(java.sql.Blob, long) throws java.sql.SQLException;
+    method public java.io.OutputStream setBinaryStream(long) throws java.sql.SQLException;
+    method public int setBytes(long, byte[]) throws java.sql.SQLException;
+    method public int setBytes(long, byte[], int, int) throws java.sql.SQLException;
+    method public void truncate(long) throws java.sql.SQLException;
+  }
+
+  public interface CallableStatement extends java.sql.PreparedStatement {
+    method public java.sql.Array getArray(int) throws java.sql.SQLException;
+    method public java.sql.Array getArray(String) throws java.sql.SQLException;
+    method @Deprecated public java.math.BigDecimal getBigDecimal(int, int) throws java.sql.SQLException;
+    method public java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
+    method public java.math.BigDecimal getBigDecimal(String) throws java.sql.SQLException;
+    method public java.sql.Blob getBlob(int) throws java.sql.SQLException;
+    method public java.sql.Blob getBlob(String) throws java.sql.SQLException;
+    method public boolean getBoolean(int) throws java.sql.SQLException;
+    method public boolean getBoolean(String) throws java.sql.SQLException;
+    method public byte getByte(int) throws java.sql.SQLException;
+    method public byte getByte(String) throws java.sql.SQLException;
+    method public byte[] getBytes(int) throws java.sql.SQLException;
+    method public byte[] getBytes(String) throws java.sql.SQLException;
+    method public java.io.Reader getCharacterStream(int) throws java.sql.SQLException;
+    method public java.io.Reader getCharacterStream(String) throws java.sql.SQLException;
+    method public java.sql.Clob getClob(int) throws java.sql.SQLException;
+    method public java.sql.Clob getClob(String) throws java.sql.SQLException;
+    method public java.sql.Date getDate(int) throws java.sql.SQLException;
+    method public java.sql.Date getDate(int, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Date getDate(String) throws java.sql.SQLException;
+    method public java.sql.Date getDate(String, java.util.Calendar) throws java.sql.SQLException;
+    method public double getDouble(int) throws java.sql.SQLException;
+    method public double getDouble(String) throws java.sql.SQLException;
+    method public float getFloat(int) throws java.sql.SQLException;
+    method public float getFloat(String) throws java.sql.SQLException;
+    method public int getInt(int) throws java.sql.SQLException;
+    method public int getInt(String) throws java.sql.SQLException;
+    method public long getLong(int) throws java.sql.SQLException;
+    method public long getLong(String) throws java.sql.SQLException;
+    method public java.io.Reader getNCharacterStream(int) throws java.sql.SQLException;
+    method public java.io.Reader getNCharacterStream(String) throws java.sql.SQLException;
+    method public java.sql.NClob getNClob(int) throws java.sql.SQLException;
+    method public java.sql.NClob getNClob(String) throws java.sql.SQLException;
+    method public String getNString(int) throws java.sql.SQLException;
+    method public String getNString(String) throws java.sql.SQLException;
+    method public Object getObject(int) throws java.sql.SQLException;
+    method public Object getObject(int, java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public Object getObject(String) throws java.sql.SQLException;
+    method public Object getObject(String, java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public java.sql.Ref getRef(int) throws java.sql.SQLException;
+    method public java.sql.Ref getRef(String) throws java.sql.SQLException;
+    method public java.sql.RowId getRowId(int) throws java.sql.SQLException;
+    method public java.sql.RowId getRowId(String) throws java.sql.SQLException;
+    method public java.sql.SQLXML getSQLXML(int) throws java.sql.SQLException;
+    method public java.sql.SQLXML getSQLXML(String) throws java.sql.SQLException;
+    method public short getShort(int) throws java.sql.SQLException;
+    method public short getShort(String) throws java.sql.SQLException;
+    method public String getString(int) throws java.sql.SQLException;
+    method public String getString(String) throws java.sql.SQLException;
+    method public java.sql.Time getTime(int) throws java.sql.SQLException;
+    method public java.sql.Time getTime(int, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Time getTime(String) throws java.sql.SQLException;
+    method public java.sql.Time getTime(String, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(int) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(int, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(String) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(String, java.util.Calendar) throws java.sql.SQLException;
+    method public java.net.URL getURL(int) throws java.sql.SQLException;
+    method public java.net.URL getURL(String) throws java.sql.SQLException;
+    method public void registerOutParameter(int, int) throws java.sql.SQLException;
+    method public void registerOutParameter(int, int, int) throws java.sql.SQLException;
+    method public void registerOutParameter(int, int, String) throws java.sql.SQLException;
+    method public void registerOutParameter(String, int) throws java.sql.SQLException;
+    method public void registerOutParameter(String, int, int) throws java.sql.SQLException;
+    method public void registerOutParameter(String, int, String) throws java.sql.SQLException;
+    method public void setAsciiStream(String, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setAsciiStream(String, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setAsciiStream(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBigDecimal(String, java.math.BigDecimal) throws java.sql.SQLException;
+    method public void setBinaryStream(String, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setBinaryStream(String, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setBinaryStream(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBlob(String, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setBlob(String, java.sql.Blob) throws java.sql.SQLException;
+    method public void setBlob(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBoolean(String, boolean) throws java.sql.SQLException;
+    method public void setByte(String, byte) throws java.sql.SQLException;
+    method public void setBytes(String, byte[]) throws java.sql.SQLException;
+    method public void setCharacterStream(String, java.io.Reader, int) throws java.sql.SQLException;
+    method public void setCharacterStream(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setCharacterStream(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setClob(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setClob(String, java.sql.Clob) throws java.sql.SQLException;
+    method public void setClob(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setDate(String, java.sql.Date) throws java.sql.SQLException;
+    method public void setDate(String, java.sql.Date, java.util.Calendar) throws java.sql.SQLException;
+    method public void setDouble(String, double) throws java.sql.SQLException;
+    method public void setFloat(String, float) throws java.sql.SQLException;
+    method public void setInt(String, int) throws java.sql.SQLException;
+    method public void setLong(String, long) throws java.sql.SQLException;
+    method public void setNCharacterStream(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNCharacterStream(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setNClob(String, java.sql.NClob) throws java.sql.SQLException;
+    method public void setNClob(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNClob(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setNString(String, String) throws java.sql.SQLException;
+    method public void setNull(String, int) throws java.sql.SQLException;
+    method public void setNull(String, int, String) throws java.sql.SQLException;
+    method public void setObject(String, Object, int, int) throws java.sql.SQLException;
+    method public void setObject(String, Object, int) throws java.sql.SQLException;
+    method public void setObject(String, Object) throws java.sql.SQLException;
+    method public void setRowId(String, java.sql.RowId) throws java.sql.SQLException;
+    method public void setSQLXML(String, java.sql.SQLXML) throws java.sql.SQLException;
+    method public void setShort(String, short) throws java.sql.SQLException;
+    method public void setString(String, String) throws java.sql.SQLException;
+    method public void setTime(String, java.sql.Time) throws java.sql.SQLException;
+    method public void setTime(String, java.sql.Time, java.util.Calendar) throws java.sql.SQLException;
+    method public void setTimestamp(String, java.sql.Timestamp) throws java.sql.SQLException;
+    method public void setTimestamp(String, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
+    method public void setURL(String, java.net.URL) throws java.sql.SQLException;
+    method public boolean wasNull() throws java.sql.SQLException;
+  }
+
+  public enum ClientInfoStatus {
+    enum_constant public static final java.sql.ClientInfoStatus REASON_UNKNOWN;
+    enum_constant public static final java.sql.ClientInfoStatus REASON_UNKNOWN_PROPERTY;
+    enum_constant public static final java.sql.ClientInfoStatus REASON_VALUE_INVALID;
+    enum_constant public static final java.sql.ClientInfoStatus REASON_VALUE_TRUNCATED;
+  }
+
+  public interface Clob {
+    method public void free() throws java.sql.SQLException;
+    method public java.io.InputStream getAsciiStream() throws java.sql.SQLException;
+    method public java.io.Reader getCharacterStream() throws java.sql.SQLException;
+    method public java.io.Reader getCharacterStream(long, long) throws java.sql.SQLException;
+    method public String getSubString(long, int) throws java.sql.SQLException;
+    method public long length() throws java.sql.SQLException;
+    method public long position(String, long) throws java.sql.SQLException;
+    method public long position(java.sql.Clob, long) throws java.sql.SQLException;
+    method public java.io.OutputStream setAsciiStream(long) throws java.sql.SQLException;
+    method public java.io.Writer setCharacterStream(long) throws java.sql.SQLException;
+    method public int setString(long, String) throws java.sql.SQLException;
+    method public int setString(long, String, int, int) throws java.sql.SQLException;
+    method public void truncate(long) throws java.sql.SQLException;
+  }
+
+  public interface Connection extends java.sql.Wrapper java.lang.AutoCloseable {
+    method public void clearWarnings() throws java.sql.SQLException;
+    method public void close() throws java.sql.SQLException;
+    method public void commit() throws java.sql.SQLException;
+    method public java.sql.Array createArrayOf(String, Object[]) throws java.sql.SQLException;
+    method public java.sql.Blob createBlob() throws java.sql.SQLException;
+    method public java.sql.Clob createClob() throws java.sql.SQLException;
+    method public java.sql.NClob createNClob() throws java.sql.SQLException;
+    method public java.sql.SQLXML createSQLXML() throws java.sql.SQLException;
+    method public java.sql.Statement createStatement() throws java.sql.SQLException;
+    method public java.sql.Statement createStatement(int, int) throws java.sql.SQLException;
+    method public java.sql.Statement createStatement(int, int, int) throws java.sql.SQLException;
+    method public java.sql.Struct createStruct(String, Object[]) throws java.sql.SQLException;
+    method public boolean getAutoCommit() throws java.sql.SQLException;
+    method public String getCatalog() throws java.sql.SQLException;
+    method public String getClientInfo(String) throws java.sql.SQLException;
+    method public java.util.Properties getClientInfo() throws java.sql.SQLException;
+    method public int getHoldability() throws java.sql.SQLException;
+    method public java.sql.DatabaseMetaData getMetaData() throws java.sql.SQLException;
+    method public int getTransactionIsolation() throws java.sql.SQLException;
+    method public java.util.Map<java.lang.String,java.lang.Class<?>> getTypeMap() throws java.sql.SQLException;
+    method public java.sql.SQLWarning getWarnings() throws java.sql.SQLException;
+    method public boolean isClosed() throws java.sql.SQLException;
+    method public boolean isReadOnly() throws java.sql.SQLException;
+    method public boolean isValid(int) throws java.sql.SQLException;
+    method public String nativeSQL(String) throws java.sql.SQLException;
+    method public java.sql.CallableStatement prepareCall(String) throws java.sql.SQLException;
+    method public java.sql.CallableStatement prepareCall(String, int, int) throws java.sql.SQLException;
+    method public java.sql.CallableStatement prepareCall(String, int, int, int) throws java.sql.SQLException;
+    method public java.sql.PreparedStatement prepareStatement(String) throws java.sql.SQLException;
+    method public java.sql.PreparedStatement prepareStatement(String, int, int) throws java.sql.SQLException;
+    method public java.sql.PreparedStatement prepareStatement(String, int, int, int) throws java.sql.SQLException;
+    method public java.sql.PreparedStatement prepareStatement(String, int) throws java.sql.SQLException;
+    method public java.sql.PreparedStatement prepareStatement(String, int[]) throws java.sql.SQLException;
+    method public java.sql.PreparedStatement prepareStatement(String, String[]) throws java.sql.SQLException;
+    method public void releaseSavepoint(java.sql.Savepoint) throws java.sql.SQLException;
+    method public void rollback() throws java.sql.SQLException;
+    method public void rollback(java.sql.Savepoint) throws java.sql.SQLException;
+    method public void setAutoCommit(boolean) throws java.sql.SQLException;
+    method public void setCatalog(String) throws java.sql.SQLException;
+    method public void setClientInfo(String, String) throws java.sql.SQLClientInfoException;
+    method public void setClientInfo(java.util.Properties) throws java.sql.SQLClientInfoException;
+    method public void setHoldability(int) throws java.sql.SQLException;
+    method public void setReadOnly(boolean) throws java.sql.SQLException;
+    method public java.sql.Savepoint setSavepoint() throws java.sql.SQLException;
+    method public java.sql.Savepoint setSavepoint(String) throws java.sql.SQLException;
+    method public void setTransactionIsolation(int) throws java.sql.SQLException;
+    method public void setTypeMap(java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    field public static final int TRANSACTION_NONE = 0; // 0x0
+    field public static final int TRANSACTION_READ_COMMITTED = 2; // 0x2
+    field public static final int TRANSACTION_READ_UNCOMMITTED = 1; // 0x1
+    field public static final int TRANSACTION_REPEATABLE_READ = 4; // 0x4
+    field public static final int TRANSACTION_SERIALIZABLE = 8; // 0x8
+  }
+
+  public class DataTruncation extends java.sql.SQLWarning {
+    ctor public DataTruncation(int, boolean, boolean, int, int);
+    ctor public DataTruncation(int, boolean, boolean, int, int, Throwable);
+    method public int getDataSize();
+    method public int getIndex();
+    method public boolean getParameter();
+    method public boolean getRead();
+    method public int getTransferSize();
+  }
+
+  public interface DatabaseMetaData extends java.sql.Wrapper {
+    method public boolean allProceduresAreCallable() throws java.sql.SQLException;
+    method public boolean allTablesAreSelectable() throws java.sql.SQLException;
+    method public boolean autoCommitFailureClosesAllResultSets() throws java.sql.SQLException;
+    method public boolean dataDefinitionCausesTransactionCommit() throws java.sql.SQLException;
+    method public boolean dataDefinitionIgnoredInTransactions() throws java.sql.SQLException;
+    method public boolean deletesAreDetected(int) throws java.sql.SQLException;
+    method public boolean doesMaxRowSizeIncludeBlobs() throws java.sql.SQLException;
+    method public java.sql.ResultSet getAttributes(String, String, String, String) throws java.sql.SQLException;
+    method public java.sql.ResultSet getBestRowIdentifier(String, String, String, int, boolean) throws java.sql.SQLException;
+    method public String getCatalogSeparator() throws java.sql.SQLException;
+    method public String getCatalogTerm() throws java.sql.SQLException;
+    method public java.sql.ResultSet getCatalogs() throws java.sql.SQLException;
+    method public java.sql.ResultSet getClientInfoProperties() throws java.sql.SQLException;
+    method public java.sql.ResultSet getColumnPrivileges(String, String, String, String) throws java.sql.SQLException;
+    method public java.sql.ResultSet getColumns(String, String, String, String) throws java.sql.SQLException;
+    method public java.sql.Connection getConnection() throws java.sql.SQLException;
+    method public java.sql.ResultSet getCrossReference(String, String, String, String, String, String) throws java.sql.SQLException;
+    method public int getDatabaseMajorVersion() throws java.sql.SQLException;
+    method public int getDatabaseMinorVersion() throws java.sql.SQLException;
+    method public String getDatabaseProductName() throws java.sql.SQLException;
+    method public String getDatabaseProductVersion() throws java.sql.SQLException;
+    method public int getDefaultTransactionIsolation() throws java.sql.SQLException;
+    method public int getDriverMajorVersion();
+    method public int getDriverMinorVersion();
+    method public String getDriverName() throws java.sql.SQLException;
+    method public String getDriverVersion() throws java.sql.SQLException;
+    method public java.sql.ResultSet getExportedKeys(String, String, String) throws java.sql.SQLException;
+    method public String getExtraNameCharacters() throws java.sql.SQLException;
+    method public java.sql.ResultSet getFunctionColumns(String, String, String, String) throws java.sql.SQLException;
+    method public java.sql.ResultSet getFunctions(String, String, String) throws java.sql.SQLException;
+    method public String getIdentifierQuoteString() throws java.sql.SQLException;
+    method public java.sql.ResultSet getImportedKeys(String, String, String) throws java.sql.SQLException;
+    method public java.sql.ResultSet getIndexInfo(String, String, String, boolean, boolean) throws java.sql.SQLException;
+    method public int getJDBCMajorVersion() throws java.sql.SQLException;
+    method public int getJDBCMinorVersion() throws java.sql.SQLException;
+    method public int getMaxBinaryLiteralLength() throws java.sql.SQLException;
+    method public int getMaxCatalogNameLength() throws java.sql.SQLException;
+    method public int getMaxCharLiteralLength() throws java.sql.SQLException;
+    method public int getMaxColumnNameLength() throws java.sql.SQLException;
+    method public int getMaxColumnsInGroupBy() throws java.sql.SQLException;
+    method public int getMaxColumnsInIndex() throws java.sql.SQLException;
+    method public int getMaxColumnsInOrderBy() throws java.sql.SQLException;
+    method public int getMaxColumnsInSelect() throws java.sql.SQLException;
+    method public int getMaxColumnsInTable() throws java.sql.SQLException;
+    method public int getMaxConnections() throws java.sql.SQLException;
+    method public int getMaxCursorNameLength() throws java.sql.SQLException;
+    method public int getMaxIndexLength() throws java.sql.SQLException;
+    method public int getMaxProcedureNameLength() throws java.sql.SQLException;
+    method public int getMaxRowSize() throws java.sql.SQLException;
+    method public int getMaxSchemaNameLength() throws java.sql.SQLException;
+    method public int getMaxStatementLength() throws java.sql.SQLException;
+    method public int getMaxStatements() throws java.sql.SQLException;
+    method public int getMaxTableNameLength() throws java.sql.SQLException;
+    method public int getMaxTablesInSelect() throws java.sql.SQLException;
+    method public int getMaxUserNameLength() throws java.sql.SQLException;
+    method public String getNumericFunctions() throws java.sql.SQLException;
+    method public java.sql.ResultSet getPrimaryKeys(String, String, String) throws java.sql.SQLException;
+    method public java.sql.ResultSet getProcedureColumns(String, String, String, String) throws java.sql.SQLException;
+    method public String getProcedureTerm() throws java.sql.SQLException;
+    method public java.sql.ResultSet getProcedures(String, String, String) throws java.sql.SQLException;
+    method public int getResultSetHoldability() throws java.sql.SQLException;
+    method public java.sql.RowIdLifetime getRowIdLifetime() throws java.sql.SQLException;
+    method public String getSQLKeywords() throws java.sql.SQLException;
+    method public int getSQLStateType() throws java.sql.SQLException;
+    method public String getSchemaTerm() throws java.sql.SQLException;
+    method public java.sql.ResultSet getSchemas() throws java.sql.SQLException;
+    method public java.sql.ResultSet getSchemas(String, String) throws java.sql.SQLException;
+    method public String getSearchStringEscape() throws java.sql.SQLException;
+    method public String getStringFunctions() throws java.sql.SQLException;
+    method public java.sql.ResultSet getSuperTables(String, String, String) throws java.sql.SQLException;
+    method public java.sql.ResultSet getSuperTypes(String, String, String) throws java.sql.SQLException;
+    method public String getSystemFunctions() throws java.sql.SQLException;
+    method public java.sql.ResultSet getTablePrivileges(String, String, String) throws java.sql.SQLException;
+    method public java.sql.ResultSet getTableTypes() throws java.sql.SQLException;
+    method public java.sql.ResultSet getTables(String, String, String, String[]) throws java.sql.SQLException;
+    method public String getTimeDateFunctions() throws java.sql.SQLException;
+    method public java.sql.ResultSet getTypeInfo() throws java.sql.SQLException;
+    method public java.sql.ResultSet getUDTs(String, String, String, int[]) throws java.sql.SQLException;
+    method public String getURL() throws java.sql.SQLException;
+    method public String getUserName() throws java.sql.SQLException;
+    method public java.sql.ResultSet getVersionColumns(String, String, String) throws java.sql.SQLException;
+    method public boolean insertsAreDetected(int) throws java.sql.SQLException;
+    method public boolean isCatalogAtStart() throws java.sql.SQLException;
+    method public boolean isReadOnly() throws java.sql.SQLException;
+    method public boolean locatorsUpdateCopy() throws java.sql.SQLException;
+    method public boolean nullPlusNonNullIsNull() throws java.sql.SQLException;
+    method public boolean nullsAreSortedAtEnd() throws java.sql.SQLException;
+    method public boolean nullsAreSortedAtStart() throws java.sql.SQLException;
+    method public boolean nullsAreSortedHigh() throws java.sql.SQLException;
+    method public boolean nullsAreSortedLow() throws java.sql.SQLException;
+    method public boolean othersDeletesAreVisible(int) throws java.sql.SQLException;
+    method public boolean othersInsertsAreVisible(int) throws java.sql.SQLException;
+    method public boolean othersUpdatesAreVisible(int) throws java.sql.SQLException;
+    method public boolean ownDeletesAreVisible(int) throws java.sql.SQLException;
+    method public boolean ownInsertsAreVisible(int) throws java.sql.SQLException;
+    method public boolean ownUpdatesAreVisible(int) throws java.sql.SQLException;
+    method public boolean storesLowerCaseIdentifiers() throws java.sql.SQLException;
+    method public boolean storesLowerCaseQuotedIdentifiers() throws java.sql.SQLException;
+    method public boolean storesMixedCaseIdentifiers() throws java.sql.SQLException;
+    method public boolean storesMixedCaseQuotedIdentifiers() throws java.sql.SQLException;
+    method public boolean storesUpperCaseIdentifiers() throws java.sql.SQLException;
+    method public boolean storesUpperCaseQuotedIdentifiers() throws java.sql.SQLException;
+    method public boolean supportsANSI92EntryLevelSQL() throws java.sql.SQLException;
+    method public boolean supportsANSI92FullSQL() throws java.sql.SQLException;
+    method public boolean supportsANSI92IntermediateSQL() throws java.sql.SQLException;
+    method public boolean supportsAlterTableWithAddColumn() throws java.sql.SQLException;
+    method public boolean supportsAlterTableWithDropColumn() throws java.sql.SQLException;
+    method public boolean supportsBatchUpdates() throws java.sql.SQLException;
+    method public boolean supportsCatalogsInDataManipulation() throws java.sql.SQLException;
+    method public boolean supportsCatalogsInIndexDefinitions() throws java.sql.SQLException;
+    method public boolean supportsCatalogsInPrivilegeDefinitions() throws java.sql.SQLException;
+    method public boolean supportsCatalogsInProcedureCalls() throws java.sql.SQLException;
+    method public boolean supportsCatalogsInTableDefinitions() throws java.sql.SQLException;
+    method public boolean supportsColumnAliasing() throws java.sql.SQLException;
+    method public boolean supportsConvert() throws java.sql.SQLException;
+    method public boolean supportsConvert(int, int) throws java.sql.SQLException;
+    method public boolean supportsCoreSQLGrammar() throws java.sql.SQLException;
+    method public boolean supportsCorrelatedSubqueries() throws java.sql.SQLException;
+    method public boolean supportsDataDefinitionAndDataManipulationTransactions() throws java.sql.SQLException;
+    method public boolean supportsDataManipulationTransactionsOnly() throws java.sql.SQLException;
+    method public boolean supportsDifferentTableCorrelationNames() throws java.sql.SQLException;
+    method public boolean supportsExpressionsInOrderBy() throws java.sql.SQLException;
+    method public boolean supportsExtendedSQLGrammar() throws java.sql.SQLException;
+    method public boolean supportsFullOuterJoins() throws java.sql.SQLException;
+    method public boolean supportsGetGeneratedKeys() throws java.sql.SQLException;
+    method public boolean supportsGroupBy() throws java.sql.SQLException;
+    method public boolean supportsGroupByBeyondSelect() throws java.sql.SQLException;
+    method public boolean supportsGroupByUnrelated() throws java.sql.SQLException;
+    method public boolean supportsIntegrityEnhancementFacility() throws java.sql.SQLException;
+    method public boolean supportsLikeEscapeClause() throws java.sql.SQLException;
+    method public boolean supportsLimitedOuterJoins() throws java.sql.SQLException;
+    method public boolean supportsMinimumSQLGrammar() throws java.sql.SQLException;
+    method public boolean supportsMixedCaseIdentifiers() throws java.sql.SQLException;
+    method public boolean supportsMixedCaseQuotedIdentifiers() throws java.sql.SQLException;
+    method public boolean supportsMultipleOpenResults() throws java.sql.SQLException;
+    method public boolean supportsMultipleResultSets() throws java.sql.SQLException;
+    method public boolean supportsMultipleTransactions() throws java.sql.SQLException;
+    method public boolean supportsNamedParameters() throws java.sql.SQLException;
+    method public boolean supportsNonNullableColumns() throws java.sql.SQLException;
+    method public boolean supportsOpenCursorsAcrossCommit() throws java.sql.SQLException;
+    method public boolean supportsOpenCursorsAcrossRollback() throws java.sql.SQLException;
+    method public boolean supportsOpenStatementsAcrossCommit() throws java.sql.SQLException;
+    method public boolean supportsOpenStatementsAcrossRollback() throws java.sql.SQLException;
+    method public boolean supportsOrderByUnrelated() throws java.sql.SQLException;
+    method public boolean supportsOuterJoins() throws java.sql.SQLException;
+    method public boolean supportsPositionedDelete() throws java.sql.SQLException;
+    method public boolean supportsPositionedUpdate() throws java.sql.SQLException;
+    method public boolean supportsResultSetConcurrency(int, int) throws java.sql.SQLException;
+    method public boolean supportsResultSetHoldability(int) throws java.sql.SQLException;
+    method public boolean supportsResultSetType(int) throws java.sql.SQLException;
+    method public boolean supportsSavepoints() throws java.sql.SQLException;
+    method public boolean supportsSchemasInDataManipulation() throws java.sql.SQLException;
+    method public boolean supportsSchemasInIndexDefinitions() throws java.sql.SQLException;
+    method public boolean supportsSchemasInPrivilegeDefinitions() throws java.sql.SQLException;
+    method public boolean supportsSchemasInProcedureCalls() throws java.sql.SQLException;
+    method public boolean supportsSchemasInTableDefinitions() throws java.sql.SQLException;
+    method public boolean supportsSelectForUpdate() throws java.sql.SQLException;
+    method public boolean supportsStatementPooling() throws java.sql.SQLException;
+    method public boolean supportsStoredFunctionsUsingCallSyntax() throws java.sql.SQLException;
+    method public boolean supportsStoredProcedures() throws java.sql.SQLException;
+    method public boolean supportsSubqueriesInComparisons() throws java.sql.SQLException;
+    method public boolean supportsSubqueriesInExists() throws java.sql.SQLException;
+    method public boolean supportsSubqueriesInIns() throws java.sql.SQLException;
+    method public boolean supportsSubqueriesInQuantifieds() throws java.sql.SQLException;
+    method public boolean supportsTableCorrelationNames() throws java.sql.SQLException;
+    method public boolean supportsTransactionIsolationLevel(int) throws java.sql.SQLException;
+    method public boolean supportsTransactions() throws java.sql.SQLException;
+    method public boolean supportsUnion() throws java.sql.SQLException;
+    method public boolean supportsUnionAll() throws java.sql.SQLException;
+    method public boolean updatesAreDetected(int) throws java.sql.SQLException;
+    method public boolean usesLocalFilePerTable() throws java.sql.SQLException;
+    method public boolean usesLocalFiles() throws java.sql.SQLException;
+    field public static final short attributeNoNulls = 0; // 0x0
+    field public static final short attributeNullable = 1; // 0x1
+    field public static final short attributeNullableUnknown = 2; // 0x2
+    field public static final int bestRowNotPseudo = 1; // 0x1
+    field public static final int bestRowPseudo = 2; // 0x2
+    field public static final int bestRowSession = 2; // 0x2
+    field public static final int bestRowTemporary = 0; // 0x0
+    field public static final int bestRowTransaction = 1; // 0x1
+    field public static final int bestRowUnknown = 0; // 0x0
+    field public static final int columnNoNulls = 0; // 0x0
+    field public static final int columnNullable = 1; // 0x1
+    field public static final int columnNullableUnknown = 2; // 0x2
+    field public static final int functionColumnIn = 1; // 0x1
+    field public static final int functionColumnInOut = 2; // 0x2
+    field public static final int functionColumnOut = 3; // 0x3
+    field public static final int functionColumnResult = 5; // 0x5
+    field public static final int functionColumnUnknown = 0; // 0x0
+    field public static final int functionNoNulls = 0; // 0x0
+    field public static final int functionNoTable = 1; // 0x1
+    field public static final int functionNullable = 1; // 0x1
+    field public static final int functionNullableUnknown = 2; // 0x2
+    field public static final int functionResultUnknown = 0; // 0x0
+    field public static final int functionReturn = 4; // 0x4
+    field public static final int functionReturnsTable = 2; // 0x2
+    field public static final int importedKeyCascade = 0; // 0x0
+    field public static final int importedKeyInitiallyDeferred = 5; // 0x5
+    field public static final int importedKeyInitiallyImmediate = 6; // 0x6
+    field public static final int importedKeyNoAction = 3; // 0x3
+    field public static final int importedKeyNotDeferrable = 7; // 0x7
+    field public static final int importedKeyRestrict = 1; // 0x1
+    field public static final int importedKeySetDefault = 4; // 0x4
+    field public static final int importedKeySetNull = 2; // 0x2
+    field public static final int procedureColumnIn = 1; // 0x1
+    field public static final int procedureColumnInOut = 2; // 0x2
+    field public static final int procedureColumnOut = 4; // 0x4
+    field public static final int procedureColumnResult = 3; // 0x3
+    field public static final int procedureColumnReturn = 5; // 0x5
+    field public static final int procedureColumnUnknown = 0; // 0x0
+    field public static final int procedureNoNulls = 0; // 0x0
+    field public static final int procedureNoResult = 1; // 0x1
+    field public static final int procedureNullable = 1; // 0x1
+    field public static final int procedureNullableUnknown = 2; // 0x2
+    field public static final int procedureResultUnknown = 0; // 0x0
+    field public static final int procedureReturnsResult = 2; // 0x2
+    field public static final int sqlStateSQL = 2; // 0x2
+    field public static final int sqlStateSQL99 = 2; // 0x2
+    field public static final int sqlStateXOpen = 1; // 0x1
+    field public static final short tableIndexClustered = 1; // 0x1
+    field public static final short tableIndexHashed = 2; // 0x2
+    field public static final short tableIndexOther = 3; // 0x3
+    field public static final short tableIndexStatistic = 0; // 0x0
+    field public static final int typeNoNulls = 0; // 0x0
+    field public static final int typeNullable = 1; // 0x1
+    field public static final int typeNullableUnknown = 2; // 0x2
+    field public static final int typePredBasic = 2; // 0x2
+    field public static final int typePredChar = 1; // 0x1
+    field public static final int typePredNone = 0; // 0x0
+    field public static final int typeSearchable = 3; // 0x3
+    field public static final int versionColumnNotPseudo = 1; // 0x1
+    field public static final int versionColumnPseudo = 2; // 0x2
+    field public static final int versionColumnUnknown = 0; // 0x0
+  }
+
+  public class Date extends java.util.Date {
+    ctor @Deprecated public Date(int, int, int);
+    ctor public Date(long);
+    method public static java.sql.Date valueOf(String);
+  }
+
+  public interface Driver {
+    method public boolean acceptsURL(String) throws java.sql.SQLException;
+    method public java.sql.Connection connect(String, java.util.Properties) throws java.sql.SQLException;
+    method public int getMajorVersion();
+    method public int getMinorVersion();
+    method public java.sql.DriverPropertyInfo[] getPropertyInfo(String, java.util.Properties) throws java.sql.SQLException;
+    method public boolean jdbcCompliant();
+  }
+
+  public class DriverManager {
+    method public static void deregisterDriver(java.sql.Driver) throws java.sql.SQLException;
+    method public static java.sql.Connection getConnection(String, java.util.Properties) throws java.sql.SQLException;
+    method public static java.sql.Connection getConnection(String, String, String) throws java.sql.SQLException;
+    method public static java.sql.Connection getConnection(String) throws java.sql.SQLException;
+    method public static java.sql.Driver getDriver(String) throws java.sql.SQLException;
+    method public static java.util.Enumeration<java.sql.Driver> getDrivers();
+    method @Deprecated public static java.io.PrintStream getLogStream();
+    method public static java.io.PrintWriter getLogWriter();
+    method public static int getLoginTimeout();
+    method public static void println(String);
+    method public static void registerDriver(java.sql.Driver) throws java.sql.SQLException;
+    method @Deprecated public static void setLogStream(java.io.PrintStream);
+    method public static void setLogWriter(java.io.PrintWriter);
+    method public static void setLoginTimeout(int);
+  }
+
+  public class DriverPropertyInfo {
+    ctor public DriverPropertyInfo(String, String);
+    field public String[] choices;
+    field public String description;
+    field public String name;
+    field public boolean required;
+    field public String value;
+  }
+
+  public interface NClob extends java.sql.Clob {
+  }
+
+  public interface ParameterMetaData extends java.sql.Wrapper {
+    method public String getParameterClassName(int) throws java.sql.SQLException;
+    method public int getParameterCount() throws java.sql.SQLException;
+    method public int getParameterMode(int) throws java.sql.SQLException;
+    method public int getParameterType(int) throws java.sql.SQLException;
+    method public String getParameterTypeName(int) throws java.sql.SQLException;
+    method public int getPrecision(int) throws java.sql.SQLException;
+    method public int getScale(int) throws java.sql.SQLException;
+    method public int isNullable(int) throws java.sql.SQLException;
+    method public boolean isSigned(int) throws java.sql.SQLException;
+    field public static final int parameterModeIn = 1; // 0x1
+    field public static final int parameterModeInOut = 2; // 0x2
+    field public static final int parameterModeOut = 4; // 0x4
+    field public static final int parameterModeUnknown = 0; // 0x0
+    field public static final int parameterNoNulls = 0; // 0x0
+    field public static final int parameterNullable = 1; // 0x1
+    field public static final int parameterNullableUnknown = 2; // 0x2
+  }
+
+  public interface PreparedStatement extends java.sql.Statement {
+    method public void addBatch() throws java.sql.SQLException;
+    method public void clearParameters() throws java.sql.SQLException;
+    method public boolean execute() throws java.sql.SQLException;
+    method public java.sql.ResultSet executeQuery() throws java.sql.SQLException;
+    method public int executeUpdate() throws java.sql.SQLException;
+    method public java.sql.ResultSetMetaData getMetaData() throws java.sql.SQLException;
+    method public java.sql.ParameterMetaData getParameterMetaData() throws java.sql.SQLException;
+    method public void setArray(int, java.sql.Array) throws java.sql.SQLException;
+    method public void setAsciiStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setAsciiStream(int, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setAsciiStream(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBigDecimal(int, java.math.BigDecimal) throws java.sql.SQLException;
+    method public void setBinaryStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setBinaryStream(int, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setBinaryStream(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBlob(int, java.sql.Blob) throws java.sql.SQLException;
+    method public void setBlob(int, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setBlob(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBoolean(int, boolean) throws java.sql.SQLException;
+    method public void setByte(int, byte) throws java.sql.SQLException;
+    method public void setBytes(int, byte[]) throws java.sql.SQLException;
+    method public void setCharacterStream(int, java.io.Reader, int) throws java.sql.SQLException;
+    method public void setCharacterStream(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setClob(int, java.sql.Clob) throws java.sql.SQLException;
+    method public void setClob(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setClob(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setDate(int, java.sql.Date) throws java.sql.SQLException;
+    method public void setDate(int, java.sql.Date, java.util.Calendar) throws java.sql.SQLException;
+    method public void setDouble(int, double) throws java.sql.SQLException;
+    method public void setFloat(int, float) throws java.sql.SQLException;
+    method public void setInt(int, int) throws java.sql.SQLException;
+    method public void setLong(int, long) throws java.sql.SQLException;
+    method public void setNCharacterStream(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setNClob(int, java.sql.NClob) throws java.sql.SQLException;
+    method public void setNClob(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNClob(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setNString(int, String) throws java.sql.SQLException;
+    method public void setNull(int, int) throws java.sql.SQLException;
+    method public void setNull(int, int, String) throws java.sql.SQLException;
+    method public void setObject(int, Object, int) throws java.sql.SQLException;
+    method public void setObject(int, Object) throws java.sql.SQLException;
+    method public void setObject(int, Object, int, int) throws java.sql.SQLException;
+    method public void setRef(int, java.sql.Ref) throws java.sql.SQLException;
+    method public void setRowId(int, java.sql.RowId) throws java.sql.SQLException;
+    method public void setSQLXML(int, java.sql.SQLXML) throws java.sql.SQLException;
+    method public void setShort(int, short) throws java.sql.SQLException;
+    method public void setString(int, String) throws java.sql.SQLException;
+    method public void setTime(int, java.sql.Time) throws java.sql.SQLException;
+    method public void setTime(int, java.sql.Time, java.util.Calendar) throws java.sql.SQLException;
+    method public void setTimestamp(int, java.sql.Timestamp) throws java.sql.SQLException;
+    method public void setTimestamp(int, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
+    method public void setURL(int, java.net.URL) throws java.sql.SQLException;
+    method @Deprecated public void setUnicodeStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+  }
+
+  public interface Ref {
+    method public String getBaseTypeName() throws java.sql.SQLException;
+    method public Object getObject(java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public Object getObject() throws java.sql.SQLException;
+    method public void setObject(Object) throws java.sql.SQLException;
+  }
+
+  public interface ResultSet extends java.sql.Wrapper java.lang.AutoCloseable {
+    method public boolean absolute(int) throws java.sql.SQLException;
+    method public void afterLast() throws java.sql.SQLException;
+    method public void beforeFirst() throws java.sql.SQLException;
+    method public void cancelRowUpdates() throws java.sql.SQLException;
+    method public void clearWarnings() throws java.sql.SQLException;
+    method public void close() throws java.sql.SQLException;
+    method public void deleteRow() throws java.sql.SQLException;
+    method public int findColumn(String) throws java.sql.SQLException;
+    method public boolean first() throws java.sql.SQLException;
+    method public java.sql.Array getArray(int) throws java.sql.SQLException;
+    method public java.sql.Array getArray(String) throws java.sql.SQLException;
+    method public java.io.InputStream getAsciiStream(int) throws java.sql.SQLException;
+    method public java.io.InputStream getAsciiStream(String) throws java.sql.SQLException;
+    method @Deprecated public java.math.BigDecimal getBigDecimal(int, int) throws java.sql.SQLException;
+    method @Deprecated public java.math.BigDecimal getBigDecimal(String, int) throws java.sql.SQLException;
+    method public java.math.BigDecimal getBigDecimal(int) throws java.sql.SQLException;
+    method public java.math.BigDecimal getBigDecimal(String) throws java.sql.SQLException;
+    method public java.io.InputStream getBinaryStream(int) throws java.sql.SQLException;
+    method public java.io.InputStream getBinaryStream(String) throws java.sql.SQLException;
+    method public java.sql.Blob getBlob(int) throws java.sql.SQLException;
+    method public java.sql.Blob getBlob(String) throws java.sql.SQLException;
+    method public boolean getBoolean(int) throws java.sql.SQLException;
+    method public boolean getBoolean(String) throws java.sql.SQLException;
+    method public byte getByte(int) throws java.sql.SQLException;
+    method public byte getByte(String) throws java.sql.SQLException;
+    method public byte[] getBytes(int) throws java.sql.SQLException;
+    method public byte[] getBytes(String) throws java.sql.SQLException;
+    method public java.io.Reader getCharacterStream(int) throws java.sql.SQLException;
+    method public java.io.Reader getCharacterStream(String) throws java.sql.SQLException;
+    method public java.sql.Clob getClob(int) throws java.sql.SQLException;
+    method public java.sql.Clob getClob(String) throws java.sql.SQLException;
+    method public int getConcurrency() throws java.sql.SQLException;
+    method public String getCursorName() throws java.sql.SQLException;
+    method public java.sql.Date getDate(int) throws java.sql.SQLException;
+    method public java.sql.Date getDate(String) throws java.sql.SQLException;
+    method public java.sql.Date getDate(int, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Date getDate(String, java.util.Calendar) throws java.sql.SQLException;
+    method public double getDouble(int) throws java.sql.SQLException;
+    method public double getDouble(String) throws java.sql.SQLException;
+    method public int getFetchDirection() throws java.sql.SQLException;
+    method public int getFetchSize() throws java.sql.SQLException;
+    method public float getFloat(int) throws java.sql.SQLException;
+    method public float getFloat(String) throws java.sql.SQLException;
+    method public int getHoldability() throws java.sql.SQLException;
+    method public int getInt(int) throws java.sql.SQLException;
+    method public int getInt(String) throws java.sql.SQLException;
+    method public long getLong(int) throws java.sql.SQLException;
+    method public long getLong(String) throws java.sql.SQLException;
+    method public java.sql.ResultSetMetaData getMetaData() throws java.sql.SQLException;
+    method public java.io.Reader getNCharacterStream(int) throws java.sql.SQLException;
+    method public java.io.Reader getNCharacterStream(String) throws java.sql.SQLException;
+    method public java.sql.NClob getNClob(int) throws java.sql.SQLException;
+    method public java.sql.NClob getNClob(String) throws java.sql.SQLException;
+    method public String getNString(int) throws java.sql.SQLException;
+    method public String getNString(String) throws java.sql.SQLException;
+    method public Object getObject(int) throws java.sql.SQLException;
+    method public Object getObject(String) throws java.sql.SQLException;
+    method public Object getObject(int, java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public Object getObject(String, java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public java.sql.Ref getRef(int) throws java.sql.SQLException;
+    method public java.sql.Ref getRef(String) throws java.sql.SQLException;
+    method public int getRow() throws java.sql.SQLException;
+    method public java.sql.RowId getRowId(int) throws java.sql.SQLException;
+    method public java.sql.RowId getRowId(String) throws java.sql.SQLException;
+    method public java.sql.SQLXML getSQLXML(int) throws java.sql.SQLException;
+    method public java.sql.SQLXML getSQLXML(String) throws java.sql.SQLException;
+    method public short getShort(int) throws java.sql.SQLException;
+    method public short getShort(String) throws java.sql.SQLException;
+    method public java.sql.Statement getStatement() throws java.sql.SQLException;
+    method public String getString(int) throws java.sql.SQLException;
+    method public String getString(String) throws java.sql.SQLException;
+    method public java.sql.Time getTime(int) throws java.sql.SQLException;
+    method public java.sql.Time getTime(String) throws java.sql.SQLException;
+    method public java.sql.Time getTime(int, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Time getTime(String, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(int) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(String) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(int, java.util.Calendar) throws java.sql.SQLException;
+    method public java.sql.Timestamp getTimestamp(String, java.util.Calendar) throws java.sql.SQLException;
+    method public int getType() throws java.sql.SQLException;
+    method public java.net.URL getURL(int) throws java.sql.SQLException;
+    method public java.net.URL getURL(String) throws java.sql.SQLException;
+    method @Deprecated public java.io.InputStream getUnicodeStream(int) throws java.sql.SQLException;
+    method @Deprecated public java.io.InputStream getUnicodeStream(String) throws java.sql.SQLException;
+    method public java.sql.SQLWarning getWarnings() throws java.sql.SQLException;
+    method public void insertRow() throws java.sql.SQLException;
+    method public boolean isAfterLast() throws java.sql.SQLException;
+    method public boolean isBeforeFirst() throws java.sql.SQLException;
+    method public boolean isClosed() throws java.sql.SQLException;
+    method public boolean isFirst() throws java.sql.SQLException;
+    method public boolean isLast() throws java.sql.SQLException;
+    method public boolean last() throws java.sql.SQLException;
+    method public void moveToCurrentRow() throws java.sql.SQLException;
+    method public void moveToInsertRow() throws java.sql.SQLException;
+    method public boolean next() throws java.sql.SQLException;
+    method public boolean previous() throws java.sql.SQLException;
+    method public void refreshRow() throws java.sql.SQLException;
+    method public boolean relative(int) throws java.sql.SQLException;
+    method public boolean rowDeleted() throws java.sql.SQLException;
+    method public boolean rowInserted() throws java.sql.SQLException;
+    method public boolean rowUpdated() throws java.sql.SQLException;
+    method public void setFetchDirection(int) throws java.sql.SQLException;
+    method public void setFetchSize(int) throws java.sql.SQLException;
+    method public void updateArray(int, java.sql.Array) throws java.sql.SQLException;
+    method public void updateArray(String, java.sql.Array) throws java.sql.SQLException;
+    method public void updateAsciiStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void updateAsciiStream(String, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void updateAsciiStream(int, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void updateAsciiStream(String, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void updateAsciiStream(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void updateAsciiStream(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void updateBigDecimal(int, java.math.BigDecimal) throws java.sql.SQLException;
+    method public void updateBigDecimal(String, java.math.BigDecimal) throws java.sql.SQLException;
+    method public void updateBinaryStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void updateBinaryStream(String, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void updateBinaryStream(int, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void updateBinaryStream(String, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void updateBinaryStream(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void updateBinaryStream(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void updateBlob(int, java.sql.Blob) throws java.sql.SQLException;
+    method public void updateBlob(String, java.sql.Blob) throws java.sql.SQLException;
+    method public void updateBlob(int, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void updateBlob(String, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void updateBlob(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void updateBlob(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void updateBoolean(int, boolean) throws java.sql.SQLException;
+    method public void updateBoolean(String, boolean) throws java.sql.SQLException;
+    method public void updateByte(int, byte) throws java.sql.SQLException;
+    method public void updateByte(String, byte) throws java.sql.SQLException;
+    method public void updateBytes(int, byte[]) throws java.sql.SQLException;
+    method public void updateBytes(String, byte[]) throws java.sql.SQLException;
+    method public void updateCharacterStream(int, java.io.Reader, int) throws java.sql.SQLException;
+    method public void updateCharacterStream(String, java.io.Reader, int) throws java.sql.SQLException;
+    method public void updateCharacterStream(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateCharacterStream(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
+    method public void updateCharacterStream(String, java.io.Reader) throws java.sql.SQLException;
+    method public void updateClob(int, java.sql.Clob) throws java.sql.SQLException;
+    method public void updateClob(String, java.sql.Clob) throws java.sql.SQLException;
+    method public void updateClob(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateClob(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateClob(int, java.io.Reader) throws java.sql.SQLException;
+    method public void updateClob(String, java.io.Reader) throws java.sql.SQLException;
+    method public void updateDate(int, java.sql.Date) throws java.sql.SQLException;
+    method public void updateDate(String, java.sql.Date) throws java.sql.SQLException;
+    method public void updateDouble(int, double) throws java.sql.SQLException;
+    method public void updateDouble(String, double) throws java.sql.SQLException;
+    method public void updateFloat(int, float) throws java.sql.SQLException;
+    method public void updateFloat(String, float) throws java.sql.SQLException;
+    method public void updateInt(int, int) throws java.sql.SQLException;
+    method public void updateInt(String, int) throws java.sql.SQLException;
+    method public void updateLong(int, long) throws java.sql.SQLException;
+    method public void updateLong(String, long) throws java.sql.SQLException;
+    method public void updateNCharacterStream(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateNCharacterStream(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateNCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
+    method public void updateNCharacterStream(String, java.io.Reader) throws java.sql.SQLException;
+    method public void updateNClob(int, java.sql.NClob) throws java.sql.SQLException;
+    method public void updateNClob(String, java.sql.NClob) throws java.sql.SQLException;
+    method public void updateNClob(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateNClob(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void updateNClob(int, java.io.Reader) throws java.sql.SQLException;
+    method public void updateNClob(String, java.io.Reader) throws java.sql.SQLException;
+    method public void updateNString(int, String) throws java.sql.SQLException;
+    method public void updateNString(String, String) throws java.sql.SQLException;
+    method public void updateNull(int) throws java.sql.SQLException;
+    method public void updateNull(String) throws java.sql.SQLException;
+    method public void updateObject(int, Object, int) throws java.sql.SQLException;
+    method public void updateObject(int, Object) throws java.sql.SQLException;
+    method public void updateObject(String, Object, int) throws java.sql.SQLException;
+    method public void updateObject(String, Object) throws java.sql.SQLException;
+    method public void updateRef(int, java.sql.Ref) throws java.sql.SQLException;
+    method public void updateRef(String, java.sql.Ref) throws java.sql.SQLException;
+    method public void updateRow() throws java.sql.SQLException;
+    method public void updateRowId(int, java.sql.RowId) throws java.sql.SQLException;
+    method public void updateRowId(String, java.sql.RowId) throws java.sql.SQLException;
+    method public void updateSQLXML(int, java.sql.SQLXML) throws java.sql.SQLException;
+    method public void updateSQLXML(String, java.sql.SQLXML) throws java.sql.SQLException;
+    method public void updateShort(int, short) throws java.sql.SQLException;
+    method public void updateShort(String, short) throws java.sql.SQLException;
+    method public void updateString(int, String) throws java.sql.SQLException;
+    method public void updateString(String, String) throws java.sql.SQLException;
+    method public void updateTime(int, java.sql.Time) throws java.sql.SQLException;
+    method public void updateTime(String, java.sql.Time) throws java.sql.SQLException;
+    method public void updateTimestamp(int, java.sql.Timestamp) throws java.sql.SQLException;
+    method public void updateTimestamp(String, java.sql.Timestamp) throws java.sql.SQLException;
+    method public boolean wasNull() throws java.sql.SQLException;
+    field public static final int CLOSE_CURSORS_AT_COMMIT = 2; // 0x2
+    field public static final int CONCUR_READ_ONLY = 1007; // 0x3ef
+    field public static final int CONCUR_UPDATABLE = 1008; // 0x3f0
+    field public static final int FETCH_FORWARD = 1000; // 0x3e8
+    field public static final int FETCH_REVERSE = 1001; // 0x3e9
+    field public static final int FETCH_UNKNOWN = 1002; // 0x3ea
+    field public static final int HOLD_CURSORS_OVER_COMMIT = 1; // 0x1
+    field public static final int TYPE_FORWARD_ONLY = 1003; // 0x3eb
+    field public static final int TYPE_SCROLL_INSENSITIVE = 1004; // 0x3ec
+    field public static final int TYPE_SCROLL_SENSITIVE = 1005; // 0x3ed
+  }
+
+  public interface ResultSetMetaData extends java.sql.Wrapper {
+    method public String getCatalogName(int) throws java.sql.SQLException;
+    method public String getColumnClassName(int) throws java.sql.SQLException;
+    method public int getColumnCount() throws java.sql.SQLException;
+    method public int getColumnDisplaySize(int) throws java.sql.SQLException;
+    method public String getColumnLabel(int) throws java.sql.SQLException;
+    method public String getColumnName(int) throws java.sql.SQLException;
+    method public int getColumnType(int) throws java.sql.SQLException;
+    method public String getColumnTypeName(int) throws java.sql.SQLException;
+    method public int getPrecision(int) throws java.sql.SQLException;
+    method public int getScale(int) throws java.sql.SQLException;
+    method public String getSchemaName(int) throws java.sql.SQLException;
+    method public String getTableName(int) throws java.sql.SQLException;
+    method public boolean isAutoIncrement(int) throws java.sql.SQLException;
+    method public boolean isCaseSensitive(int) throws java.sql.SQLException;
+    method public boolean isCurrency(int) throws java.sql.SQLException;
+    method public boolean isDefinitelyWritable(int) throws java.sql.SQLException;
+    method public int isNullable(int) throws java.sql.SQLException;
+    method public boolean isReadOnly(int) throws java.sql.SQLException;
+    method public boolean isSearchable(int) throws java.sql.SQLException;
+    method public boolean isSigned(int) throws java.sql.SQLException;
+    method public boolean isWritable(int) throws java.sql.SQLException;
+    field public static final int columnNoNulls = 0; // 0x0
+    field public static final int columnNullable = 1; // 0x1
+    field public static final int columnNullableUnknown = 2; // 0x2
+  }
+
+  public interface RowId {
+    method public boolean equals(Object);
+    method public byte[] getBytes();
+    method public int hashCode();
+    method public String toString();
+  }
+
+  public enum RowIdLifetime {
+    enum_constant public static final java.sql.RowIdLifetime ROWID_UNSUPPORTED;
+    enum_constant public static final java.sql.RowIdLifetime ROWID_VALID_FOREVER;
+    enum_constant public static final java.sql.RowIdLifetime ROWID_VALID_OTHER;
+    enum_constant public static final java.sql.RowIdLifetime ROWID_VALID_SESSION;
+    enum_constant public static final java.sql.RowIdLifetime ROWID_VALID_TRANSACTION;
+  }
+
+  public class SQLClientInfoException extends java.sql.SQLException {
+    ctor public SQLClientInfoException();
+    ctor public SQLClientInfoException(java.util.Map<java.lang.String,java.sql.ClientInfoStatus>);
+    ctor public SQLClientInfoException(java.util.Map<java.lang.String,java.sql.ClientInfoStatus>, Throwable);
+    ctor public SQLClientInfoException(String, java.util.Map<java.lang.String,java.sql.ClientInfoStatus>);
+    ctor public SQLClientInfoException(String, java.util.Map<java.lang.String,java.sql.ClientInfoStatus>, Throwable);
+    ctor public SQLClientInfoException(String, String, java.util.Map<java.lang.String,java.sql.ClientInfoStatus>);
+    ctor public SQLClientInfoException(String, String, java.util.Map<java.lang.String,java.sql.ClientInfoStatus>, Throwable);
+    ctor public SQLClientInfoException(String, String, int, java.util.Map<java.lang.String,java.sql.ClientInfoStatus>);
+    ctor public SQLClientInfoException(String, String, int, java.util.Map<java.lang.String,java.sql.ClientInfoStatus>, Throwable);
+    method public java.util.Map<java.lang.String,java.sql.ClientInfoStatus> getFailedProperties();
+  }
+
+  public interface SQLData {
+    method public String getSQLTypeName() throws java.sql.SQLException;
+    method public void readSQL(java.sql.SQLInput, String) throws java.sql.SQLException;
+    method public void writeSQL(java.sql.SQLOutput) throws java.sql.SQLException;
+  }
+
+  public class SQLDataException extends java.sql.SQLNonTransientException {
+    ctor public SQLDataException();
+    ctor public SQLDataException(String);
+    ctor public SQLDataException(String, String);
+    ctor public SQLDataException(String, String, int);
+    ctor public SQLDataException(Throwable);
+    ctor public SQLDataException(String, Throwable);
+    ctor public SQLDataException(String, String, Throwable);
+    ctor public SQLDataException(String, String, int, Throwable);
+  }
+
+  public class SQLException extends java.lang.Exception implements java.lang.Iterable<java.lang.Throwable> {
+    ctor public SQLException(String, String, int);
+    ctor public SQLException(String, String);
+    ctor public SQLException(String);
+    ctor public SQLException();
+    ctor public SQLException(Throwable);
+    ctor public SQLException(String, Throwable);
+    ctor public SQLException(String, String, Throwable);
+    ctor public SQLException(String, String, int, Throwable);
+    method public int getErrorCode();
+    method public java.sql.SQLException getNextException();
+    method public String getSQLState();
+    method public java.util.Iterator<java.lang.Throwable> iterator();
+    method public void setNextException(java.sql.SQLException);
+  }
+
+  public class SQLFeatureNotSupportedException extends java.sql.SQLNonTransientException {
+    ctor public SQLFeatureNotSupportedException();
+    ctor public SQLFeatureNotSupportedException(String);
+    ctor public SQLFeatureNotSupportedException(String, String);
+    ctor public SQLFeatureNotSupportedException(String, String, int);
+    ctor public SQLFeatureNotSupportedException(Throwable);
+    ctor public SQLFeatureNotSupportedException(String, Throwable);
+    ctor public SQLFeatureNotSupportedException(String, String, Throwable);
+    ctor public SQLFeatureNotSupportedException(String, String, int, Throwable);
+  }
+
+  public interface SQLInput {
+    method public java.sql.Array readArray() throws java.sql.SQLException;
+    method public java.io.InputStream readAsciiStream() throws java.sql.SQLException;
+    method public java.math.BigDecimal readBigDecimal() throws java.sql.SQLException;
+    method public java.io.InputStream readBinaryStream() throws java.sql.SQLException;
+    method public java.sql.Blob readBlob() throws java.sql.SQLException;
+    method public boolean readBoolean() throws java.sql.SQLException;
+    method public byte readByte() throws java.sql.SQLException;
+    method public byte[] readBytes() throws java.sql.SQLException;
+    method public java.io.Reader readCharacterStream() throws java.sql.SQLException;
+    method public java.sql.Clob readClob() throws java.sql.SQLException;
+    method public java.sql.Date readDate() throws java.sql.SQLException;
+    method public double readDouble() throws java.sql.SQLException;
+    method public float readFloat() throws java.sql.SQLException;
+    method public int readInt() throws java.sql.SQLException;
+    method public long readLong() throws java.sql.SQLException;
+    method public java.sql.NClob readNClob() throws java.sql.SQLException;
+    method public String readNString() throws java.sql.SQLException;
+    method public Object readObject() throws java.sql.SQLException;
+    method public java.sql.Ref readRef() throws java.sql.SQLException;
+    method public java.sql.RowId readRowId() throws java.sql.SQLException;
+    method public java.sql.SQLXML readSQLXML() throws java.sql.SQLException;
+    method public short readShort() throws java.sql.SQLException;
+    method public String readString() throws java.sql.SQLException;
+    method public java.sql.Time readTime() throws java.sql.SQLException;
+    method public java.sql.Timestamp readTimestamp() throws java.sql.SQLException;
+    method public java.net.URL readURL() throws java.sql.SQLException;
+    method public boolean wasNull() throws java.sql.SQLException;
+  }
+
+  public class SQLIntegrityConstraintViolationException extends java.sql.SQLNonTransientException {
+    ctor public SQLIntegrityConstraintViolationException();
+    ctor public SQLIntegrityConstraintViolationException(String);
+    ctor public SQLIntegrityConstraintViolationException(String, String);
+    ctor public SQLIntegrityConstraintViolationException(String, String, int);
+    ctor public SQLIntegrityConstraintViolationException(Throwable);
+    ctor public SQLIntegrityConstraintViolationException(String, Throwable);
+    ctor public SQLIntegrityConstraintViolationException(String, String, Throwable);
+    ctor public SQLIntegrityConstraintViolationException(String, String, int, Throwable);
+  }
+
+  public class SQLInvalidAuthorizationSpecException extends java.sql.SQLNonTransientException {
+    ctor public SQLInvalidAuthorizationSpecException();
+    ctor public SQLInvalidAuthorizationSpecException(String);
+    ctor public SQLInvalidAuthorizationSpecException(String, String);
+    ctor public SQLInvalidAuthorizationSpecException(String, String, int);
+    ctor public SQLInvalidAuthorizationSpecException(Throwable);
+    ctor public SQLInvalidAuthorizationSpecException(String, Throwable);
+    ctor public SQLInvalidAuthorizationSpecException(String, String, Throwable);
+    ctor public SQLInvalidAuthorizationSpecException(String, String, int, Throwable);
+  }
+
+  public class SQLNonTransientConnectionException extends java.sql.SQLNonTransientException {
+    ctor public SQLNonTransientConnectionException();
+    ctor public SQLNonTransientConnectionException(String);
+    ctor public SQLNonTransientConnectionException(String, String);
+    ctor public SQLNonTransientConnectionException(String, String, int);
+    ctor public SQLNonTransientConnectionException(Throwable);
+    ctor public SQLNonTransientConnectionException(String, Throwable);
+    ctor public SQLNonTransientConnectionException(String, String, Throwable);
+    ctor public SQLNonTransientConnectionException(String, String, int, Throwable);
+  }
+
+  public class SQLNonTransientException extends java.sql.SQLException {
+    ctor public SQLNonTransientException();
+    ctor public SQLNonTransientException(String);
+    ctor public SQLNonTransientException(String, String);
+    ctor public SQLNonTransientException(String, String, int);
+    ctor public SQLNonTransientException(Throwable);
+    ctor public SQLNonTransientException(String, Throwable);
+    ctor public SQLNonTransientException(String, String, Throwable);
+    ctor public SQLNonTransientException(String, String, int, Throwable);
+  }
+
+  public interface SQLOutput {
+    method public void writeArray(java.sql.Array) throws java.sql.SQLException;
+    method public void writeAsciiStream(java.io.InputStream) throws java.sql.SQLException;
+    method public void writeBigDecimal(java.math.BigDecimal) throws java.sql.SQLException;
+    method public void writeBinaryStream(java.io.InputStream) throws java.sql.SQLException;
+    method public void writeBlob(java.sql.Blob) throws java.sql.SQLException;
+    method public void writeBoolean(boolean) throws java.sql.SQLException;
+    method public void writeByte(byte) throws java.sql.SQLException;
+    method public void writeBytes(byte[]) throws java.sql.SQLException;
+    method public void writeCharacterStream(java.io.Reader) throws java.sql.SQLException;
+    method public void writeClob(java.sql.Clob) throws java.sql.SQLException;
+    method public void writeDate(java.sql.Date) throws java.sql.SQLException;
+    method public void writeDouble(double) throws java.sql.SQLException;
+    method public void writeFloat(float) throws java.sql.SQLException;
+    method public void writeInt(int) throws java.sql.SQLException;
+    method public void writeLong(long) throws java.sql.SQLException;
+    method public void writeNClob(java.sql.NClob) throws java.sql.SQLException;
+    method public void writeNString(String) throws java.sql.SQLException;
+    method public void writeObject(java.sql.SQLData) throws java.sql.SQLException;
+    method public void writeRef(java.sql.Ref) throws java.sql.SQLException;
+    method public void writeRowId(java.sql.RowId) throws java.sql.SQLException;
+    method public void writeSQLXML(java.sql.SQLXML) throws java.sql.SQLException;
+    method public void writeShort(short) throws java.sql.SQLException;
+    method public void writeString(String) throws java.sql.SQLException;
+    method public void writeStruct(java.sql.Struct) throws java.sql.SQLException;
+    method public void writeTime(java.sql.Time) throws java.sql.SQLException;
+    method public void writeTimestamp(java.sql.Timestamp) throws java.sql.SQLException;
+    method public void writeURL(java.net.URL) throws java.sql.SQLException;
+  }
+
+  public final class SQLPermission extends java.security.BasicPermission {
+    ctor public SQLPermission(String);
+    ctor public SQLPermission(String, String);
+  }
+
+  public class SQLRecoverableException extends java.sql.SQLException {
+    ctor public SQLRecoverableException();
+    ctor public SQLRecoverableException(String);
+    ctor public SQLRecoverableException(String, String);
+    ctor public SQLRecoverableException(String, String, int);
+    ctor public SQLRecoverableException(Throwable);
+    ctor public SQLRecoverableException(String, Throwable);
+    ctor public SQLRecoverableException(String, String, Throwable);
+    ctor public SQLRecoverableException(String, String, int, Throwable);
+  }
+
+  public class SQLSyntaxErrorException extends java.sql.SQLNonTransientException {
+    ctor public SQLSyntaxErrorException();
+    ctor public SQLSyntaxErrorException(String);
+    ctor public SQLSyntaxErrorException(String, String);
+    ctor public SQLSyntaxErrorException(String, String, int);
+    ctor public SQLSyntaxErrorException(Throwable);
+    ctor public SQLSyntaxErrorException(String, Throwable);
+    ctor public SQLSyntaxErrorException(String, String, Throwable);
+    ctor public SQLSyntaxErrorException(String, String, int, Throwable);
+  }
+
+  public class SQLTimeoutException extends java.sql.SQLTransientException {
+    ctor public SQLTimeoutException();
+    ctor public SQLTimeoutException(String);
+    ctor public SQLTimeoutException(String, String);
+    ctor public SQLTimeoutException(String, String, int);
+    ctor public SQLTimeoutException(Throwable);
+    ctor public SQLTimeoutException(String, Throwable);
+    ctor public SQLTimeoutException(String, String, Throwable);
+    ctor public SQLTimeoutException(String, String, int, Throwable);
+  }
+
+  public class SQLTransactionRollbackException extends java.sql.SQLTransientException {
+    ctor public SQLTransactionRollbackException();
+    ctor public SQLTransactionRollbackException(String);
+    ctor public SQLTransactionRollbackException(String, String);
+    ctor public SQLTransactionRollbackException(String, String, int);
+    ctor public SQLTransactionRollbackException(Throwable);
+    ctor public SQLTransactionRollbackException(String, Throwable);
+    ctor public SQLTransactionRollbackException(String, String, Throwable);
+    ctor public SQLTransactionRollbackException(String, String, int, Throwable);
+  }
+
+  public class SQLTransientConnectionException extends java.sql.SQLTransientException {
+    ctor public SQLTransientConnectionException();
+    ctor public SQLTransientConnectionException(String);
+    ctor public SQLTransientConnectionException(String, String);
+    ctor public SQLTransientConnectionException(String, String, int);
+    ctor public SQLTransientConnectionException(Throwable);
+    ctor public SQLTransientConnectionException(String, Throwable);
+    ctor public SQLTransientConnectionException(String, String, Throwable);
+    ctor public SQLTransientConnectionException(String, String, int, Throwable);
+  }
+
+  public class SQLTransientException extends java.sql.SQLException {
+    ctor public SQLTransientException();
+    ctor public SQLTransientException(String);
+    ctor public SQLTransientException(String, String);
+    ctor public SQLTransientException(String, String, int);
+    ctor public SQLTransientException(Throwable);
+    ctor public SQLTransientException(String, Throwable);
+    ctor public SQLTransientException(String, String, Throwable);
+    ctor public SQLTransientException(String, String, int, Throwable);
+  }
+
+  public class SQLWarning extends java.sql.SQLException {
+    ctor public SQLWarning(String, String, int);
+    ctor public SQLWarning(String, String);
+    ctor public SQLWarning(String);
+    ctor public SQLWarning();
+    ctor public SQLWarning(Throwable);
+    ctor public SQLWarning(String, Throwable);
+    ctor public SQLWarning(String, String, Throwable);
+    ctor public SQLWarning(String, String, int, Throwable);
+    method public java.sql.SQLWarning getNextWarning();
+    method public void setNextWarning(java.sql.SQLWarning);
+  }
+
+  public interface SQLXML {
+    method public void free() throws java.sql.SQLException;
+    method public java.io.InputStream getBinaryStream() throws java.sql.SQLException;
+    method public java.io.Reader getCharacterStream() throws java.sql.SQLException;
+    method public <T extends javax.xml.transform.Source> T getSource(Class<T>) throws java.sql.SQLException;
+    method public String getString() throws java.sql.SQLException;
+    method public java.io.OutputStream setBinaryStream() throws java.sql.SQLException;
+    method public java.io.Writer setCharacterStream() throws java.sql.SQLException;
+    method public <T extends javax.xml.transform.Result> T setResult(Class<T>) throws java.sql.SQLException;
+    method public void setString(String) throws java.sql.SQLException;
+  }
+
+  public interface Savepoint {
+    method public int getSavepointId() throws java.sql.SQLException;
+    method public String getSavepointName() throws java.sql.SQLException;
+  }
+
+  public interface Statement extends java.sql.Wrapper java.lang.AutoCloseable {
+    method public void addBatch(String) throws java.sql.SQLException;
+    method public void cancel() throws java.sql.SQLException;
+    method public void clearBatch() throws java.sql.SQLException;
+    method public void clearWarnings() throws java.sql.SQLException;
+    method public void close() throws java.sql.SQLException;
+    method public boolean execute(String) throws java.sql.SQLException;
+    method public boolean execute(String, int) throws java.sql.SQLException;
+    method public boolean execute(String, int[]) throws java.sql.SQLException;
+    method public boolean execute(String, String[]) throws java.sql.SQLException;
+    method public int[] executeBatch() throws java.sql.SQLException;
+    method public java.sql.ResultSet executeQuery(String) throws java.sql.SQLException;
+    method public int executeUpdate(String) throws java.sql.SQLException;
+    method public int executeUpdate(String, int) throws java.sql.SQLException;
+    method public int executeUpdate(String, int[]) throws java.sql.SQLException;
+    method public int executeUpdate(String, String[]) throws java.sql.SQLException;
+    method public java.sql.Connection getConnection() throws java.sql.SQLException;
+    method public int getFetchDirection() throws java.sql.SQLException;
+    method public int getFetchSize() throws java.sql.SQLException;
+    method public java.sql.ResultSet getGeneratedKeys() throws java.sql.SQLException;
+    method public int getMaxFieldSize() throws java.sql.SQLException;
+    method public int getMaxRows() throws java.sql.SQLException;
+    method public boolean getMoreResults() throws java.sql.SQLException;
+    method public boolean getMoreResults(int) throws java.sql.SQLException;
+    method public int getQueryTimeout() throws java.sql.SQLException;
+    method public java.sql.ResultSet getResultSet() throws java.sql.SQLException;
+    method public int getResultSetConcurrency() throws java.sql.SQLException;
+    method public int getResultSetHoldability() throws java.sql.SQLException;
+    method public int getResultSetType() throws java.sql.SQLException;
+    method public int getUpdateCount() throws java.sql.SQLException;
+    method public java.sql.SQLWarning getWarnings() throws java.sql.SQLException;
+    method public boolean isClosed() throws java.sql.SQLException;
+    method public boolean isPoolable() throws java.sql.SQLException;
+    method public void setCursorName(String) throws java.sql.SQLException;
+    method public void setEscapeProcessing(boolean) throws java.sql.SQLException;
+    method public void setFetchDirection(int) throws java.sql.SQLException;
+    method public void setFetchSize(int) throws java.sql.SQLException;
+    method public void setMaxFieldSize(int) throws java.sql.SQLException;
+    method public void setMaxRows(int) throws java.sql.SQLException;
+    method public void setPoolable(boolean) throws java.sql.SQLException;
+    method public void setQueryTimeout(int) throws java.sql.SQLException;
+    field public static final int CLOSE_ALL_RESULTS = 3; // 0x3
+    field public static final int CLOSE_CURRENT_RESULT = 1; // 0x1
+    field public static final int EXECUTE_FAILED = -3; // 0xfffffffd
+    field public static final int KEEP_CURRENT_RESULT = 2; // 0x2
+    field public static final int NO_GENERATED_KEYS = 2; // 0x2
+    field public static final int RETURN_GENERATED_KEYS = 1; // 0x1
+    field public static final int SUCCESS_NO_INFO = -2; // 0xfffffffe
+  }
+
+  public interface Struct {
+    method public Object[] getAttributes() throws java.sql.SQLException;
+    method public Object[] getAttributes(java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public String getSQLTypeName() throws java.sql.SQLException;
+  }
+
+  public class Time extends java.util.Date {
+    ctor @Deprecated public Time(int, int, int);
+    ctor public Time(long);
+    method public static java.sql.Time valueOf(String);
+  }
+
+  public class Timestamp extends java.util.Date {
+    ctor @Deprecated public Timestamp(int, int, int, int, int, int, int);
+    ctor public Timestamp(long);
+    method public boolean after(java.sql.Timestamp);
+    method public boolean before(java.sql.Timestamp);
+    method public int compareTo(java.sql.Timestamp);
+    method public boolean equals(java.sql.Timestamp);
+    method public int getNanos();
+    method public void setNanos(int);
+    method public static java.sql.Timestamp valueOf(String);
+  }
+
+  public class Types {
+    field public static final int ARRAY = 2003; // 0x7d3
+    field public static final int BIGINT = -5; // 0xfffffffb
+    field public static final int BINARY = -2; // 0xfffffffe
+    field public static final int BIT = -7; // 0xfffffff9
+    field public static final int BLOB = 2004; // 0x7d4
+    field public static final int BOOLEAN = 16; // 0x10
+    field public static final int CHAR = 1; // 0x1
+    field public static final int CLOB = 2005; // 0x7d5
+    field public static final int DATALINK = 70; // 0x46
+    field public static final int DATE = 91; // 0x5b
+    field public static final int DECIMAL = 3; // 0x3
+    field public static final int DISTINCT = 2001; // 0x7d1
+    field public static final int DOUBLE = 8; // 0x8
+    field public static final int FLOAT = 6; // 0x6
+    field public static final int INTEGER = 4; // 0x4
+    field public static final int JAVA_OBJECT = 2000; // 0x7d0
+    field public static final int LONGNVARCHAR = -16; // 0xfffffff0
+    field public static final int LONGVARBINARY = -4; // 0xfffffffc
+    field public static final int LONGVARCHAR = -1; // 0xffffffff
+    field public static final int NCHAR = -15; // 0xfffffff1
+    field public static final int NCLOB = 2011; // 0x7db
+    field public static final int NULL = 0; // 0x0
+    field public static final int NUMERIC = 2; // 0x2
+    field public static final int NVARCHAR = -9; // 0xfffffff7
+    field public static final int OTHER = 1111; // 0x457
+    field public static final int REAL = 7; // 0x7
+    field public static final int REF = 2006; // 0x7d6
+    field public static final int ROWID = -8; // 0xfffffff8
+    field public static final int SMALLINT = 5; // 0x5
+    field public static final int SQLXML = 2009; // 0x7d9
+    field public static final int STRUCT = 2002; // 0x7d2
+    field public static final int TIME = 92; // 0x5c
+    field public static final int TIMESTAMP = 93; // 0x5d
+    field public static final int TINYINT = -6; // 0xfffffffa
+    field public static final int VARBINARY = -3; // 0xfffffffd
+    field public static final int VARCHAR = 12; // 0xc
+  }
+
+  public interface Wrapper {
+    method public boolean isWrapperFor(Class<?>) throws java.sql.SQLException;
+    method public <T> T unwrap(Class<T>) throws java.sql.SQLException;
+  }
+
+}
+
+package java.text {
+
+  public class Annotation {
+    ctor public Annotation(Object);
+    method public Object getValue();
+  }
+
+  public interface AttributedCharacterIterator extends java.text.CharacterIterator {
+    method public java.util.Set<java.text.AttributedCharacterIterator.Attribute> getAllAttributeKeys();
+    method public Object getAttribute(java.text.AttributedCharacterIterator.Attribute);
+    method public java.util.Map<java.text.AttributedCharacterIterator.Attribute,java.lang.Object> getAttributes();
+    method public int getRunLimit();
+    method public int getRunLimit(java.text.AttributedCharacterIterator.Attribute);
+    method public int getRunLimit(java.util.Set<? extends java.text.AttributedCharacterIterator.Attribute>);
+    method public int getRunStart();
+    method public int getRunStart(java.text.AttributedCharacterIterator.Attribute);
+    method public int getRunStart(java.util.Set<? extends java.text.AttributedCharacterIterator.Attribute>);
+  }
+
+  public static class AttributedCharacterIterator.Attribute implements java.io.Serializable {
+    ctor protected AttributedCharacterIterator.Attribute(String);
+    method public final boolean equals(Object);
+    method protected String getName();
+    method public final int hashCode();
+    method protected Object readResolve() throws java.io.InvalidObjectException;
+    field public static final java.text.AttributedCharacterIterator.Attribute INPUT_METHOD_SEGMENT;
+    field public static final java.text.AttributedCharacterIterator.Attribute LANGUAGE;
+    field public static final java.text.AttributedCharacterIterator.Attribute READING;
+  }
+
+  public class AttributedString {
+    ctor public AttributedString(String);
+    ctor public AttributedString(String, java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute,?>);
+    ctor public AttributedString(java.text.AttributedCharacterIterator);
+    ctor public AttributedString(java.text.AttributedCharacterIterator, int, int);
+    ctor public AttributedString(java.text.AttributedCharacterIterator, int, int, java.text.AttributedCharacterIterator.Attribute[]);
+    method public void addAttribute(java.text.AttributedCharacterIterator.Attribute, Object);
+    method public void addAttribute(java.text.AttributedCharacterIterator.Attribute, Object, int, int);
+    method public void addAttributes(java.util.Map<? extends java.text.AttributedCharacterIterator.Attribute,?>, int, int);
+    method public java.text.AttributedCharacterIterator getIterator();
+    method public java.text.AttributedCharacterIterator getIterator(java.text.AttributedCharacterIterator.Attribute[]);
+    method public java.text.AttributedCharacterIterator getIterator(java.text.AttributedCharacterIterator.Attribute[], int, int);
+  }
+
+  public final class Bidi {
+    ctor public Bidi(String, int);
+    ctor public Bidi(java.text.AttributedCharacterIterator);
+    ctor public Bidi(char[], int, byte[], int, int, int);
+    method public boolean baseIsLeftToRight();
+    method public java.text.Bidi createLineBidi(int, int);
+    method public int getBaseLevel();
+    method public int getLength();
+    method public int getLevelAt(int);
+    method public int getRunCount();
+    method public int getRunLevel(int);
+    method public int getRunLimit(int);
+    method public int getRunStart(int);
+    method public boolean isLeftToRight();
+    method public boolean isMixed();
+    method public boolean isRightToLeft();
+    method public static void reorderVisually(byte[], int, Object[], int, int);
+    method public static boolean requiresBidi(char[], int, int);
+    field public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2; // 0xfffffffe
+    field public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1; // 0xffffffff
+    field public static final int DIRECTION_LEFT_TO_RIGHT = 0; // 0x0
+    field public static final int DIRECTION_RIGHT_TO_LEFT = 1; // 0x1
+  }
+
+  public abstract class BreakIterator implements java.lang.Cloneable {
+    ctor protected BreakIterator();
+    method public Object clone();
+    method public abstract int current();
+    method public abstract int first();
+    method public abstract int following(int);
+    method public static java.util.Locale[] getAvailableLocales();
+    method public static java.text.BreakIterator getCharacterInstance();
+    method public static java.text.BreakIterator getCharacterInstance(java.util.Locale);
+    method public static java.text.BreakIterator getLineInstance();
+    method public static java.text.BreakIterator getLineInstance(java.util.Locale);
+    method public static java.text.BreakIterator getSentenceInstance();
+    method public static java.text.BreakIterator getSentenceInstance(java.util.Locale);
+    method public abstract java.text.CharacterIterator getText();
+    method public static java.text.BreakIterator getWordInstance();
+    method public static java.text.BreakIterator getWordInstance(java.util.Locale);
+    method public boolean isBoundary(int);
+    method public abstract int last();
+    method public abstract int next(int);
+    method public abstract int next();
+    method public int preceding(int);
+    method public abstract int previous();
+    method public void setText(String);
+    method public abstract void setText(java.text.CharacterIterator);
+    field public static final int DONE = -1; // 0xffffffff
+  }
+
+  public interface CharacterIterator extends java.lang.Cloneable {
+    method public Object clone();
+    method public char current();
+    method public char first();
+    method public int getBeginIndex();
+    method public int getEndIndex();
+    method public int getIndex();
+    method public char last();
+    method public char next();
+    method public char previous();
+    method public char setIndex(int);
+    field public static final char DONE = 65535; // 0xffff '\uffff'
+  }
+
+  public class ChoiceFormat extends java.text.NumberFormat {
+    ctor public ChoiceFormat(String);
+    ctor public ChoiceFormat(double[], String[]);
+    method public void applyPattern(String);
+    method public StringBuffer format(long, StringBuffer, java.text.FieldPosition);
+    method public StringBuffer format(double, StringBuffer, java.text.FieldPosition);
+    method public Object[] getFormats();
+    method public double[] getLimits();
+    method public static final double nextDouble(double);
+    method public static double nextDouble(double, boolean);
+    method public Number parse(String, java.text.ParsePosition);
+    method public static final double previousDouble(double);
+    method public void setChoices(double[], String[]);
+    method public String toPattern();
+  }
+
+  public final class CollationElementIterator {
+    method public int getMaxExpansion(int);
+    method public int getOffset();
+    method public int next();
+    method public int previous();
+    method public static int primaryOrder(int);
+    method public void reset();
+    method public static short secondaryOrder(int);
+    method public void setOffset(int);
+    method public void setText(String);
+    method public void setText(java.text.CharacterIterator);
+    method public static short tertiaryOrder(int);
+    field public static final int NULLORDER = -1; // 0xffffffff
+  }
+
+  public abstract class CollationKey implements java.lang.Comparable<java.text.CollationKey> {
+    ctor protected CollationKey(String);
+    method public abstract int compareTo(java.text.CollationKey);
+    method public String getSourceString();
+    method public abstract byte[] toByteArray();
+  }
+
+  public abstract class Collator implements java.lang.Cloneable java.util.Comparator<java.lang.Object> {
+    ctor protected Collator();
+    method public Object clone();
+    method public abstract int compare(String, String);
+    method public int compare(Object, Object);
+    method public boolean equals(String, String);
+    method public static java.util.Locale[] getAvailableLocales();
+    method public abstract java.text.CollationKey getCollationKey(String);
+    method public int getDecomposition();
+    method public static java.text.Collator getInstance();
+    method public static java.text.Collator getInstance(java.util.Locale);
+    method public int getStrength();
+    method public abstract int hashCode();
+    method public void setDecomposition(int);
+    method public void setStrength(int);
+    field public static final int CANONICAL_DECOMPOSITION = 1; // 0x1
+    field public static final int FULL_DECOMPOSITION = 2; // 0x2
+    field public static final int IDENTICAL = 3; // 0x3
+    field public static final int NO_DECOMPOSITION = 0; // 0x0
+    field public static final int PRIMARY = 0; // 0x0
+    field public static final int SECONDARY = 1; // 0x1
+    field public static final int TERTIARY = 2; // 0x2
+  }
+
+  public abstract class DateFormat extends java.text.Format {
+    ctor protected DateFormat();
+    method @NonNull public final StringBuffer format(@NonNull Object, @NonNull StringBuffer, @NonNull java.text.FieldPosition);
+    method @NonNull public abstract StringBuffer format(@NonNull java.util.Date, @NonNull StringBuffer, @NonNull java.text.FieldPosition);
+    method @NonNull public final String format(@NonNull java.util.Date);
+    method @NonNull public static java.util.Locale[] getAvailableLocales();
+    method @NonNull public java.util.Calendar getCalendar();
+    method @NonNull public static final java.text.DateFormat getDateInstance();
+    method @NonNull public static final java.text.DateFormat getDateInstance(int);
+    method @NonNull public static final java.text.DateFormat getDateInstance(int, @NonNull java.util.Locale);
+    method @NonNull public static final java.text.DateFormat getDateTimeInstance();
+    method @NonNull public static final java.text.DateFormat getDateTimeInstance(int, int);
+    method @NonNull public static final java.text.DateFormat getDateTimeInstance(int, int, @NonNull java.util.Locale);
+    method @NonNull public static final java.text.DateFormat getInstance();
+    method @NonNull public java.text.NumberFormat getNumberFormat();
+    method @NonNull public static final java.text.DateFormat getTimeInstance();
+    method @NonNull public static final java.text.DateFormat getTimeInstance(int);
+    method @NonNull public static final java.text.DateFormat getTimeInstance(int, @NonNull java.util.Locale);
+    method @NonNull public java.util.TimeZone getTimeZone();
+    method public boolean isLenient();
+    method @Nullable public java.util.Date parse(@NonNull String) throws java.text.ParseException;
+    method @Nullable public abstract java.util.Date parse(@NonNull String, @NonNull java.text.ParsePosition);
+    method @Nullable public Object parseObject(@NonNull String, @NonNull java.text.ParsePosition);
+    method public void setCalendar(@NonNull java.util.Calendar);
+    method public void setLenient(boolean);
+    method public void setNumberFormat(@NonNull java.text.NumberFormat);
+    method public void setTimeZone(@NonNull java.util.TimeZone);
+    field public static final int AM_PM_FIELD = 14; // 0xe
+    field public static final int DATE_FIELD = 3; // 0x3
+    field public static final int DAY_OF_WEEK_FIELD = 9; // 0x9
+    field public static final int DAY_OF_WEEK_IN_MONTH_FIELD = 11; // 0xb
+    field public static final int DAY_OF_YEAR_FIELD = 10; // 0xa
+    field public static final int DEFAULT = 2; // 0x2
+    field public static final int ERA_FIELD = 0; // 0x0
+    field public static final int FULL = 0; // 0x0
+    field public static final int HOUR0_FIELD = 16; // 0x10
+    field public static final int HOUR1_FIELD = 15; // 0xf
+    field public static final int HOUR_OF_DAY0_FIELD = 5; // 0x5
+    field public static final int HOUR_OF_DAY1_FIELD = 4; // 0x4
+    field public static final int LONG = 1; // 0x1
+    field public static final int MEDIUM = 2; // 0x2
+    field public static final int MILLISECOND_FIELD = 8; // 0x8
+    field public static final int MINUTE_FIELD = 6; // 0x6
+    field public static final int MONTH_FIELD = 2; // 0x2
+    field public static final int SECOND_FIELD = 7; // 0x7
+    field public static final int SHORT = 3; // 0x3
+    field public static final int TIMEZONE_FIELD = 17; // 0x11
+    field public static final int WEEK_OF_MONTH_FIELD = 13; // 0xd
+    field public static final int WEEK_OF_YEAR_FIELD = 12; // 0xc
+    field public static final int YEAR_FIELD = 1; // 0x1
+    field @NonNull protected java.util.Calendar calendar;
+    field @NonNull protected java.text.NumberFormat numberFormat;
+  }
+
+  public static class DateFormat.Field extends java.text.Format.Field {
+    ctor protected DateFormat.Field(@NonNull String, int);
+    method public int getCalendarField();
+    method @NonNull public static java.text.DateFormat.Field ofCalendarField(int);
+    field @NonNull public static final java.text.DateFormat.Field AM_PM;
+    field @NonNull public static final java.text.DateFormat.Field DAY_OF_MONTH;
+    field @NonNull public static final java.text.DateFormat.Field DAY_OF_WEEK;
+    field @NonNull public static final java.text.DateFormat.Field DAY_OF_WEEK_IN_MONTH;
+    field @NonNull public static final java.text.DateFormat.Field DAY_OF_YEAR;
+    field @NonNull public static final java.text.DateFormat.Field ERA;
+    field @NonNull public static final java.text.DateFormat.Field HOUR0;
+    field @NonNull public static final java.text.DateFormat.Field HOUR1;
+    field @NonNull public static final java.text.DateFormat.Field HOUR_OF_DAY0;
+    field @NonNull public static final java.text.DateFormat.Field HOUR_OF_DAY1;
+    field @NonNull public static final java.text.DateFormat.Field MILLISECOND;
+    field @NonNull public static final java.text.DateFormat.Field MINUTE;
+    field @NonNull public static final java.text.DateFormat.Field MONTH;
+    field @NonNull public static final java.text.DateFormat.Field SECOND;
+    field @NonNull public static final java.text.DateFormat.Field TIME_ZONE;
+    field @NonNull public static final java.text.DateFormat.Field WEEK_OF_MONTH;
+    field @NonNull public static final java.text.DateFormat.Field WEEK_OF_YEAR;
+    field @NonNull public static final java.text.DateFormat.Field YEAR;
+  }
+
+  public class DateFormatSymbols implements java.lang.Cloneable java.io.Serializable {
+    ctor public DateFormatSymbols();
+    ctor public DateFormatSymbols(java.util.Locale);
+    method public Object clone();
+    method public String[] getAmPmStrings();
+    method public static java.util.Locale[] getAvailableLocales();
+    method public String[] getEras();
+    method public static final java.text.DateFormatSymbols getInstance();
+    method public static final java.text.DateFormatSymbols getInstance(java.util.Locale);
+    method public String getLocalPatternChars();
+    method public String[] getMonths();
+    method public String[] getShortMonths();
+    method public String[] getShortWeekdays();
+    method public String[] getWeekdays();
+    method public String[][] getZoneStrings();
+    method public void setAmPmStrings(String[]);
+    method public void setEras(String[]);
+    method public void setLocalPatternChars(String);
+    method public void setMonths(String[]);
+    method public void setShortMonths(String[]);
+    method public void setShortWeekdays(String[]);
+    method public void setWeekdays(String[]);
+    method public void setZoneStrings(String[][]);
+  }
+
+  public class DecimalFormat extends java.text.NumberFormat {
+    ctor public DecimalFormat();
+    ctor public DecimalFormat(String);
+    ctor public DecimalFormat(String, java.text.DecimalFormatSymbols);
+    method public void applyLocalizedPattern(String);
+    method public void applyPattern(String);
+    method public final StringBuffer format(Object, StringBuffer, java.text.FieldPosition);
+    method public StringBuffer format(double, StringBuffer, java.text.FieldPosition);
+    method public StringBuffer format(long, StringBuffer, java.text.FieldPosition);
+    method public java.text.DecimalFormatSymbols getDecimalFormatSymbols();
+    method public int getGroupingSize();
+    method public int getMultiplier();
+    method public String getNegativePrefix();
+    method public String getNegativeSuffix();
+    method public String getPositivePrefix();
+    method public String getPositiveSuffix();
+    method public boolean isDecimalSeparatorAlwaysShown();
+    method public boolean isParseBigDecimal();
+    method public Number parse(String, java.text.ParsePosition);
+    method public void setDecimalFormatSymbols(java.text.DecimalFormatSymbols);
+    method public void setDecimalSeparatorAlwaysShown(boolean);
+    method public void setGroupingSize(int);
+    method public void setMultiplier(int);
+    method public void setNegativePrefix(String);
+    method public void setNegativeSuffix(String);
+    method public void setParseBigDecimal(boolean);
+    method public void setPositivePrefix(String);
+    method public void setPositiveSuffix(String);
+    method public String toLocalizedPattern();
+    method public String toPattern();
+  }
+
+  public class DecimalFormatSymbols implements java.lang.Cloneable java.io.Serializable {
+    ctor public DecimalFormatSymbols();
+    ctor public DecimalFormatSymbols(java.util.Locale);
+    method public Object clone();
+    method public static java.util.Locale[] getAvailableLocales();
+    method public java.util.Currency getCurrency();
+    method public String getCurrencySymbol();
+    method public char getDecimalSeparator();
+    method public char getDigit();
+    method public String getExponentSeparator();
+    method public char getGroupingSeparator();
+    method public String getInfinity();
+    method public static final java.text.DecimalFormatSymbols getInstance();
+    method public static final java.text.DecimalFormatSymbols getInstance(java.util.Locale);
+    method public String getInternationalCurrencySymbol();
+    method public char getMinusSign();
+    method public char getMonetaryDecimalSeparator();
+    method public String getNaN();
+    method public char getPatternSeparator();
+    method public char getPerMill();
+    method public char getPercent();
+    method public char getZeroDigit();
+    method public void setCurrency(java.util.Currency);
+    method public void setCurrencySymbol(String);
+    method public void setDecimalSeparator(char);
+    method public void setDigit(char);
+    method public void setExponentSeparator(String);
+    method public void setGroupingSeparator(char);
+    method public void setInfinity(String);
+    method public void setInternationalCurrencySymbol(String);
+    method public void setMinusSign(char);
+    method public void setMonetaryDecimalSeparator(char);
+    method public void setNaN(String);
+    method public void setPatternSeparator(char);
+    method public void setPerMill(char);
+    method public void setPercent(char);
+    method public void setZeroDigit(char);
+  }
+
+  public class FieldPosition {
+    ctor public FieldPosition(int);
+    ctor public FieldPosition(java.text.Format.Field);
+    ctor public FieldPosition(java.text.Format.Field, int);
+    method public int getBeginIndex();
+    method public int getEndIndex();
+    method public int getField();
+    method public java.text.Format.Field getFieldAttribute();
+    method public void setBeginIndex(int);
+    method public void setEndIndex(int);
+  }
+
+  public abstract class Format implements java.lang.Cloneable java.io.Serializable {
+    ctor protected Format();
+    method public Object clone();
+    method public final String format(Object);
+    method public abstract StringBuffer format(Object, StringBuffer, java.text.FieldPosition);
+    method public java.text.AttributedCharacterIterator formatToCharacterIterator(Object);
+    method public abstract Object parseObject(String, java.text.ParsePosition);
+    method public Object parseObject(String) throws java.text.ParseException;
+  }
+
+  public static class Format.Field extends java.text.AttributedCharacterIterator.Attribute {
+    ctor protected Format.Field(String);
+  }
+
+  public class MessageFormat extends java.text.Format {
+    ctor public MessageFormat(String);
+    ctor public MessageFormat(String, java.util.Locale);
+    method public void applyPattern(String);
+    method public final StringBuffer format(Object[], StringBuffer, java.text.FieldPosition);
+    method public static String format(String, java.lang.Object...);
+    method public final StringBuffer format(Object, StringBuffer, java.text.FieldPosition);
+    method public java.text.Format[] getFormats();
+    method public java.text.Format[] getFormatsByArgumentIndex();
+    method public java.util.Locale getLocale();
+    method public Object[] parse(String, java.text.ParsePosition);
+    method public Object[] parse(String) throws java.text.ParseException;
+    method public Object parseObject(String, java.text.ParsePosition);
+    method public void setFormat(int, java.text.Format);
+    method public void setFormatByArgumentIndex(int, java.text.Format);
+    method public void setFormats(java.text.Format[]);
+    method public void setFormatsByArgumentIndex(java.text.Format[]);
+    method public void setLocale(java.util.Locale);
+    method public String toPattern();
+  }
+
+  public static class MessageFormat.Field extends java.text.Format.Field {
+    ctor protected MessageFormat.Field(String);
+    field public static final java.text.MessageFormat.Field ARGUMENT;
+  }
+
+  public final class Normalizer {
+    method public static boolean isNormalized(CharSequence, java.text.Normalizer.Form);
+    method public static String normalize(CharSequence, java.text.Normalizer.Form);
+  }
+
+  public enum Normalizer.Form {
+    enum_constant public static final java.text.Normalizer.Form NFC;
+    enum_constant public static final java.text.Normalizer.Form NFD;
+    enum_constant public static final java.text.Normalizer.Form NFKC;
+    enum_constant public static final java.text.Normalizer.Form NFKD;
+  }
+
+  public abstract class NumberFormat extends java.text.Format {
+    ctor protected NumberFormat();
+    method @NonNull public StringBuffer format(@NonNull Object, @NonNull StringBuffer, @NonNull java.text.FieldPosition);
+    method @NonNull public final String format(double);
+    method @NonNull public final String format(long);
+    method @NonNull public abstract StringBuffer format(double, @NonNull StringBuffer, @NonNull java.text.FieldPosition);
+    method @NonNull public abstract StringBuffer format(long, @NonNull StringBuffer, @NonNull java.text.FieldPosition);
+    method @NonNull public static java.util.Locale[] getAvailableLocales();
+    method @Nullable public java.util.Currency getCurrency();
+    method @NonNull public static final java.text.NumberFormat getCurrencyInstance();
+    method @NonNull public static java.text.NumberFormat getCurrencyInstance(@NonNull java.util.Locale);
+    method @NonNull public static final java.text.NumberFormat getInstance();
+    method @NonNull public static java.text.NumberFormat getInstance(@NonNull java.util.Locale);
+    method @NonNull public static final java.text.NumberFormat getIntegerInstance();
+    method @NonNull public static java.text.NumberFormat getIntegerInstance(@NonNull java.util.Locale);
+    method public int getMaximumFractionDigits();
+    method public int getMaximumIntegerDigits();
+    method public int getMinimumFractionDigits();
+    method public int getMinimumIntegerDigits();
+    method @NonNull public static final java.text.NumberFormat getNumberInstance();
+    method @NonNull public static java.text.NumberFormat getNumberInstance(@NonNull java.util.Locale);
+    method @NonNull public static final java.text.NumberFormat getPercentInstance();
+    method @NonNull public static java.text.NumberFormat getPercentInstance(@NonNull java.util.Locale);
+    method @NonNull public java.math.RoundingMode getRoundingMode();
+    method public boolean isGroupingUsed();
+    method public boolean isParseIntegerOnly();
+    method @Nullable public abstract Number parse(@NonNull String, @NonNull java.text.ParsePosition);
+    method @Nullable public Number parse(@NonNull String) throws java.text.ParseException;
+    method @Nullable public final Object parseObject(@NonNull String, @NonNull java.text.ParsePosition);
+    method public void setCurrency(@NonNull java.util.Currency);
+    method public void setGroupingUsed(boolean);
+    method public void setMaximumFractionDigits(int);
+    method public void setMaximumIntegerDigits(int);
+    method public void setMinimumFractionDigits(int);
+    method public void setMinimumIntegerDigits(int);
+    method public void setParseIntegerOnly(boolean);
+    method public void setRoundingMode(@Nullable java.math.RoundingMode);
+    field public static final int FRACTION_FIELD = 1; // 0x1
+    field public static final int INTEGER_FIELD = 0; // 0x0
+  }
+
+  public static class NumberFormat.Field extends java.text.Format.Field {
+    ctor protected NumberFormat.Field(@NonNull String);
+    field @NonNull public static final java.text.NumberFormat.Field CURRENCY;
+    field @NonNull public static final java.text.NumberFormat.Field DECIMAL_SEPARATOR;
+    field @NonNull public static final java.text.NumberFormat.Field EXPONENT;
+    field @NonNull public static final java.text.NumberFormat.Field EXPONENT_SIGN;
+    field @NonNull public static final java.text.NumberFormat.Field EXPONENT_SYMBOL;
+    field @NonNull public static final java.text.NumberFormat.Field FRACTION;
+    field @NonNull public static final java.text.NumberFormat.Field GROUPING_SEPARATOR;
+    field @NonNull public static final java.text.NumberFormat.Field INTEGER;
+    field @NonNull public static final java.text.NumberFormat.Field PERCENT;
+    field @NonNull public static final java.text.NumberFormat.Field PERMILLE;
+    field @NonNull public static final java.text.NumberFormat.Field SIGN;
+  }
+
+  public class ParseException extends java.lang.Exception {
+    ctor public ParseException(String, int);
+    method public int getErrorOffset();
+  }
+
+  public class ParsePosition {
+    ctor public ParsePosition(int);
+    method public int getErrorIndex();
+    method public int getIndex();
+    method public void setErrorIndex(int);
+    method public void setIndex(int);
+  }
+
+  public class RuleBasedCollator extends java.text.Collator {
+    ctor public RuleBasedCollator(String) throws java.text.ParseException;
+    method public int compare(String, String);
+    method public java.text.CollationElementIterator getCollationElementIterator(String);
+    method public java.text.CollationElementIterator getCollationElementIterator(java.text.CharacterIterator);
+    method public java.text.CollationKey getCollationKey(String);
+    method public String getRules();
+  }
+
+  public class SimpleDateFormat extends java.text.DateFormat {
+    ctor public SimpleDateFormat();
+    ctor public SimpleDateFormat(String);
+    ctor public SimpleDateFormat(String, java.util.Locale);
+    ctor public SimpleDateFormat(String, java.text.DateFormatSymbols);
+    method public void applyLocalizedPattern(String);
+    method public void applyPattern(String);
+    method public StringBuffer format(java.util.Date, StringBuffer, java.text.FieldPosition);
+    method public java.util.Date get2DigitYearStart();
+    method public java.text.DateFormatSymbols getDateFormatSymbols();
+    method public java.util.Date parse(String, java.text.ParsePosition);
+    method public void set2DigitYearStart(java.util.Date);
+    method public void setDateFormatSymbols(java.text.DateFormatSymbols);
+    method public String toLocalizedPattern();
+    method public String toPattern();
+  }
+
+  public final class StringCharacterIterator implements java.text.CharacterIterator {
+    ctor public StringCharacterIterator(String);
+    ctor public StringCharacterIterator(String, int);
+    ctor public StringCharacterIterator(String, int, int, int);
+    method public Object clone();
+    method public char current();
+    method public char first();
+    method public int getBeginIndex();
+    method public int getEndIndex();
+    method public int getIndex();
+    method public char last();
+    method public char next();
+    method public char previous();
+    method public char setIndex(int);
+    method public void setText(String);
+  }
+
+}
+
+package java.time {
+
+  public abstract class Clock {
+    ctor protected Clock();
+    method public static java.time.Clock fixed(java.time.Instant, java.time.ZoneId);
+    method public abstract java.time.ZoneId getZone();
+    method public abstract java.time.Instant instant();
+    method public long millis();
+    method public static java.time.Clock offset(java.time.Clock, java.time.Duration);
+    method public static java.time.Clock system(java.time.ZoneId);
+    method public static java.time.Clock systemDefaultZone();
+    method public static java.time.Clock systemUTC();
+    method public static java.time.Clock tick(java.time.Clock, java.time.Duration);
+    method public static java.time.Clock tickMinutes(java.time.ZoneId);
+    method public static java.time.Clock tickSeconds(java.time.ZoneId);
+    method public abstract java.time.Clock withZone(java.time.ZoneId);
+  }
+
+  public class DateTimeException extends java.lang.RuntimeException {
+    ctor public DateTimeException(String);
+    ctor public DateTimeException(String, Throwable);
+  }
+
+  public enum DayOfWeek implements java.time.temporal.TemporalAccessor java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public static java.time.DayOfWeek from(java.time.temporal.TemporalAccessor);
+    method public String getDisplayName(java.time.format.TextStyle, java.util.Locale);
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getValue();
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public java.time.DayOfWeek minus(long);
+    method public static java.time.DayOfWeek of(int);
+    method public java.time.DayOfWeek plus(long);
+    enum_constant public static final java.time.DayOfWeek FRIDAY;
+    enum_constant public static final java.time.DayOfWeek MONDAY;
+    enum_constant public static final java.time.DayOfWeek SATURDAY;
+    enum_constant public static final java.time.DayOfWeek SUNDAY;
+    enum_constant public static final java.time.DayOfWeek THURSDAY;
+    enum_constant public static final java.time.DayOfWeek TUESDAY;
+    enum_constant public static final java.time.DayOfWeek WEDNESDAY;
+  }
+
+  public final class Duration implements java.lang.Comparable<java.time.Duration> java.io.Serializable java.time.temporal.TemporalAmount {
+    method public java.time.Duration abs();
+    method public java.time.temporal.Temporal addTo(java.time.temporal.Temporal);
+    method public static java.time.Duration between(java.time.temporal.Temporal, java.time.temporal.Temporal);
+    method public int compareTo(java.time.Duration);
+    method public java.time.Duration dividedBy(long);
+    method public long dividedBy(java.time.Duration);
+    method public static java.time.Duration from(java.time.temporal.TemporalAmount);
+    method public long get(java.time.temporal.TemporalUnit);
+    method public int getNano();
+    method public long getSeconds();
+    method public java.util.List<java.time.temporal.TemporalUnit> getUnits();
+    method public boolean isNegative();
+    method public boolean isZero();
+    method public java.time.Duration minus(java.time.Duration);
+    method public java.time.Duration minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.Duration minusDays(long);
+    method public java.time.Duration minusHours(long);
+    method public java.time.Duration minusMillis(long);
+    method public java.time.Duration minusMinutes(long);
+    method public java.time.Duration minusNanos(long);
+    method public java.time.Duration minusSeconds(long);
+    method public java.time.Duration multipliedBy(long);
+    method public java.time.Duration negated();
+    method public static java.time.Duration of(long, java.time.temporal.TemporalUnit);
+    method public static java.time.Duration ofDays(long);
+    method public static java.time.Duration ofHours(long);
+    method public static java.time.Duration ofMillis(long);
+    method public static java.time.Duration ofMinutes(long);
+    method public static java.time.Duration ofNanos(long);
+    method public static java.time.Duration ofSeconds(long);
+    method public static java.time.Duration ofSeconds(long, long);
+    method public static java.time.Duration parse(CharSequence);
+    method public java.time.Duration plus(java.time.Duration);
+    method public java.time.Duration plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.Duration plusDays(long);
+    method public java.time.Duration plusHours(long);
+    method public java.time.Duration plusMillis(long);
+    method public java.time.Duration plusMinutes(long);
+    method public java.time.Duration plusNanos(long);
+    method public java.time.Duration plusSeconds(long);
+    method public java.time.temporal.Temporal subtractFrom(java.time.temporal.Temporal);
+    method public long toDays();
+    method public long toDaysPart();
+    method public long toHours();
+    method public int toHoursPart();
+    method public long toMillis();
+    method public int toMillisPart();
+    method public long toMinutes();
+    method public int toMinutesPart();
+    method public long toNanos();
+    method public int toNanosPart();
+    method public long toSeconds();
+    method public int toSecondsPart();
+    method public java.time.Duration truncatedTo(java.time.temporal.TemporalUnit);
+    method public java.time.Duration withNanos(int);
+    method public java.time.Duration withSeconds(long);
+    field public static final java.time.Duration ZERO;
+  }
+
+  public final class Instant implements java.lang.Comparable<java.time.Instant> java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.OffsetDateTime atOffset(java.time.ZoneOffset);
+    method public java.time.ZonedDateTime atZone(java.time.ZoneId);
+    method public int compareTo(java.time.Instant);
+    method public static java.time.Instant from(java.time.temporal.TemporalAccessor);
+    method public long getEpochSecond();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getNano();
+    method public boolean isAfter(java.time.Instant);
+    method public boolean isBefore(java.time.Instant);
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public boolean isSupported(java.time.temporal.TemporalUnit);
+    method public java.time.Instant minus(java.time.temporal.TemporalAmount);
+    method public java.time.Instant minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.Instant minusMillis(long);
+    method public java.time.Instant minusNanos(long);
+    method public java.time.Instant minusSeconds(long);
+    method public static java.time.Instant now();
+    method public static java.time.Instant now(java.time.Clock);
+    method public static java.time.Instant ofEpochMilli(long);
+    method public static java.time.Instant ofEpochSecond(long);
+    method public static java.time.Instant ofEpochSecond(long, long);
+    method public static java.time.Instant parse(CharSequence);
+    method public java.time.Instant plus(java.time.temporal.TemporalAmount);
+    method public java.time.Instant plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.Instant plusMillis(long);
+    method public java.time.Instant plusNanos(long);
+    method public java.time.Instant plusSeconds(long);
+    method public long toEpochMilli();
+    method public java.time.Instant truncatedTo(java.time.temporal.TemporalUnit);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.Instant with(java.time.temporal.TemporalAdjuster);
+    method public java.time.Instant with(java.time.temporal.TemporalField, long);
+    field public static final java.time.Instant EPOCH;
+    field public static final java.time.Instant MAX;
+    field public static final java.time.Instant MIN;
+  }
+
+  public final class LocalDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.LocalDateTime atStartOfDay();
+    method public java.time.ZonedDateTime atStartOfDay(java.time.ZoneId);
+    method public java.time.LocalDateTime atTime(java.time.LocalTime);
+    method public java.time.LocalDateTime atTime(int, int);
+    method public java.time.LocalDateTime atTime(int, int, int);
+    method public java.time.LocalDateTime atTime(int, int, int, int);
+    method public java.time.OffsetDateTime atTime(java.time.OffsetTime);
+    method public static java.time.LocalDate from(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.IsoChronology getChronology();
+    method public int getDayOfMonth();
+    method public java.time.DayOfWeek getDayOfWeek();
+    method public int getDayOfYear();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public java.time.Month getMonth();
+    method public int getMonthValue();
+    method public int getYear();
+    method public int lengthOfMonth();
+    method public java.time.LocalDate minus(java.time.temporal.TemporalAmount);
+    method public java.time.LocalDate minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.LocalDate minusDays(long);
+    method public java.time.LocalDate minusMonths(long);
+    method public java.time.LocalDate minusWeeks(long);
+    method public java.time.LocalDate minusYears(long);
+    method public static java.time.LocalDate now();
+    method public static java.time.LocalDate now(java.time.ZoneId);
+    method public static java.time.LocalDate now(java.time.Clock);
+    method public static java.time.LocalDate of(int, java.time.Month, int);
+    method public static java.time.LocalDate of(int, int, int);
+    method public static java.time.LocalDate ofEpochDay(long);
+    method public static java.time.LocalDate ofYearDay(int, int);
+    method public static java.time.LocalDate parse(CharSequence);
+    method public static java.time.LocalDate parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.LocalDate plus(java.time.temporal.TemporalAmount);
+    method public java.time.LocalDate plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.LocalDate plusDays(long);
+    method public java.time.LocalDate plusMonths(long);
+    method public java.time.LocalDate plusWeeks(long);
+    method public java.time.LocalDate plusYears(long);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.Period until(java.time.chrono.ChronoLocalDate);
+    method public java.time.LocalDate with(java.time.temporal.TemporalAdjuster);
+    method public java.time.LocalDate with(java.time.temporal.TemporalField, long);
+    method public java.time.LocalDate withDayOfMonth(int);
+    method public java.time.LocalDate withDayOfYear(int);
+    method public java.time.LocalDate withMonth(int);
+    method public java.time.LocalDate withYear(int);
+    field public static final java.time.LocalDate MAX;
+    field public static final java.time.LocalDate MIN;
+  }
+
+  public final class LocalDateTime implements java.time.chrono.ChronoLocalDateTime<java.time.LocalDate> java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.OffsetDateTime atOffset(java.time.ZoneOffset);
+    method public java.time.ZonedDateTime atZone(java.time.ZoneId);
+    method public static java.time.LocalDateTime from(java.time.temporal.TemporalAccessor);
+    method public int getDayOfMonth();
+    method public java.time.DayOfWeek getDayOfWeek();
+    method public int getDayOfYear();
+    method public int getHour();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getMinute();
+    method public java.time.Month getMonth();
+    method public int getMonthValue();
+    method public int getNano();
+    method public int getSecond();
+    method public int getYear();
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public java.time.LocalDateTime minus(java.time.temporal.TemporalAmount);
+    method public java.time.LocalDateTime minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.LocalDateTime minusDays(long);
+    method public java.time.LocalDateTime minusHours(long);
+    method public java.time.LocalDateTime minusMinutes(long);
+    method public java.time.LocalDateTime minusMonths(long);
+    method public java.time.LocalDateTime minusNanos(long);
+    method public java.time.LocalDateTime minusSeconds(long);
+    method public java.time.LocalDateTime minusWeeks(long);
+    method public java.time.LocalDateTime minusYears(long);
+    method public static java.time.LocalDateTime now();
+    method public static java.time.LocalDateTime now(java.time.ZoneId);
+    method public static java.time.LocalDateTime now(java.time.Clock);
+    method public static java.time.LocalDateTime of(int, java.time.Month, int, int, int);
+    method public static java.time.LocalDateTime of(int, java.time.Month, int, int, int, int);
+    method public static java.time.LocalDateTime of(int, java.time.Month, int, int, int, int, int);
+    method public static java.time.LocalDateTime of(int, int, int, int, int);
+    method public static java.time.LocalDateTime of(int, int, int, int, int, int);
+    method public static java.time.LocalDateTime of(int, int, int, int, int, int, int);
+    method public static java.time.LocalDateTime of(java.time.LocalDate, java.time.LocalTime);
+    method public static java.time.LocalDateTime ofEpochSecond(long, int, java.time.ZoneOffset);
+    method public static java.time.LocalDateTime ofInstant(java.time.Instant, java.time.ZoneId);
+    method public static java.time.LocalDateTime parse(CharSequence);
+    method public static java.time.LocalDateTime parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.LocalDateTime plus(java.time.temporal.TemporalAmount);
+    method public java.time.LocalDateTime plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.LocalDateTime plusDays(long);
+    method public java.time.LocalDateTime plusHours(long);
+    method public java.time.LocalDateTime plusMinutes(long);
+    method public java.time.LocalDateTime plusMonths(long);
+    method public java.time.LocalDateTime plusNanos(long);
+    method public java.time.LocalDateTime plusSeconds(long);
+    method public java.time.LocalDateTime plusWeeks(long);
+    method public java.time.LocalDateTime plusYears(long);
+    method public java.time.LocalDate toLocalDate();
+    method public java.time.LocalTime toLocalTime();
+    method public java.time.LocalDateTime truncatedTo(java.time.temporal.TemporalUnit);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.LocalDateTime with(java.time.temporal.TemporalAdjuster);
+    method public java.time.LocalDateTime with(java.time.temporal.TemporalField, long);
+    method public java.time.LocalDateTime withDayOfMonth(int);
+    method public java.time.LocalDateTime withDayOfYear(int);
+    method public java.time.LocalDateTime withHour(int);
+    method public java.time.LocalDateTime withMinute(int);
+    method public java.time.LocalDateTime withMonth(int);
+    method public java.time.LocalDateTime withNano(int);
+    method public java.time.LocalDateTime withSecond(int);
+    method public java.time.LocalDateTime withYear(int);
+    field public static final java.time.LocalDateTime MAX;
+    field public static final java.time.LocalDateTime MIN;
+  }
+
+  public final class LocalTime implements java.lang.Comparable<java.time.LocalTime> java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.LocalDateTime atDate(java.time.LocalDate);
+    method public java.time.OffsetTime atOffset(java.time.ZoneOffset);
+    method public int compareTo(java.time.LocalTime);
+    method public String format(java.time.format.DateTimeFormatter);
+    method public static java.time.LocalTime from(java.time.temporal.TemporalAccessor);
+    method public int getHour();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getMinute();
+    method public int getNano();
+    method public int getSecond();
+    method public boolean isAfter(java.time.LocalTime);
+    method public boolean isBefore(java.time.LocalTime);
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public boolean isSupported(java.time.temporal.TemporalUnit);
+    method public java.time.LocalTime minus(java.time.temporal.TemporalAmount);
+    method public java.time.LocalTime minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.LocalTime minusHours(long);
+    method public java.time.LocalTime minusMinutes(long);
+    method public java.time.LocalTime minusNanos(long);
+    method public java.time.LocalTime minusSeconds(long);
+    method public static java.time.LocalTime now();
+    method public static java.time.LocalTime now(java.time.ZoneId);
+    method public static java.time.LocalTime now(java.time.Clock);
+    method public static java.time.LocalTime of(int, int);
+    method public static java.time.LocalTime of(int, int, int);
+    method public static java.time.LocalTime of(int, int, int, int);
+    method public static java.time.LocalTime ofInstant(java.time.Instant, java.time.ZoneId);
+    method public static java.time.LocalTime ofNanoOfDay(long);
+    method public static java.time.LocalTime ofSecondOfDay(long);
+    method public static java.time.LocalTime parse(CharSequence);
+    method public static java.time.LocalTime parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.LocalTime plus(java.time.temporal.TemporalAmount);
+    method public java.time.LocalTime plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.LocalTime plusHours(long);
+    method public java.time.LocalTime plusMinutes(long);
+    method public java.time.LocalTime plusNanos(long);
+    method public java.time.LocalTime plusSeconds(long);
+    method public long toEpochSecond(java.time.LocalDate, java.time.ZoneOffset);
+    method public long toNanoOfDay();
+    method public int toSecondOfDay();
+    method public java.time.LocalTime truncatedTo(java.time.temporal.TemporalUnit);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.LocalTime with(java.time.temporal.TemporalAdjuster);
+    method public java.time.LocalTime with(java.time.temporal.TemporalField, long);
+    method public java.time.LocalTime withHour(int);
+    method public java.time.LocalTime withMinute(int);
+    method public java.time.LocalTime withNano(int);
+    method public java.time.LocalTime withSecond(int);
+    field public static final java.time.LocalTime MAX;
+    field public static final java.time.LocalTime MIDNIGHT;
+    field public static final java.time.LocalTime MIN;
+    field public static final java.time.LocalTime NOON;
+  }
+
+  public enum Month implements java.time.temporal.TemporalAccessor java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public int firstDayOfYear(boolean);
+    method public java.time.Month firstMonthOfQuarter();
+    method public static java.time.Month from(java.time.temporal.TemporalAccessor);
+    method public String getDisplayName(java.time.format.TextStyle, java.util.Locale);
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getValue();
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public int length(boolean);
+    method public int maxLength();
+    method public int minLength();
+    method public java.time.Month minus(long);
+    method public static java.time.Month of(int);
+    method public java.time.Month plus(long);
+    enum_constant public static final java.time.Month APRIL;
+    enum_constant public static final java.time.Month AUGUST;
+    enum_constant public static final java.time.Month DECEMBER;
+    enum_constant public static final java.time.Month FEBRUARY;
+    enum_constant public static final java.time.Month JANUARY;
+    enum_constant public static final java.time.Month JULY;
+    enum_constant public static final java.time.Month JUNE;
+    enum_constant public static final java.time.Month MARCH;
+    enum_constant public static final java.time.Month MAY;
+    enum_constant public static final java.time.Month NOVEMBER;
+    enum_constant public static final java.time.Month OCTOBER;
+    enum_constant public static final java.time.Month SEPTEMBER;
+  }
+
+  public final class MonthDay implements java.lang.Comparable<java.time.MonthDay> java.io.Serializable java.time.temporal.TemporalAccessor java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.LocalDate atYear(int);
+    method public int compareTo(java.time.MonthDay);
+    method public String format(java.time.format.DateTimeFormatter);
+    method public static java.time.MonthDay from(java.time.temporal.TemporalAccessor);
+    method public int getDayOfMonth();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public java.time.Month getMonth();
+    method public int getMonthValue();
+    method public boolean isAfter(java.time.MonthDay);
+    method public boolean isBefore(java.time.MonthDay);
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public boolean isValidYear(int);
+    method public static java.time.MonthDay now();
+    method public static java.time.MonthDay now(java.time.ZoneId);
+    method public static java.time.MonthDay now(java.time.Clock);
+    method public static java.time.MonthDay of(java.time.Month, int);
+    method public static java.time.MonthDay of(int, int);
+    method public static java.time.MonthDay parse(CharSequence);
+    method public static java.time.MonthDay parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.MonthDay with(java.time.Month);
+    method public java.time.MonthDay withDayOfMonth(int);
+    method public java.time.MonthDay withMonth(int);
+  }
+
+  public final class OffsetDateTime implements java.lang.Comparable<java.time.OffsetDateTime> java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.ZonedDateTime atZoneSameInstant(java.time.ZoneId);
+    method public java.time.ZonedDateTime atZoneSimilarLocal(java.time.ZoneId);
+    method public int compareTo(java.time.OffsetDateTime);
+    method public String format(java.time.format.DateTimeFormatter);
+    method public static java.time.OffsetDateTime from(java.time.temporal.TemporalAccessor);
+    method public int getDayOfMonth();
+    method public java.time.DayOfWeek getDayOfWeek();
+    method public int getDayOfYear();
+    method public int getHour();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getMinute();
+    method public java.time.Month getMonth();
+    method public int getMonthValue();
+    method public int getNano();
+    method public java.time.ZoneOffset getOffset();
+    method public int getSecond();
+    method public int getYear();
+    method public boolean isAfter(java.time.OffsetDateTime);
+    method public boolean isBefore(java.time.OffsetDateTime);
+    method public boolean isEqual(java.time.OffsetDateTime);
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public boolean isSupported(java.time.temporal.TemporalUnit);
+    method public java.time.OffsetDateTime minus(java.time.temporal.TemporalAmount);
+    method public java.time.OffsetDateTime minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.OffsetDateTime minusDays(long);
+    method public java.time.OffsetDateTime minusHours(long);
+    method public java.time.OffsetDateTime minusMinutes(long);
+    method public java.time.OffsetDateTime minusMonths(long);
+    method public java.time.OffsetDateTime minusNanos(long);
+    method public java.time.OffsetDateTime minusSeconds(long);
+    method public java.time.OffsetDateTime minusWeeks(long);
+    method public java.time.OffsetDateTime minusYears(long);
+    method public static java.time.OffsetDateTime now();
+    method public static java.time.OffsetDateTime now(java.time.ZoneId);
+    method public static java.time.OffsetDateTime now(java.time.Clock);
+    method public static java.time.OffsetDateTime of(java.time.LocalDate, java.time.LocalTime, java.time.ZoneOffset);
+    method public static java.time.OffsetDateTime of(java.time.LocalDateTime, java.time.ZoneOffset);
+    method public static java.time.OffsetDateTime of(int, int, int, int, int, int, int, java.time.ZoneOffset);
+    method public static java.time.OffsetDateTime ofInstant(java.time.Instant, java.time.ZoneId);
+    method public static java.time.OffsetDateTime parse(CharSequence);
+    method public static java.time.OffsetDateTime parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.OffsetDateTime plus(java.time.temporal.TemporalAmount);
+    method public java.time.OffsetDateTime plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.OffsetDateTime plusDays(long);
+    method public java.time.OffsetDateTime plusHours(long);
+    method public java.time.OffsetDateTime plusMinutes(long);
+    method public java.time.OffsetDateTime plusMonths(long);
+    method public java.time.OffsetDateTime plusNanos(long);
+    method public java.time.OffsetDateTime plusSeconds(long);
+    method public java.time.OffsetDateTime plusWeeks(long);
+    method public java.time.OffsetDateTime plusYears(long);
+    method public static java.util.Comparator<java.time.OffsetDateTime> timeLineOrder();
+    method public long toEpochSecond();
+    method public java.time.Instant toInstant();
+    method public java.time.LocalDate toLocalDate();
+    method public java.time.LocalDateTime toLocalDateTime();
+    method public java.time.LocalTime toLocalTime();
+    method public java.time.OffsetTime toOffsetTime();
+    method public java.time.ZonedDateTime toZonedDateTime();
+    method public java.time.OffsetDateTime truncatedTo(java.time.temporal.TemporalUnit);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.OffsetDateTime with(java.time.temporal.TemporalAdjuster);
+    method public java.time.OffsetDateTime with(java.time.temporal.TemporalField, long);
+    method public java.time.OffsetDateTime withDayOfMonth(int);
+    method public java.time.OffsetDateTime withDayOfYear(int);
+    method public java.time.OffsetDateTime withHour(int);
+    method public java.time.OffsetDateTime withMinute(int);
+    method public java.time.OffsetDateTime withMonth(int);
+    method public java.time.OffsetDateTime withNano(int);
+    method public java.time.OffsetDateTime withOffsetSameInstant(java.time.ZoneOffset);
+    method public java.time.OffsetDateTime withOffsetSameLocal(java.time.ZoneOffset);
+    method public java.time.OffsetDateTime withSecond(int);
+    method public java.time.OffsetDateTime withYear(int);
+    field public static final java.time.OffsetDateTime MAX;
+    field public static final java.time.OffsetDateTime MIN;
+  }
+
+  public final class OffsetTime implements java.lang.Comparable<java.time.OffsetTime> java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.OffsetDateTime atDate(java.time.LocalDate);
+    method public int compareTo(java.time.OffsetTime);
+    method public String format(java.time.format.DateTimeFormatter);
+    method public static java.time.OffsetTime from(java.time.temporal.TemporalAccessor);
+    method public int getHour();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getMinute();
+    method public int getNano();
+    method public java.time.ZoneOffset getOffset();
+    method public int getSecond();
+    method public boolean isAfter(java.time.OffsetTime);
+    method public boolean isBefore(java.time.OffsetTime);
+    method public boolean isEqual(java.time.OffsetTime);
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public boolean isSupported(java.time.temporal.TemporalUnit);
+    method public java.time.OffsetTime minus(java.time.temporal.TemporalAmount);
+    method public java.time.OffsetTime minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.OffsetTime minusHours(long);
+    method public java.time.OffsetTime minusMinutes(long);
+    method public java.time.OffsetTime minusNanos(long);
+    method public java.time.OffsetTime minusSeconds(long);
+    method public static java.time.OffsetTime now();
+    method public static java.time.OffsetTime now(java.time.ZoneId);
+    method public static java.time.OffsetTime now(java.time.Clock);
+    method public static java.time.OffsetTime of(java.time.LocalTime, java.time.ZoneOffset);
+    method public static java.time.OffsetTime of(int, int, int, int, java.time.ZoneOffset);
+    method public static java.time.OffsetTime ofInstant(java.time.Instant, java.time.ZoneId);
+    method public static java.time.OffsetTime parse(CharSequence);
+    method public static java.time.OffsetTime parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.OffsetTime plus(java.time.temporal.TemporalAmount);
+    method public java.time.OffsetTime plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.OffsetTime plusHours(long);
+    method public java.time.OffsetTime plusMinutes(long);
+    method public java.time.OffsetTime plusNanos(long);
+    method public java.time.OffsetTime plusSeconds(long);
+    method public java.time.LocalTime toLocalTime();
+    method public java.time.OffsetTime truncatedTo(java.time.temporal.TemporalUnit);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.OffsetTime with(java.time.temporal.TemporalAdjuster);
+    method public java.time.OffsetTime with(java.time.temporal.TemporalField, long);
+    method public java.time.OffsetTime withHour(int);
+    method public java.time.OffsetTime withMinute(int);
+    method public java.time.OffsetTime withNano(int);
+    method public java.time.OffsetTime withOffsetSameInstant(java.time.ZoneOffset);
+    method public java.time.OffsetTime withOffsetSameLocal(java.time.ZoneOffset);
+    method public java.time.OffsetTime withSecond(int);
+    field public static final java.time.OffsetTime MAX;
+    field public static final java.time.OffsetTime MIN;
+  }
+
+  public final class Period implements java.time.chrono.ChronoPeriod java.io.Serializable {
+    method public java.time.temporal.Temporal addTo(java.time.temporal.Temporal);
+    method public static java.time.Period between(java.time.LocalDate, java.time.LocalDate);
+    method public static java.time.Period from(java.time.temporal.TemporalAmount);
+    method public long get(java.time.temporal.TemporalUnit);
+    method public java.time.chrono.IsoChronology getChronology();
+    method public int getDays();
+    method public int getMonths();
+    method public java.util.List<java.time.temporal.TemporalUnit> getUnits();
+    method public int getYears();
+    method public java.time.Period minus(java.time.temporal.TemporalAmount);
+    method public java.time.Period minusDays(long);
+    method public java.time.Period minusMonths(long);
+    method public java.time.Period minusYears(long);
+    method public java.time.Period multipliedBy(int);
+    method public java.time.Period negated();
+    method public java.time.Period normalized();
+    method public static java.time.Period of(int, int, int);
+    method public static java.time.Period ofDays(int);
+    method public static java.time.Period ofMonths(int);
+    method public static java.time.Period ofWeeks(int);
+    method public static java.time.Period ofYears(int);
+    method public static java.time.Period parse(CharSequence);
+    method public java.time.Period plus(java.time.temporal.TemporalAmount);
+    method public java.time.Period plusDays(long);
+    method public java.time.Period plusMonths(long);
+    method public java.time.Period plusYears(long);
+    method public java.time.temporal.Temporal subtractFrom(java.time.temporal.Temporal);
+    method public long toTotalMonths();
+    method public java.time.Period withDays(int);
+    method public java.time.Period withMonths(int);
+    method public java.time.Period withYears(int);
+    field public static final java.time.Period ZERO;
+  }
+
+  public final class Year implements java.lang.Comparable<java.time.Year> java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.LocalDate atDay(int);
+    method public java.time.YearMonth atMonth(java.time.Month);
+    method public java.time.YearMonth atMonth(int);
+    method public java.time.LocalDate atMonthDay(java.time.MonthDay);
+    method public int compareTo(java.time.Year);
+    method public String format(java.time.format.DateTimeFormatter);
+    method public static java.time.Year from(java.time.temporal.TemporalAccessor);
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int getValue();
+    method public boolean isAfter(java.time.Year);
+    method public boolean isBefore(java.time.Year);
+    method public static boolean isLeap(long);
+    method public boolean isLeap();
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public boolean isSupported(java.time.temporal.TemporalUnit);
+    method public boolean isValidMonthDay(java.time.MonthDay);
+    method public int length();
+    method public java.time.Year minus(java.time.temporal.TemporalAmount);
+    method public java.time.Year minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.Year minusYears(long);
+    method public static java.time.Year now();
+    method public static java.time.Year now(java.time.ZoneId);
+    method public static java.time.Year now(java.time.Clock);
+    method public static java.time.Year of(int);
+    method public static java.time.Year parse(CharSequence);
+    method public static java.time.Year parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.Year plus(java.time.temporal.TemporalAmount);
+    method public java.time.Year plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.Year plusYears(long);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.Year with(java.time.temporal.TemporalAdjuster);
+    method public java.time.Year with(java.time.temporal.TemporalField, long);
+    field public static final int MAX_VALUE = 999999999; // 0x3b9ac9ff
+    field public static final int MIN_VALUE = -999999999; // 0xc4653601
+  }
+
+  public final class YearMonth implements java.lang.Comparable<java.time.YearMonth> java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.LocalDate atDay(int);
+    method public java.time.LocalDate atEndOfMonth();
+    method public int compareTo(java.time.YearMonth);
+    method public String format(java.time.format.DateTimeFormatter);
+    method public static java.time.YearMonth from(java.time.temporal.TemporalAccessor);
+    method public long getLong(java.time.temporal.TemporalField);
+    method public java.time.Month getMonth();
+    method public int getMonthValue();
+    method public int getYear();
+    method public boolean isAfter(java.time.YearMonth);
+    method public boolean isBefore(java.time.YearMonth);
+    method public boolean isLeapYear();
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public boolean isSupported(java.time.temporal.TemporalUnit);
+    method public boolean isValidDay(int);
+    method public int lengthOfMonth();
+    method public int lengthOfYear();
+    method public java.time.YearMonth minus(java.time.temporal.TemporalAmount);
+    method public java.time.YearMonth minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.YearMonth minusMonths(long);
+    method public java.time.YearMonth minusYears(long);
+    method public static java.time.YearMonth now();
+    method public static java.time.YearMonth now(java.time.ZoneId);
+    method public static java.time.YearMonth now(java.time.Clock);
+    method public static java.time.YearMonth of(int, java.time.Month);
+    method public static java.time.YearMonth of(int, int);
+    method public static java.time.YearMonth parse(CharSequence);
+    method public static java.time.YearMonth parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.YearMonth plus(java.time.temporal.TemporalAmount);
+    method public java.time.YearMonth plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.YearMonth plusMonths(long);
+    method public java.time.YearMonth plusYears(long);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.YearMonth with(java.time.temporal.TemporalAdjuster);
+    method public java.time.YearMonth with(java.time.temporal.TemporalField, long);
+    method public java.time.YearMonth withMonth(int);
+    method public java.time.YearMonth withYear(int);
+  }
+
+  public abstract class ZoneId implements java.io.Serializable {
+    method public static java.time.ZoneId from(java.time.temporal.TemporalAccessor);
+    method public static java.util.Set<java.lang.String> getAvailableZoneIds();
+    method public String getDisplayName(java.time.format.TextStyle, java.util.Locale);
+    method public abstract String getId();
+    method public abstract java.time.zone.ZoneRules getRules();
+    method public java.time.ZoneId normalized();
+    method public static java.time.ZoneId of(String, java.util.Map<java.lang.String,java.lang.String>);
+    method public static java.time.ZoneId of(String);
+    method public static java.time.ZoneId ofOffset(String, java.time.ZoneOffset);
+    method public static java.time.ZoneId systemDefault();
+    field public static final java.util.Map<java.lang.String,java.lang.String> SHORT_IDS;
+  }
+
+  public final class ZoneOffset extends java.time.ZoneId implements java.lang.Comparable<java.time.ZoneOffset> java.io.Serializable java.time.temporal.TemporalAccessor java.time.temporal.TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public int compareTo(java.time.ZoneOffset);
+    method public static java.time.ZoneOffset from(java.time.temporal.TemporalAccessor);
+    method public String getId();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public java.time.zone.ZoneRules getRules();
+    method public int getTotalSeconds();
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public static java.time.ZoneOffset of(String);
+    method public static java.time.ZoneOffset ofHours(int);
+    method public static java.time.ZoneOffset ofHoursMinutes(int, int);
+    method public static java.time.ZoneOffset ofHoursMinutesSeconds(int, int, int);
+    method public static java.time.ZoneOffset ofTotalSeconds(int);
+    field public static final java.time.ZoneOffset MAX;
+    field public static final java.time.ZoneOffset MIN;
+    field public static final java.time.ZoneOffset UTC;
+  }
+
+  public final class ZonedDateTime implements java.time.chrono.ChronoZonedDateTime<java.time.LocalDate> java.io.Serializable java.time.temporal.Temporal {
+    method public static java.time.ZonedDateTime from(java.time.temporal.TemporalAccessor);
+    method public int getDayOfMonth();
+    method public java.time.DayOfWeek getDayOfWeek();
+    method public int getDayOfYear();
+    method public int getHour();
+    method public int getMinute();
+    method public java.time.Month getMonth();
+    method public int getMonthValue();
+    method public int getNano();
+    method public java.time.ZoneOffset getOffset();
+    method public int getSecond();
+    method public int getYear();
+    method public java.time.ZoneId getZone();
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public java.time.ZonedDateTime minus(java.time.temporal.TemporalAmount);
+    method public java.time.ZonedDateTime minus(long, java.time.temporal.TemporalUnit);
+    method public java.time.ZonedDateTime minusDays(long);
+    method public java.time.ZonedDateTime minusHours(long);
+    method public java.time.ZonedDateTime minusMinutes(long);
+    method public java.time.ZonedDateTime minusMonths(long);
+    method public java.time.ZonedDateTime minusNanos(long);
+    method public java.time.ZonedDateTime minusSeconds(long);
+    method public java.time.ZonedDateTime minusWeeks(long);
+    method public java.time.ZonedDateTime minusYears(long);
+    method public static java.time.ZonedDateTime now();
+    method public static java.time.ZonedDateTime now(java.time.ZoneId);
+    method public static java.time.ZonedDateTime now(java.time.Clock);
+    method public static java.time.ZonedDateTime of(java.time.LocalDate, java.time.LocalTime, java.time.ZoneId);
+    method public static java.time.ZonedDateTime of(java.time.LocalDateTime, java.time.ZoneId);
+    method public static java.time.ZonedDateTime of(int, int, int, int, int, int, int, java.time.ZoneId);
+    method public static java.time.ZonedDateTime ofInstant(java.time.Instant, java.time.ZoneId);
+    method public static java.time.ZonedDateTime ofInstant(java.time.LocalDateTime, java.time.ZoneOffset, java.time.ZoneId);
+    method public static java.time.ZonedDateTime ofLocal(java.time.LocalDateTime, java.time.ZoneId, java.time.ZoneOffset);
+    method public static java.time.ZonedDateTime ofStrict(java.time.LocalDateTime, java.time.ZoneOffset, java.time.ZoneId);
+    method public static java.time.ZonedDateTime parse(CharSequence);
+    method public static java.time.ZonedDateTime parse(CharSequence, java.time.format.DateTimeFormatter);
+    method public java.time.ZonedDateTime plus(java.time.temporal.TemporalAmount);
+    method public java.time.ZonedDateTime plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.ZonedDateTime plusDays(long);
+    method public java.time.ZonedDateTime plusHours(long);
+    method public java.time.ZonedDateTime plusMinutes(long);
+    method public java.time.ZonedDateTime plusMonths(long);
+    method public java.time.ZonedDateTime plusNanos(long);
+    method public java.time.ZonedDateTime plusSeconds(long);
+    method public java.time.ZonedDateTime plusWeeks(long);
+    method public java.time.ZonedDateTime plusYears(long);
+    method public java.time.LocalDate toLocalDate();
+    method public java.time.LocalDateTime toLocalDateTime();
+    method public java.time.OffsetDateTime toOffsetDateTime();
+    method public java.time.ZonedDateTime truncatedTo(java.time.temporal.TemporalUnit);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.ZonedDateTime with(java.time.temporal.TemporalAdjuster);
+    method public java.time.ZonedDateTime with(java.time.temporal.TemporalField, long);
+    method public java.time.ZonedDateTime withDayOfMonth(int);
+    method public java.time.ZonedDateTime withDayOfYear(int);
+    method public java.time.ZonedDateTime withEarlierOffsetAtOverlap();
+    method public java.time.ZonedDateTime withFixedOffsetZone();
+    method public java.time.ZonedDateTime withHour(int);
+    method public java.time.ZonedDateTime withLaterOffsetAtOverlap();
+    method public java.time.ZonedDateTime withMinute(int);
+    method public java.time.ZonedDateTime withMonth(int);
+    method public java.time.ZonedDateTime withNano(int);
+    method public java.time.ZonedDateTime withSecond(int);
+    method public java.time.ZonedDateTime withYear(int);
+    method public java.time.ZonedDateTime withZoneSameInstant(java.time.ZoneId);
+    method public java.time.ZonedDateTime withZoneSameLocal(java.time.ZoneId);
+  }
+
+}
+
+package java.time.chrono {
+
+  public abstract class AbstractChronology implements java.time.chrono.Chronology {
+    ctor protected AbstractChronology();
+    method public int compareTo(java.time.chrono.Chronology);
+    method public java.time.chrono.ChronoLocalDate resolveDate(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.format.ResolverStyle);
+  }
+
+  public interface ChronoLocalDate extends java.time.temporal.Temporal java.lang.Comparable<java.time.chrono.ChronoLocalDate> java.time.temporal.TemporalAdjuster {
+    method public default java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public default java.time.chrono.ChronoLocalDateTime<?> atTime(java.time.LocalTime);
+    method public default int compareTo(java.time.chrono.ChronoLocalDate);
+    method public boolean equals(Object);
+    method public default String format(java.time.format.DateTimeFormatter);
+    method public static java.time.chrono.ChronoLocalDate from(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.Chronology getChronology();
+    method public default java.time.chrono.Era getEra();
+    method public int hashCode();
+    method public default boolean isAfter(java.time.chrono.ChronoLocalDate);
+    method public default boolean isBefore(java.time.chrono.ChronoLocalDate);
+    method public default boolean isEqual(java.time.chrono.ChronoLocalDate);
+    method public default boolean isLeapYear();
+    method public default boolean isSupported(java.time.temporal.TemporalField);
+    method public default boolean isSupported(java.time.temporal.TemporalUnit);
+    method public int lengthOfMonth();
+    method public default int lengthOfYear();
+    method public default java.time.chrono.ChronoLocalDate minus(java.time.temporal.TemporalAmount);
+    method public default java.time.chrono.ChronoLocalDate minus(long, java.time.temporal.TemporalUnit);
+    method public default java.time.chrono.ChronoLocalDate plus(java.time.temporal.TemporalAmount);
+    method public default java.time.chrono.ChronoLocalDate plus(long, java.time.temporal.TemporalUnit);
+    method public static java.util.Comparator<java.time.chrono.ChronoLocalDate> timeLineOrder();
+    method public default long toEpochDay();
+    method public String toString();
+    method public java.time.chrono.ChronoPeriod until(java.time.chrono.ChronoLocalDate);
+    method public default java.time.chrono.ChronoLocalDate with(java.time.temporal.TemporalAdjuster);
+    method public default java.time.chrono.ChronoLocalDate with(java.time.temporal.TemporalField, long);
+  }
+
+  public interface ChronoLocalDateTime<D extends java.time.chrono.ChronoLocalDate> extends java.time.temporal.Temporal java.lang.Comparable<java.time.chrono.ChronoLocalDateTime<?>> java.time.temporal.TemporalAdjuster {
+    method public default java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public java.time.chrono.ChronoZonedDateTime<D> atZone(java.time.ZoneId);
+    method public default int compareTo(java.time.chrono.ChronoLocalDateTime<?>);
+    method public boolean equals(Object);
+    method public default String format(java.time.format.DateTimeFormatter);
+    method public static java.time.chrono.ChronoLocalDateTime<?> from(java.time.temporal.TemporalAccessor);
+    method public default java.time.chrono.Chronology getChronology();
+    method public int hashCode();
+    method public default boolean isAfter(java.time.chrono.ChronoLocalDateTime<?>);
+    method public default boolean isBefore(java.time.chrono.ChronoLocalDateTime<?>);
+    method public default boolean isEqual(java.time.chrono.ChronoLocalDateTime<?>);
+    method public default boolean isSupported(java.time.temporal.TemporalUnit);
+    method public default java.time.chrono.ChronoLocalDateTime<D> minus(java.time.temporal.TemporalAmount);
+    method public default java.time.chrono.ChronoLocalDateTime<D> minus(long, java.time.temporal.TemporalUnit);
+    method public default java.time.chrono.ChronoLocalDateTime<D> plus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.ChronoLocalDateTime<D> plus(long, java.time.temporal.TemporalUnit);
+    method public static java.util.Comparator<java.time.chrono.ChronoLocalDateTime<?>> timeLineOrder();
+    method public default long toEpochSecond(java.time.ZoneOffset);
+    method public default java.time.Instant toInstant(java.time.ZoneOffset);
+    method public D toLocalDate();
+    method public java.time.LocalTime toLocalTime();
+    method public String toString();
+    method public default java.time.chrono.ChronoLocalDateTime<D> with(java.time.temporal.TemporalAdjuster);
+    method public java.time.chrono.ChronoLocalDateTime<D> with(java.time.temporal.TemporalField, long);
+  }
+
+  public interface ChronoPeriod extends java.time.temporal.TemporalAmount {
+    method public static java.time.chrono.ChronoPeriod between(java.time.chrono.ChronoLocalDate, java.time.chrono.ChronoLocalDate);
+    method public boolean equals(Object);
+    method public java.time.chrono.Chronology getChronology();
+    method public int hashCode();
+    method public default boolean isNegative();
+    method public default boolean isZero();
+    method public java.time.chrono.ChronoPeriod minus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.ChronoPeriod multipliedBy(int);
+    method public default java.time.chrono.ChronoPeriod negated();
+    method public java.time.chrono.ChronoPeriod normalized();
+    method public java.time.chrono.ChronoPeriod plus(java.time.temporal.TemporalAmount);
+    method public String toString();
+  }
+
+  public interface ChronoZonedDateTime<D extends java.time.chrono.ChronoLocalDate> extends java.time.temporal.Temporal java.lang.Comparable<java.time.chrono.ChronoZonedDateTime<?>> {
+    method public default int compareTo(java.time.chrono.ChronoZonedDateTime<?>);
+    method public boolean equals(Object);
+    method public default String format(java.time.format.DateTimeFormatter);
+    method public static java.time.chrono.ChronoZonedDateTime<?> from(java.time.temporal.TemporalAccessor);
+    method public default java.time.chrono.Chronology getChronology();
+    method public default long getLong(java.time.temporal.TemporalField);
+    method public java.time.ZoneOffset getOffset();
+    method public java.time.ZoneId getZone();
+    method public int hashCode();
+    method public default boolean isAfter(java.time.chrono.ChronoZonedDateTime<?>);
+    method public default boolean isBefore(java.time.chrono.ChronoZonedDateTime<?>);
+    method public default boolean isEqual(java.time.chrono.ChronoZonedDateTime<?>);
+    method public default boolean isSupported(java.time.temporal.TemporalUnit);
+    method public default java.time.chrono.ChronoZonedDateTime<D> minus(java.time.temporal.TemporalAmount);
+    method public default java.time.chrono.ChronoZonedDateTime<D> minus(long, java.time.temporal.TemporalUnit);
+    method public default java.time.chrono.ChronoZonedDateTime<D> plus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.ChronoZonedDateTime<D> plus(long, java.time.temporal.TemporalUnit);
+    method public static java.util.Comparator<java.time.chrono.ChronoZonedDateTime<?>> timeLineOrder();
+    method public default long toEpochSecond();
+    method public default java.time.Instant toInstant();
+    method public default D toLocalDate();
+    method public java.time.chrono.ChronoLocalDateTime<D> toLocalDateTime();
+    method public default java.time.LocalTime toLocalTime();
+    method public String toString();
+    method public default java.time.chrono.ChronoZonedDateTime<D> with(java.time.temporal.TemporalAdjuster);
+    method public java.time.chrono.ChronoZonedDateTime<D> with(java.time.temporal.TemporalField, long);
+    method public java.time.chrono.ChronoZonedDateTime<D> withEarlierOffsetAtOverlap();
+    method public java.time.chrono.ChronoZonedDateTime<D> withLaterOffsetAtOverlap();
+    method public java.time.chrono.ChronoZonedDateTime<D> withZoneSameInstant(java.time.ZoneId);
+    method public java.time.chrono.ChronoZonedDateTime<D> withZoneSameLocal(java.time.ZoneId);
+  }
+
+  public interface Chronology extends java.lang.Comparable<java.time.chrono.Chronology> {
+    method public int compareTo(java.time.chrono.Chronology);
+    method public default java.time.chrono.ChronoLocalDate date(java.time.chrono.Era, int, int, int);
+    method public java.time.chrono.ChronoLocalDate date(int, int, int);
+    method public java.time.chrono.ChronoLocalDate date(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.ChronoLocalDate dateEpochDay(long);
+    method public default java.time.chrono.ChronoLocalDate dateNow();
+    method public default java.time.chrono.ChronoLocalDate dateNow(java.time.ZoneId);
+    method public default java.time.chrono.ChronoLocalDate dateNow(java.time.Clock);
+    method public default java.time.chrono.ChronoLocalDate dateYearDay(java.time.chrono.Era, int, int);
+    method public java.time.chrono.ChronoLocalDate dateYearDay(int, int);
+    method public boolean equals(Object);
+    method public java.time.chrono.Era eraOf(int);
+    method public java.util.List<java.time.chrono.Era> eras();
+    method public static java.time.chrono.Chronology from(java.time.temporal.TemporalAccessor);
+    method public static java.util.Set<java.time.chrono.Chronology> getAvailableChronologies();
+    method public String getCalendarType();
+    method public default String getDisplayName(java.time.format.TextStyle, java.util.Locale);
+    method public String getId();
+    method public int hashCode();
+    method public boolean isLeapYear(long);
+    method public default java.time.chrono.ChronoLocalDateTime<? extends java.time.chrono.ChronoLocalDate> localDateTime(java.time.temporal.TemporalAccessor);
+    method public static java.time.chrono.Chronology of(String);
+    method public static java.time.chrono.Chronology ofLocale(java.util.Locale);
+    method public default java.time.chrono.ChronoPeriod period(int, int, int);
+    method public int prolepticYear(java.time.chrono.Era, int);
+    method public java.time.temporal.ValueRange range(java.time.temporal.ChronoField);
+    method public java.time.chrono.ChronoLocalDate resolveDate(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.format.ResolverStyle);
+    method public String toString();
+    method public default java.time.chrono.ChronoZonedDateTime<? extends java.time.chrono.ChronoLocalDate> zonedDateTime(java.time.temporal.TemporalAccessor);
+    method public default java.time.chrono.ChronoZonedDateTime<? extends java.time.chrono.ChronoLocalDate> zonedDateTime(java.time.Instant, java.time.ZoneId);
+  }
+
+  public interface Era extends java.time.temporal.TemporalAccessor java.time.temporal.TemporalAdjuster {
+    method public default java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+    method public default String getDisplayName(java.time.format.TextStyle, java.util.Locale);
+    method public default long getLong(java.time.temporal.TemporalField);
+    method public int getValue();
+    method public default boolean isSupported(java.time.temporal.TemporalField);
+  }
+
+  public final class HijrahChronology extends java.time.chrono.AbstractChronology implements java.io.Serializable {
+    method public java.time.chrono.HijrahDate date(java.time.chrono.Era, int, int, int);
+    method public java.time.chrono.HijrahDate date(int, int, int);
+    method public java.time.chrono.HijrahDate date(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.HijrahDate dateEpochDay(long);
+    method public java.time.chrono.HijrahDate dateNow();
+    method public java.time.chrono.HijrahDate dateNow(java.time.ZoneId);
+    method public java.time.chrono.HijrahDate dateNow(java.time.Clock);
+    method public java.time.chrono.HijrahDate dateYearDay(java.time.chrono.Era, int, int);
+    method public java.time.chrono.HijrahDate dateYearDay(int, int);
+    method public java.time.chrono.HijrahEra eraOf(int);
+    method public java.util.List<java.time.chrono.Era> eras();
+    method public String getCalendarType();
+    method public String getId();
+    method public boolean isLeapYear(long);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> localDateTime(java.time.temporal.TemporalAccessor);
+    method public int prolepticYear(java.time.chrono.Era, int);
+    method public java.time.temporal.ValueRange range(java.time.temporal.ChronoField);
+    method public java.time.chrono.HijrahDate resolveDate(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.format.ResolverStyle);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.HijrahDate> zonedDateTime(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.HijrahDate> zonedDateTime(java.time.Instant, java.time.ZoneId);
+    field public static final java.time.chrono.HijrahChronology INSTANCE;
+  }
+
+  public final class HijrahDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.HijrahDate> atTime(java.time.LocalTime);
+    method public static java.time.chrono.HijrahDate from(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.HijrahChronology getChronology();
+    method public java.time.chrono.HijrahEra getEra();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int lengthOfMonth();
+    method public java.time.chrono.HijrahDate minus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.HijrahDate minus(long, java.time.temporal.TemporalUnit);
+    method public static java.time.chrono.HijrahDate now();
+    method public static java.time.chrono.HijrahDate now(java.time.ZoneId);
+    method public static java.time.chrono.HijrahDate now(java.time.Clock);
+    method public static java.time.chrono.HijrahDate of(int, int, int);
+    method public java.time.chrono.HijrahDate plus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.HijrahDate plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.ChronoPeriod until(java.time.chrono.ChronoLocalDate);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.HijrahDate with(java.time.temporal.TemporalField, long);
+    method public java.time.chrono.HijrahDate with(java.time.temporal.TemporalAdjuster);
+    method public java.time.chrono.HijrahDate withVariant(java.time.chrono.HijrahChronology);
+  }
+
+  public enum HijrahEra implements java.time.chrono.Era {
+    method public int getValue();
+    method public static java.time.chrono.HijrahEra of(int);
+    enum_constant public static final java.time.chrono.HijrahEra AH;
+  }
+
+  public final class IsoChronology extends java.time.chrono.AbstractChronology implements java.io.Serializable {
+    method public java.time.LocalDate date(java.time.chrono.Era, int, int, int);
+    method public java.time.LocalDate date(int, int, int);
+    method public java.time.LocalDate date(java.time.temporal.TemporalAccessor);
+    method public java.time.LocalDate dateEpochDay(long);
+    method public java.time.LocalDate dateNow();
+    method public java.time.LocalDate dateNow(java.time.ZoneId);
+    method public java.time.LocalDate dateNow(java.time.Clock);
+    method public java.time.LocalDate dateYearDay(java.time.chrono.Era, int, int);
+    method public java.time.LocalDate dateYearDay(int, int);
+    method public java.time.chrono.IsoEra eraOf(int);
+    method public java.util.List<java.time.chrono.Era> eras();
+    method public String getCalendarType();
+    method public String getId();
+    method public boolean isLeapYear(long);
+    method public java.time.LocalDateTime localDateTime(java.time.temporal.TemporalAccessor);
+    method public java.time.Period period(int, int, int);
+    method public int prolepticYear(java.time.chrono.Era, int);
+    method public java.time.temporal.ValueRange range(java.time.temporal.ChronoField);
+    method public java.time.LocalDate resolveDate(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.format.ResolverStyle);
+    method public java.time.ZonedDateTime zonedDateTime(java.time.temporal.TemporalAccessor);
+    method public java.time.ZonedDateTime zonedDateTime(java.time.Instant, java.time.ZoneId);
+    field public static final java.time.chrono.IsoChronology INSTANCE;
+  }
+
+  public enum IsoEra implements java.time.chrono.Era {
+    method public int getValue();
+    method public static java.time.chrono.IsoEra of(int);
+    enum_constant public static final java.time.chrono.IsoEra BCE;
+    enum_constant public static final java.time.chrono.IsoEra CE;
+  }
+
+  public final class JapaneseChronology extends java.time.chrono.AbstractChronology implements java.io.Serializable {
+    method public java.time.chrono.JapaneseDate date(java.time.chrono.Era, int, int, int);
+    method public java.time.chrono.JapaneseDate date(int, int, int);
+    method public java.time.chrono.JapaneseDate date(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.JapaneseDate dateEpochDay(long);
+    method public java.time.chrono.JapaneseDate dateNow();
+    method public java.time.chrono.JapaneseDate dateNow(java.time.ZoneId);
+    method public java.time.chrono.JapaneseDate dateNow(java.time.Clock);
+    method public java.time.chrono.JapaneseDate dateYearDay(java.time.chrono.Era, int, int);
+    method public java.time.chrono.JapaneseDate dateYearDay(int, int);
+    method public java.time.chrono.JapaneseEra eraOf(int);
+    method public java.util.List<java.time.chrono.Era> eras();
+    method public String getCalendarType();
+    method public String getId();
+    method public boolean isLeapYear(long);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> localDateTime(java.time.temporal.TemporalAccessor);
+    method public int prolepticYear(java.time.chrono.Era, int);
+    method public java.time.temporal.ValueRange range(java.time.temporal.ChronoField);
+    method public java.time.chrono.JapaneseDate resolveDate(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.format.ResolverStyle);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.JapaneseDate> zonedDateTime(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.JapaneseDate> zonedDateTime(java.time.Instant, java.time.ZoneId);
+    field public static final java.time.chrono.JapaneseChronology INSTANCE;
+  }
+
+  public final class JapaneseDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.JapaneseDate> atTime(java.time.LocalTime);
+    method public static java.time.chrono.JapaneseDate from(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.JapaneseChronology getChronology();
+    method public java.time.chrono.JapaneseEra getEra();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int lengthOfMonth();
+    method public java.time.chrono.JapaneseDate minus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.JapaneseDate minus(long, java.time.temporal.TemporalUnit);
+    method public static java.time.chrono.JapaneseDate now();
+    method public static java.time.chrono.JapaneseDate now(java.time.ZoneId);
+    method public static java.time.chrono.JapaneseDate now(java.time.Clock);
+    method public static java.time.chrono.JapaneseDate of(java.time.chrono.JapaneseEra, int, int, int);
+    method public static java.time.chrono.JapaneseDate of(int, int, int);
+    method public java.time.chrono.JapaneseDate plus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.JapaneseDate plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.ChronoPeriod until(java.time.chrono.ChronoLocalDate);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.JapaneseDate with(java.time.temporal.TemporalField, long);
+    method public java.time.chrono.JapaneseDate with(java.time.temporal.TemporalAdjuster);
+  }
+
+  public final class JapaneseEra implements java.time.chrono.Era java.io.Serializable {
+    method public int getValue();
+    method public static java.time.chrono.JapaneseEra of(int);
+    method public static java.time.chrono.JapaneseEra valueOf(String);
+    method public static java.time.chrono.JapaneseEra[] values();
+    field public static final java.time.chrono.JapaneseEra HEISEI;
+    field public static final java.time.chrono.JapaneseEra MEIJI;
+    field public static final java.time.chrono.JapaneseEra REIWA;
+    field public static final java.time.chrono.JapaneseEra SHOWA;
+    field public static final java.time.chrono.JapaneseEra TAISHO;
+  }
+
+  public final class MinguoChronology extends java.time.chrono.AbstractChronology implements java.io.Serializable {
+    method public java.time.chrono.MinguoDate date(java.time.chrono.Era, int, int, int);
+    method public java.time.chrono.MinguoDate date(int, int, int);
+    method public java.time.chrono.MinguoDate date(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.MinguoDate dateEpochDay(long);
+    method public java.time.chrono.MinguoDate dateNow();
+    method public java.time.chrono.MinguoDate dateNow(java.time.ZoneId);
+    method public java.time.chrono.MinguoDate dateNow(java.time.Clock);
+    method public java.time.chrono.MinguoDate dateYearDay(java.time.chrono.Era, int, int);
+    method public java.time.chrono.MinguoDate dateYearDay(int, int);
+    method public java.time.chrono.MinguoEra eraOf(int);
+    method public java.util.List<java.time.chrono.Era> eras();
+    method public String getCalendarType();
+    method public String getId();
+    method public boolean isLeapYear(long);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> localDateTime(java.time.temporal.TemporalAccessor);
+    method public int prolepticYear(java.time.chrono.Era, int);
+    method public java.time.temporal.ValueRange range(java.time.temporal.ChronoField);
+    method public java.time.chrono.MinguoDate resolveDate(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.format.ResolverStyle);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.MinguoDate> zonedDateTime(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.MinguoDate> zonedDateTime(java.time.Instant, java.time.ZoneId);
+    field public static final java.time.chrono.MinguoChronology INSTANCE;
+  }
+
+  public final class MinguoDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.MinguoDate> atTime(java.time.LocalTime);
+    method public static java.time.chrono.MinguoDate from(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.MinguoChronology getChronology();
+    method public java.time.chrono.MinguoEra getEra();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int lengthOfMonth();
+    method public java.time.chrono.MinguoDate minus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.MinguoDate minus(long, java.time.temporal.TemporalUnit);
+    method public static java.time.chrono.MinguoDate now();
+    method public static java.time.chrono.MinguoDate now(java.time.ZoneId);
+    method public static java.time.chrono.MinguoDate now(java.time.Clock);
+    method public static java.time.chrono.MinguoDate of(int, int, int);
+    method public java.time.chrono.MinguoDate plus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.MinguoDate plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.ChronoPeriod until(java.time.chrono.ChronoLocalDate);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.MinguoDate with(java.time.temporal.TemporalField, long);
+    method public java.time.chrono.MinguoDate with(java.time.temporal.TemporalAdjuster);
+  }
+
+  public enum MinguoEra implements java.time.chrono.Era {
+    method public int getValue();
+    method public static java.time.chrono.MinguoEra of(int);
+    enum_constant public static final java.time.chrono.MinguoEra BEFORE_ROC;
+    enum_constant public static final java.time.chrono.MinguoEra ROC;
+  }
+
+  public final class ThaiBuddhistChronology extends java.time.chrono.AbstractChronology implements java.io.Serializable {
+    method public java.time.chrono.ThaiBuddhistDate date(java.time.chrono.Era, int, int, int);
+    method public java.time.chrono.ThaiBuddhistDate date(int, int, int);
+    method public java.time.chrono.ThaiBuddhistDate date(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.ThaiBuddhistDate dateEpochDay(long);
+    method public java.time.chrono.ThaiBuddhistDate dateNow();
+    method public java.time.chrono.ThaiBuddhistDate dateNow(java.time.ZoneId);
+    method public java.time.chrono.ThaiBuddhistDate dateNow(java.time.Clock);
+    method public java.time.chrono.ThaiBuddhistDate dateYearDay(java.time.chrono.Era, int, int);
+    method public java.time.chrono.ThaiBuddhistDate dateYearDay(int, int);
+    method public java.time.chrono.ThaiBuddhistEra eraOf(int);
+    method public java.util.List<java.time.chrono.Era> eras();
+    method public String getCalendarType();
+    method public String getId();
+    method public boolean isLeapYear(long);
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> localDateTime(java.time.temporal.TemporalAccessor);
+    method public int prolepticYear(java.time.chrono.Era, int);
+    method public java.time.temporal.ValueRange range(java.time.temporal.ChronoField);
+    method public java.time.chrono.ThaiBuddhistDate resolveDate(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.format.ResolverStyle);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.ThaiBuddhistDate> zonedDateTime(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.ChronoZonedDateTime<java.time.chrono.ThaiBuddhistDate> zonedDateTime(java.time.Instant, java.time.ZoneId);
+    field public static final java.time.chrono.ThaiBuddhistChronology INSTANCE;
+  }
+
+  public final class ThaiBuddhistDate implements java.time.chrono.ChronoLocalDate java.io.Serializable java.time.temporal.Temporal java.time.temporal.TemporalAdjuster {
+    method public java.time.chrono.ChronoLocalDateTime<java.time.chrono.ThaiBuddhistDate> atTime(java.time.LocalTime);
+    method public static java.time.chrono.ThaiBuddhistDate from(java.time.temporal.TemporalAccessor);
+    method public java.time.chrono.ThaiBuddhistChronology getChronology();
+    method public java.time.chrono.ThaiBuddhistEra getEra();
+    method public long getLong(java.time.temporal.TemporalField);
+    method public int lengthOfMonth();
+    method public java.time.chrono.ThaiBuddhistDate minus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.ThaiBuddhistDate minus(long, java.time.temporal.TemporalUnit);
+    method public static java.time.chrono.ThaiBuddhistDate now();
+    method public static java.time.chrono.ThaiBuddhistDate now(java.time.ZoneId);
+    method public static java.time.chrono.ThaiBuddhistDate now(java.time.Clock);
+    method public static java.time.chrono.ThaiBuddhistDate of(int, int, int);
+    method public java.time.chrono.ThaiBuddhistDate plus(java.time.temporal.TemporalAmount);
+    method public java.time.chrono.ThaiBuddhistDate plus(long, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.ChronoPeriod until(java.time.chrono.ChronoLocalDate);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public java.time.chrono.ThaiBuddhistDate with(java.time.temporal.TemporalField, long);
+    method public java.time.chrono.ThaiBuddhistDate with(java.time.temporal.TemporalAdjuster);
+  }
+
+  public enum ThaiBuddhistEra implements java.time.chrono.Era {
+    method public int getValue();
+    method public static java.time.chrono.ThaiBuddhistEra of(int);
+    enum_constant public static final java.time.chrono.ThaiBuddhistEra BE;
+    enum_constant public static final java.time.chrono.ThaiBuddhistEra BEFORE_BE;
+  }
+
+}
+
+package java.time.format {
+
+  public final class DateTimeFormatter {
+    method public String format(java.time.temporal.TemporalAccessor);
+    method public void formatTo(java.time.temporal.TemporalAccessor, Appendable);
+    method public java.time.chrono.Chronology getChronology();
+    method public java.time.format.DecimalStyle getDecimalStyle();
+    method public java.util.Locale getLocale();
+    method public java.util.Set<java.time.temporal.TemporalField> getResolverFields();
+    method public java.time.format.ResolverStyle getResolverStyle();
+    method public java.time.ZoneId getZone();
+    method public static java.time.format.DateTimeFormatter ofLocalizedDate(java.time.format.FormatStyle);
+    method public static java.time.format.DateTimeFormatter ofLocalizedDateTime(java.time.format.FormatStyle);
+    method public static java.time.format.DateTimeFormatter ofLocalizedDateTime(java.time.format.FormatStyle, java.time.format.FormatStyle);
+    method public static java.time.format.DateTimeFormatter ofLocalizedTime(java.time.format.FormatStyle);
+    method public static java.time.format.DateTimeFormatter ofPattern(String);
+    method public static java.time.format.DateTimeFormatter ofPattern(String, java.util.Locale);
+    method public java.time.temporal.TemporalAccessor parse(CharSequence);
+    method public java.time.temporal.TemporalAccessor parse(CharSequence, java.text.ParsePosition);
+    method public <T> T parse(CharSequence, java.time.temporal.TemporalQuery<T>);
+    method public java.time.temporal.TemporalAccessor parseBest(CharSequence, java.time.temporal.TemporalQuery<?>...);
+    method public java.time.temporal.TemporalAccessor parseUnresolved(CharSequence, java.text.ParsePosition);
+    method public static java.time.temporal.TemporalQuery<java.time.Period> parsedExcessDays();
+    method public static java.time.temporal.TemporalQuery<java.lang.Boolean> parsedLeapSecond();
+    method public java.text.Format toFormat();
+    method public java.text.Format toFormat(java.time.temporal.TemporalQuery<?>);
+    method public java.time.format.DateTimeFormatter withChronology(java.time.chrono.Chronology);
+    method public java.time.format.DateTimeFormatter withDecimalStyle(java.time.format.DecimalStyle);
+    method public java.time.format.DateTimeFormatter withLocale(java.util.Locale);
+    method public java.time.format.DateTimeFormatter withResolverFields(java.time.temporal.TemporalField...);
+    method public java.time.format.DateTimeFormatter withResolverFields(java.util.Set<java.time.temporal.TemporalField>);
+    method public java.time.format.DateTimeFormatter withResolverStyle(java.time.format.ResolverStyle);
+    method public java.time.format.DateTimeFormatter withZone(java.time.ZoneId);
+    field public static final java.time.format.DateTimeFormatter BASIC_ISO_DATE;
+    field public static final java.time.format.DateTimeFormatter ISO_DATE;
+    field public static final java.time.format.DateTimeFormatter ISO_DATE_TIME;
+    field public static final java.time.format.DateTimeFormatter ISO_INSTANT;
+    field public static final java.time.format.DateTimeFormatter ISO_LOCAL_DATE;
+    field public static final java.time.format.DateTimeFormatter ISO_LOCAL_DATE_TIME;
+    field public static final java.time.format.DateTimeFormatter ISO_LOCAL_TIME;
+    field public static final java.time.format.DateTimeFormatter ISO_OFFSET_DATE;
+    field public static final java.time.format.DateTimeFormatter ISO_OFFSET_DATE_TIME;
+    field public static final java.time.format.DateTimeFormatter ISO_OFFSET_TIME;
+    field public static final java.time.format.DateTimeFormatter ISO_ORDINAL_DATE;
+    field public static final java.time.format.DateTimeFormatter ISO_TIME;
+    field public static final java.time.format.DateTimeFormatter ISO_WEEK_DATE;
+    field public static final java.time.format.DateTimeFormatter ISO_ZONED_DATE_TIME;
+    field public static final java.time.format.DateTimeFormatter RFC_1123_DATE_TIME;
+  }
+
+  public final class DateTimeFormatterBuilder {
+    ctor public DateTimeFormatterBuilder();
+    method public java.time.format.DateTimeFormatterBuilder append(java.time.format.DateTimeFormatter);
+    method public java.time.format.DateTimeFormatterBuilder appendChronologyId();
+    method public java.time.format.DateTimeFormatterBuilder appendChronologyText(java.time.format.TextStyle);
+    method public java.time.format.DateTimeFormatterBuilder appendFraction(java.time.temporal.TemporalField, int, int, boolean);
+    method public java.time.format.DateTimeFormatterBuilder appendInstant();
+    method public java.time.format.DateTimeFormatterBuilder appendInstant(int);
+    method public java.time.format.DateTimeFormatterBuilder appendLiteral(char);
+    method public java.time.format.DateTimeFormatterBuilder appendLiteral(String);
+    method public java.time.format.DateTimeFormatterBuilder appendLocalized(java.time.format.FormatStyle, java.time.format.FormatStyle);
+    method public java.time.format.DateTimeFormatterBuilder appendLocalizedOffset(java.time.format.TextStyle);
+    method public java.time.format.DateTimeFormatterBuilder appendOffset(String, String);
+    method public java.time.format.DateTimeFormatterBuilder appendOffsetId();
+    method public java.time.format.DateTimeFormatterBuilder appendOptional(java.time.format.DateTimeFormatter);
+    method public java.time.format.DateTimeFormatterBuilder appendPattern(String);
+    method public java.time.format.DateTimeFormatterBuilder appendText(java.time.temporal.TemporalField);
+    method public java.time.format.DateTimeFormatterBuilder appendText(java.time.temporal.TemporalField, java.time.format.TextStyle);
+    method public java.time.format.DateTimeFormatterBuilder appendText(java.time.temporal.TemporalField, java.util.Map<java.lang.Long,java.lang.String>);
+    method public java.time.format.DateTimeFormatterBuilder appendValue(java.time.temporal.TemporalField);
+    method public java.time.format.DateTimeFormatterBuilder appendValue(java.time.temporal.TemporalField, int);
+    method public java.time.format.DateTimeFormatterBuilder appendValue(java.time.temporal.TemporalField, int, int, java.time.format.SignStyle);
+    method public java.time.format.DateTimeFormatterBuilder appendValueReduced(java.time.temporal.TemporalField, int, int, int);
+    method public java.time.format.DateTimeFormatterBuilder appendValueReduced(java.time.temporal.TemporalField, int, int, java.time.chrono.ChronoLocalDate);
+    method public java.time.format.DateTimeFormatterBuilder appendZoneId();
+    method public java.time.format.DateTimeFormatterBuilder appendZoneOrOffsetId();
+    method public java.time.format.DateTimeFormatterBuilder appendZoneRegionId();
+    method public java.time.format.DateTimeFormatterBuilder appendZoneText(java.time.format.TextStyle);
+    method public java.time.format.DateTimeFormatterBuilder appendZoneText(java.time.format.TextStyle, java.util.Set<java.time.ZoneId>);
+    method public static String getLocalizedDateTimePattern(java.time.format.FormatStyle, java.time.format.FormatStyle, java.time.chrono.Chronology, java.util.Locale);
+    method public java.time.format.DateTimeFormatterBuilder optionalEnd();
+    method public java.time.format.DateTimeFormatterBuilder optionalStart();
+    method public java.time.format.DateTimeFormatterBuilder padNext(int);
+    method public java.time.format.DateTimeFormatterBuilder padNext(int, char);
+    method public java.time.format.DateTimeFormatterBuilder parseCaseInsensitive();
+    method public java.time.format.DateTimeFormatterBuilder parseCaseSensitive();
+    method public java.time.format.DateTimeFormatterBuilder parseDefaulting(java.time.temporal.TemporalField, long);
+    method public java.time.format.DateTimeFormatterBuilder parseLenient();
+    method public java.time.format.DateTimeFormatterBuilder parseStrict();
+    method public java.time.format.DateTimeFormatter toFormatter();
+    method public java.time.format.DateTimeFormatter toFormatter(java.util.Locale);
+  }
+
+  public class DateTimeParseException extends java.time.DateTimeException {
+    ctor public DateTimeParseException(String, CharSequence, int);
+    ctor public DateTimeParseException(String, CharSequence, int, Throwable);
+    method public int getErrorIndex();
+    method public String getParsedString();
+  }
+
+  public final class DecimalStyle {
+    method public static java.util.Set<java.util.Locale> getAvailableLocales();
+    method public char getDecimalSeparator();
+    method public char getNegativeSign();
+    method public char getPositiveSign();
+    method public char getZeroDigit();
+    method public static java.time.format.DecimalStyle of(java.util.Locale);
+    method public static java.time.format.DecimalStyle ofDefaultLocale();
+    method public java.time.format.DecimalStyle withDecimalSeparator(char);
+    method public java.time.format.DecimalStyle withNegativeSign(char);
+    method public java.time.format.DecimalStyle withPositiveSign(char);
+    method public java.time.format.DecimalStyle withZeroDigit(char);
+    field public static final java.time.format.DecimalStyle STANDARD;
+  }
+
+  public enum FormatStyle {
+    enum_constant public static final java.time.format.FormatStyle FULL;
+    enum_constant public static final java.time.format.FormatStyle LONG;
+    enum_constant public static final java.time.format.FormatStyle MEDIUM;
+    enum_constant public static final java.time.format.FormatStyle SHORT;
+  }
+
+  public enum ResolverStyle {
+    enum_constant public static final java.time.format.ResolverStyle LENIENT;
+    enum_constant public static final java.time.format.ResolverStyle SMART;
+    enum_constant public static final java.time.format.ResolverStyle STRICT;
+  }
+
+  public enum SignStyle {
+    enum_constant public static final java.time.format.SignStyle ALWAYS;
+    enum_constant public static final java.time.format.SignStyle EXCEEDS_PAD;
+    enum_constant public static final java.time.format.SignStyle NEVER;
+    enum_constant public static final java.time.format.SignStyle NORMAL;
+    enum_constant public static final java.time.format.SignStyle NOT_NEGATIVE;
+  }
+
+  public enum TextStyle {
+    method public java.time.format.TextStyle asNormal();
+    method public java.time.format.TextStyle asStandalone();
+    method public boolean isStandalone();
+    enum_constant public static final java.time.format.TextStyle FULL;
+    enum_constant public static final java.time.format.TextStyle FULL_STANDALONE;
+    enum_constant public static final java.time.format.TextStyle NARROW;
+    enum_constant public static final java.time.format.TextStyle NARROW_STANDALONE;
+    enum_constant public static final java.time.format.TextStyle SHORT;
+    enum_constant public static final java.time.format.TextStyle SHORT_STANDALONE;
+  }
+
+}
+
+package java.time.temporal {
+
+  public enum ChronoField implements java.time.temporal.TemporalField {
+    method public <R extends java.time.temporal.Temporal> R adjustInto(R, long);
+    method public int checkValidIntValue(long);
+    method public long checkValidValue(long);
+    method public java.time.temporal.TemporalUnit getBaseUnit();
+    method public long getFrom(java.time.temporal.TemporalAccessor);
+    method public java.time.temporal.TemporalUnit getRangeUnit();
+    method public boolean isDateBased();
+    method public boolean isSupportedBy(java.time.temporal.TemporalAccessor);
+    method public boolean isTimeBased();
+    method public java.time.temporal.ValueRange range();
+    method public java.time.temporal.ValueRange rangeRefinedBy(java.time.temporal.TemporalAccessor);
+    enum_constant public static final java.time.temporal.ChronoField ALIGNED_DAY_OF_WEEK_IN_MONTH;
+    enum_constant public static final java.time.temporal.ChronoField ALIGNED_DAY_OF_WEEK_IN_YEAR;
+    enum_constant public static final java.time.temporal.ChronoField ALIGNED_WEEK_OF_MONTH;
+    enum_constant public static final java.time.temporal.ChronoField ALIGNED_WEEK_OF_YEAR;
+    enum_constant public static final java.time.temporal.ChronoField AMPM_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField CLOCK_HOUR_OF_AMPM;
+    enum_constant public static final java.time.temporal.ChronoField CLOCK_HOUR_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField DAY_OF_MONTH;
+    enum_constant public static final java.time.temporal.ChronoField DAY_OF_WEEK;
+    enum_constant public static final java.time.temporal.ChronoField DAY_OF_YEAR;
+    enum_constant public static final java.time.temporal.ChronoField EPOCH_DAY;
+    enum_constant public static final java.time.temporal.ChronoField ERA;
+    enum_constant public static final java.time.temporal.ChronoField HOUR_OF_AMPM;
+    enum_constant public static final java.time.temporal.ChronoField HOUR_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField INSTANT_SECONDS;
+    enum_constant public static final java.time.temporal.ChronoField MICRO_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField MICRO_OF_SECOND;
+    enum_constant public static final java.time.temporal.ChronoField MILLI_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField MILLI_OF_SECOND;
+    enum_constant public static final java.time.temporal.ChronoField MINUTE_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField MINUTE_OF_HOUR;
+    enum_constant public static final java.time.temporal.ChronoField MONTH_OF_YEAR;
+    enum_constant public static final java.time.temporal.ChronoField NANO_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField NANO_OF_SECOND;
+    enum_constant public static final java.time.temporal.ChronoField OFFSET_SECONDS;
+    enum_constant public static final java.time.temporal.ChronoField PROLEPTIC_MONTH;
+    enum_constant public static final java.time.temporal.ChronoField SECOND_OF_DAY;
+    enum_constant public static final java.time.temporal.ChronoField SECOND_OF_MINUTE;
+    enum_constant public static final java.time.temporal.ChronoField YEAR;
+    enum_constant public static final java.time.temporal.ChronoField YEAR_OF_ERA;
+  }
+
+  public enum ChronoUnit implements java.time.temporal.TemporalUnit {
+    method public <R extends java.time.temporal.Temporal> R addTo(R, long);
+    method public long between(java.time.temporal.Temporal, java.time.temporal.Temporal);
+    method public java.time.Duration getDuration();
+    method public boolean isDateBased();
+    method public boolean isDurationEstimated();
+    method public boolean isTimeBased();
+    enum_constant public static final java.time.temporal.ChronoUnit CENTURIES;
+    enum_constant public static final java.time.temporal.ChronoUnit DAYS;
+    enum_constant public static final java.time.temporal.ChronoUnit DECADES;
+    enum_constant public static final java.time.temporal.ChronoUnit ERAS;
+    enum_constant public static final java.time.temporal.ChronoUnit FOREVER;
+    enum_constant public static final java.time.temporal.ChronoUnit HALF_DAYS;
+    enum_constant public static final java.time.temporal.ChronoUnit HOURS;
+    enum_constant public static final java.time.temporal.ChronoUnit MICROS;
+    enum_constant public static final java.time.temporal.ChronoUnit MILLENNIA;
+    enum_constant public static final java.time.temporal.ChronoUnit MILLIS;
+    enum_constant public static final java.time.temporal.ChronoUnit MINUTES;
+    enum_constant public static final java.time.temporal.ChronoUnit MONTHS;
+    enum_constant public static final java.time.temporal.ChronoUnit NANOS;
+    enum_constant public static final java.time.temporal.ChronoUnit SECONDS;
+    enum_constant public static final java.time.temporal.ChronoUnit WEEKS;
+    enum_constant public static final java.time.temporal.ChronoUnit YEARS;
+  }
+
+  public final class IsoFields {
+    field public static final java.time.temporal.TemporalField DAY_OF_QUARTER;
+    field public static final java.time.temporal.TemporalField QUARTER_OF_YEAR;
+    field public static final java.time.temporal.TemporalUnit QUARTER_YEARS;
+    field public static final java.time.temporal.TemporalField WEEK_BASED_YEAR;
+    field public static final java.time.temporal.TemporalUnit WEEK_BASED_YEARS;
+    field public static final java.time.temporal.TemporalField WEEK_OF_WEEK_BASED_YEAR;
+  }
+
+  public final class JulianFields {
+    field public static final java.time.temporal.TemporalField JULIAN_DAY;
+    field public static final java.time.temporal.TemporalField MODIFIED_JULIAN_DAY;
+    field public static final java.time.temporal.TemporalField RATA_DIE;
+  }
+
+  public interface Temporal extends java.time.temporal.TemporalAccessor {
+    method public boolean isSupported(java.time.temporal.TemporalUnit);
+    method public default java.time.temporal.Temporal minus(java.time.temporal.TemporalAmount);
+    method public default java.time.temporal.Temporal minus(long, java.time.temporal.TemporalUnit);
+    method public default java.time.temporal.Temporal plus(java.time.temporal.TemporalAmount);
+    method public java.time.temporal.Temporal plus(long, java.time.temporal.TemporalUnit);
+    method public long until(java.time.temporal.Temporal, java.time.temporal.TemporalUnit);
+    method public default java.time.temporal.Temporal with(java.time.temporal.TemporalAdjuster);
+    method public java.time.temporal.Temporal with(java.time.temporal.TemporalField, long);
+  }
+
+  public interface TemporalAccessor {
+    method public default int get(java.time.temporal.TemporalField);
+    method public long getLong(java.time.temporal.TemporalField);
+    method public boolean isSupported(java.time.temporal.TemporalField);
+    method public default <R> R query(java.time.temporal.TemporalQuery<R>);
+    method public default java.time.temporal.ValueRange range(java.time.temporal.TemporalField);
+  }
+
+  @java.lang.FunctionalInterface public interface TemporalAdjuster {
+    method public java.time.temporal.Temporal adjustInto(java.time.temporal.Temporal);
+  }
+
+  public final class TemporalAdjusters {
+    method public static java.time.temporal.TemporalAdjuster dayOfWeekInMonth(int, java.time.DayOfWeek);
+    method public static java.time.temporal.TemporalAdjuster firstDayOfMonth();
+    method public static java.time.temporal.TemporalAdjuster firstDayOfNextMonth();
+    method public static java.time.temporal.TemporalAdjuster firstDayOfNextYear();
+    method public static java.time.temporal.TemporalAdjuster firstDayOfYear();
+    method public static java.time.temporal.TemporalAdjuster firstInMonth(java.time.DayOfWeek);
+    method public static java.time.temporal.TemporalAdjuster lastDayOfMonth();
+    method public static java.time.temporal.TemporalAdjuster lastDayOfYear();
+    method public static java.time.temporal.TemporalAdjuster lastInMonth(java.time.DayOfWeek);
+    method public static java.time.temporal.TemporalAdjuster next(java.time.DayOfWeek);
+    method public static java.time.temporal.TemporalAdjuster nextOrSame(java.time.DayOfWeek);
+    method public static java.time.temporal.TemporalAdjuster ofDateAdjuster(java.util.function.UnaryOperator<java.time.LocalDate>);
+    method public static java.time.temporal.TemporalAdjuster previous(java.time.DayOfWeek);
+    method public static java.time.temporal.TemporalAdjuster previousOrSame(java.time.DayOfWeek);
+  }
+
+  public interface TemporalAmount {
+    method public java.time.temporal.Temporal addTo(java.time.temporal.Temporal);
+    method public long get(java.time.temporal.TemporalUnit);
+    method public java.util.List<java.time.temporal.TemporalUnit> getUnits();
+    method public java.time.temporal.Temporal subtractFrom(java.time.temporal.Temporal);
+  }
+
+  public interface TemporalField {
+    method public <R extends java.time.temporal.Temporal> R adjustInto(R, long);
+    method public java.time.temporal.TemporalUnit getBaseUnit();
+    method public default String getDisplayName(java.util.Locale);
+    method public long getFrom(java.time.temporal.TemporalAccessor);
+    method public java.time.temporal.TemporalUnit getRangeUnit();
+    method public boolean isDateBased();
+    method public boolean isSupportedBy(java.time.temporal.TemporalAccessor);
+    method public boolean isTimeBased();
+    method public java.time.temporal.ValueRange range();
+    method public java.time.temporal.ValueRange rangeRefinedBy(java.time.temporal.TemporalAccessor);
+    method public default java.time.temporal.TemporalAccessor resolve(java.util.Map<java.time.temporal.TemporalField,java.lang.Long>, java.time.temporal.TemporalAccessor, java.time.format.ResolverStyle);
+    method public String toString();
+  }
+
+  public final class TemporalQueries {
+    method public static java.time.temporal.TemporalQuery<java.time.chrono.Chronology> chronology();
+    method public static java.time.temporal.TemporalQuery<java.time.LocalDate> localDate();
+    method public static java.time.temporal.TemporalQuery<java.time.LocalTime> localTime();
+    method public static java.time.temporal.TemporalQuery<java.time.ZoneOffset> offset();
+    method public static java.time.temporal.TemporalQuery<java.time.temporal.TemporalUnit> precision();
+    method public static java.time.temporal.TemporalQuery<java.time.ZoneId> zone();
+    method public static java.time.temporal.TemporalQuery<java.time.ZoneId> zoneId();
+  }
+
+  @java.lang.FunctionalInterface public interface TemporalQuery<R> {
+    method public R queryFrom(java.time.temporal.TemporalAccessor);
+  }
+
+  public interface TemporalUnit {
+    method public <R extends java.time.temporal.Temporal> R addTo(R, long);
+    method public long between(java.time.temporal.Temporal, java.time.temporal.Temporal);
+    method public java.time.Duration getDuration();
+    method public boolean isDateBased();
+    method public boolean isDurationEstimated();
+    method public default boolean isSupportedBy(java.time.temporal.Temporal);
+    method public boolean isTimeBased();
+    method public String toString();
+  }
+
+  public class UnsupportedTemporalTypeException extends java.time.DateTimeException {
+    ctor public UnsupportedTemporalTypeException(String);
+    ctor public UnsupportedTemporalTypeException(String, Throwable);
+  }
+
+  public final class ValueRange implements java.io.Serializable {
+    method public int checkValidIntValue(long, java.time.temporal.TemporalField);
+    method public long checkValidValue(long, java.time.temporal.TemporalField);
+    method public long getLargestMinimum();
+    method public long getMaximum();
+    method public long getMinimum();
+    method public long getSmallestMaximum();
+    method public boolean isFixed();
+    method public boolean isIntValue();
+    method public boolean isValidIntValue(long);
+    method public boolean isValidValue(long);
+    method public static java.time.temporal.ValueRange of(long, long);
+    method public static java.time.temporal.ValueRange of(long, long, long);
+    method public static java.time.temporal.ValueRange of(long, long, long, long);
+  }
+
+  public final class WeekFields implements java.io.Serializable {
+    method public java.time.temporal.TemporalField dayOfWeek();
+    method public java.time.DayOfWeek getFirstDayOfWeek();
+    method public int getMinimalDaysInFirstWeek();
+    method public static java.time.temporal.WeekFields of(java.util.Locale);
+    method public static java.time.temporal.WeekFields of(java.time.DayOfWeek, int);
+    method public java.time.temporal.TemporalField weekBasedYear();
+    method public java.time.temporal.TemporalField weekOfMonth();
+    method public java.time.temporal.TemporalField weekOfWeekBasedYear();
+    method public java.time.temporal.TemporalField weekOfYear();
+    field public static final java.time.temporal.WeekFields ISO;
+    field public static final java.time.temporal.WeekFields SUNDAY_START;
+    field public static final java.time.temporal.TemporalUnit WEEK_BASED_YEARS;
+  }
+
+}
+
+package java.time.zone {
+
+  public final class ZoneOffsetTransition implements java.lang.Comparable<java.time.zone.ZoneOffsetTransition> java.io.Serializable {
+    method public int compareTo(java.time.zone.ZoneOffsetTransition);
+    method public java.time.LocalDateTime getDateTimeAfter();
+    method public java.time.LocalDateTime getDateTimeBefore();
+    method public java.time.Duration getDuration();
+    method public java.time.Instant getInstant();
+    method public java.time.ZoneOffset getOffsetAfter();
+    method public java.time.ZoneOffset getOffsetBefore();
+    method public boolean isGap();
+    method public boolean isOverlap();
+    method public boolean isValidOffset(java.time.ZoneOffset);
+    method public static java.time.zone.ZoneOffsetTransition of(java.time.LocalDateTime, java.time.ZoneOffset, java.time.ZoneOffset);
+    method public long toEpochSecond();
+  }
+
+  public final class ZoneOffsetTransitionRule implements java.io.Serializable {
+    method public java.time.zone.ZoneOffsetTransition createTransition(int);
+    method public int getDayOfMonthIndicator();
+    method public java.time.DayOfWeek getDayOfWeek();
+    method public java.time.LocalTime getLocalTime();
+    method public java.time.Month getMonth();
+    method public java.time.ZoneOffset getOffsetAfter();
+    method public java.time.ZoneOffset getOffsetBefore();
+    method public java.time.ZoneOffset getStandardOffset();
+    method public java.time.zone.ZoneOffsetTransitionRule.TimeDefinition getTimeDefinition();
+    method public boolean isMidnightEndOfDay();
+    method public static java.time.zone.ZoneOffsetTransitionRule of(java.time.Month, int, java.time.DayOfWeek, java.time.LocalTime, boolean, java.time.zone.ZoneOffsetTransitionRule.TimeDefinition, java.time.ZoneOffset, java.time.ZoneOffset, java.time.ZoneOffset);
+  }
+
+  public enum ZoneOffsetTransitionRule.TimeDefinition {
+    method public java.time.LocalDateTime createDateTime(java.time.LocalDateTime, java.time.ZoneOffset, java.time.ZoneOffset);
+    enum_constant public static final java.time.zone.ZoneOffsetTransitionRule.TimeDefinition STANDARD;
+    enum_constant public static final java.time.zone.ZoneOffsetTransitionRule.TimeDefinition UTC;
+    enum_constant public static final java.time.zone.ZoneOffsetTransitionRule.TimeDefinition WALL;
+  }
+
+  public final class ZoneRules implements java.io.Serializable {
+    method public java.time.Duration getDaylightSavings(java.time.Instant);
+    method public java.time.ZoneOffset getOffset(java.time.Instant);
+    method public java.time.ZoneOffset getOffset(java.time.LocalDateTime);
+    method public java.time.ZoneOffset getStandardOffset(java.time.Instant);
+    method public java.time.zone.ZoneOffsetTransition getTransition(java.time.LocalDateTime);
+    method public java.util.List<java.time.zone.ZoneOffsetTransitionRule> getTransitionRules();
+    method public java.util.List<java.time.zone.ZoneOffsetTransition> getTransitions();
+    method public java.util.List<java.time.ZoneOffset> getValidOffsets(java.time.LocalDateTime);
+    method public boolean isDaylightSavings(java.time.Instant);
+    method public boolean isFixedOffset();
+    method public boolean isValidOffset(java.time.LocalDateTime, java.time.ZoneOffset);
+    method public java.time.zone.ZoneOffsetTransition nextTransition(java.time.Instant);
+    method public static java.time.zone.ZoneRules of(java.time.ZoneOffset, java.time.ZoneOffset, java.util.List<java.time.zone.ZoneOffsetTransition>, java.util.List<java.time.zone.ZoneOffsetTransition>, java.util.List<java.time.zone.ZoneOffsetTransitionRule>);
+    method public static java.time.zone.ZoneRules of(java.time.ZoneOffset);
+    method public java.time.zone.ZoneOffsetTransition previousTransition(java.time.Instant);
+  }
+
+  public class ZoneRulesException extends java.time.DateTimeException {
+    ctor public ZoneRulesException(String);
+    ctor public ZoneRulesException(String, Throwable);
+  }
+
+}
+
+package java.util {
+
+  public abstract class AbstractCollection<E> implements java.util.Collection<E> {
+    ctor protected AbstractCollection();
+    method public boolean add(E);
+    method public boolean addAll(@NonNull java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(@Nullable Object);
+    method public boolean containsAll(@NonNull java.util.Collection<?>);
+    method public boolean isEmpty();
+    method public boolean remove(@Nullable Object);
+    method public boolean removeAll(@NonNull java.util.Collection<?>);
+    method public boolean retainAll(@NonNull java.util.Collection<?>);
+    method @NonNull public Object[] toArray();
+    method @NonNull public <T> T[] toArray(@NonNull T[]);
+  }
+
+  public abstract class AbstractList<E> extends java.util.AbstractCollection<E> implements java.util.List<E> {
+    ctor protected AbstractList();
+    method public void add(int, E);
+    method public boolean addAll(int, @NonNull java.util.Collection<? extends E>);
+    method public int indexOf(@Nullable Object);
+    method @NonNull public java.util.Iterator<E> iterator();
+    method public int lastIndexOf(@Nullable Object);
+    method @NonNull public java.util.ListIterator<E> listIterator();
+    method @NonNull public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method protected void removeRange(int, int);
+    method public E set(int, E);
+    method @NonNull public java.util.List<E> subList(int, int);
+    field protected transient int modCount;
+  }
+
+  public abstract class AbstractMap<K, V> implements java.util.Map<K,V> {
+    ctor protected AbstractMap();
+    method public void clear();
+    method public boolean containsKey(@Nullable Object);
+    method public boolean containsValue(@Nullable Object);
+    method @Nullable public V get(@Nullable Object);
+    method public boolean isEmpty();
+    method @NonNull public java.util.Set<K> keySet();
+    method @Nullable public V put(K, V);
+    method public void putAll(@NonNull java.util.Map<? extends K,? extends V>);
+    method @Nullable public V remove(@Nullable Object);
+    method public int size();
+    method @NonNull public java.util.Collection<V> values();
+  }
+
+  public static class AbstractMap.SimpleEntry<K, V> implements java.util.Map.Entry<K,V> java.io.Serializable {
+    ctor public AbstractMap.SimpleEntry(K, V);
+    ctor public AbstractMap.SimpleEntry(@NonNull java.util.Map.Entry<? extends K,? extends V>);
+    method public K getKey();
+    method public V getValue();
+    method public V setValue(V);
+  }
+
+  public static class AbstractMap.SimpleImmutableEntry<K, V> implements java.util.Map.Entry<K,V> java.io.Serializable {
+    ctor public AbstractMap.SimpleImmutableEntry(K, V);
+    ctor public AbstractMap.SimpleImmutableEntry(@NonNull java.util.Map.Entry<? extends K,? extends V>);
+    method public K getKey();
+    method public V getValue();
+    method public V setValue(V);
+  }
+
+  public abstract class AbstractQueue<E> extends java.util.AbstractCollection<E> implements java.util.Queue<E> {
+    ctor protected AbstractQueue();
+    method public E element();
+    method public E remove();
+  }
+
+  public abstract class AbstractSequentialList<E> extends java.util.AbstractList<E> {
+    ctor protected AbstractSequentialList();
+    method public E get(int);
+  }
+
+  public abstract class AbstractSet<E> extends java.util.AbstractCollection<E> implements java.util.Set<E> {
+    ctor protected AbstractSet();
+  }
+
+  public class ArrayDeque<E> extends java.util.AbstractCollection<E> implements java.lang.Cloneable java.util.Deque<E> java.io.Serializable {
+    ctor public ArrayDeque();
+    ctor public ArrayDeque(int);
+    ctor public ArrayDeque(@NonNull java.util.Collection<? extends E>);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method @NonNull public java.util.ArrayDeque<E> clone();
+    method @NonNull public java.util.Iterator<E> descendingIterator();
+    method public E element();
+    method public E getFirst();
+    method public E getLast();
+    method @NonNull public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public boolean offerFirst(E);
+    method public boolean offerLast(E);
+    method @Nullable public E peek();
+    method @Nullable public E peekFirst();
+    method @Nullable public E peekLast();
+    method @Nullable public E poll();
+    method @Nullable public E pollFirst();
+    method @Nullable public E pollLast();
+    method public E pop();
+    method public void push(E);
+    method public E remove();
+    method public E removeFirst();
+    method public boolean removeFirstOccurrence(@Nullable Object);
+    method public E removeLast();
+    method public boolean removeLastOccurrence(@Nullable Object);
+    method public int size();
+  }
+
+  public class ArrayList<E> extends java.util.AbstractList<E> implements java.lang.Cloneable java.util.List<E> java.util.RandomAccess java.io.Serializable {
+    ctor public ArrayList(int);
+    ctor public ArrayList();
+    ctor public ArrayList(@NonNull java.util.Collection<? extends E>);
+    method @NonNull public Object clone();
+    method public void ensureCapacity(int);
+    method public void forEach(@NonNull java.util.function.Consumer<? super E>);
+    method public E get(int);
+    method public int size();
+    method public void trimToSize();
+  }
+
+  public class Arrays {
+    method @NonNull @java.lang.SafeVarargs public static <T> java.util.List<T> asList(@NonNull T...);
+    method public static int binarySearch(@NonNull long[], long);
+    method public static int binarySearch(@NonNull long[], int, int, long);
+    method public static int binarySearch(@NonNull int[], int);
+    method public static int binarySearch(@NonNull int[], int, int, int);
+    method public static int binarySearch(@NonNull short[], short);
+    method public static int binarySearch(@NonNull short[], int, int, short);
+    method public static int binarySearch(@NonNull char[], char);
+    method public static int binarySearch(@NonNull char[], int, int, char);
+    method public static int binarySearch(@NonNull byte[], byte);
+    method public static int binarySearch(@NonNull byte[], int, int, byte);
+    method public static int binarySearch(@NonNull double[], double);
+    method public static int binarySearch(@NonNull double[], int, int, double);
+    method public static int binarySearch(@NonNull float[], float);
+    method public static int binarySearch(@NonNull float[], int, int, float);
+    method public static int binarySearch(@NonNull Object[], @NonNull Object);
+    method public static int binarySearch(@NonNull Object[], int, int, @NonNull Object);
+    method public static <T> int binarySearch(@NonNull T[], T, @Nullable java.util.Comparator<? super T>);
+    method public static <T> int binarySearch(@NonNull T[], int, int, T, @Nullable java.util.Comparator<? super T>);
+    method @NonNull public static <T> T[] copyOf(@NonNull T[], int);
+    method @NonNull public static <T, U> T[] copyOf(@NonNull U[], int, @NonNull Class<? extends T[]>);
+    method @NonNull public static byte[] copyOf(@NonNull byte[], int);
+    method @NonNull public static short[] copyOf(@NonNull short[], int);
+    method @NonNull public static int[] copyOf(@NonNull int[], int);
+    method @NonNull public static long[] copyOf(@NonNull long[], int);
+    method @NonNull public static char[] copyOf(@NonNull char[], int);
+    method @NonNull public static float[] copyOf(@NonNull float[], int);
+    method @NonNull public static double[] copyOf(@NonNull double[], int);
+    method @NonNull public static boolean[] copyOf(@NonNull boolean[], int);
+    method @NonNull public static <T> T[] copyOfRange(@NonNull T[], int, int);
+    method @NonNull public static <T, U> T[] copyOfRange(@NonNull U[], int, int, @NonNull Class<? extends T[]>);
+    method @NonNull public static byte[] copyOfRange(@NonNull byte[], int, int);
+    method @NonNull public static short[] copyOfRange(@NonNull short[], int, int);
+    method @NonNull public static int[] copyOfRange(@NonNull int[], int, int);
+    method @NonNull public static long[] copyOfRange(@NonNull long[], int, int);
+    method @NonNull public static char[] copyOfRange(@NonNull char[], int, int);
+    method @NonNull public static float[] copyOfRange(@NonNull float[], int, int);
+    method @NonNull public static double[] copyOfRange(@NonNull double[], int, int);
+    method @NonNull public static boolean[] copyOfRange(@NonNull boolean[], int, int);
+    method public static boolean deepEquals(@Nullable Object[], @Nullable Object[]);
+    method public static int deepHashCode(@Nullable Object[]);
+    method @NonNull public static String deepToString(@Nullable Object[]);
+    method public static boolean equals(@Nullable long[], @Nullable long[]);
+    method public static boolean equals(@Nullable int[], @Nullable int[]);
+    method public static boolean equals(@Nullable short[], @Nullable short[]);
+    method public static boolean equals(@Nullable char[], @Nullable char[]);
+    method public static boolean equals(@Nullable byte[], @Nullable byte[]);
+    method public static boolean equals(@Nullable boolean[], @Nullable boolean[]);
+    method public static boolean equals(@Nullable double[], @Nullable double[]);
+    method public static boolean equals(@Nullable float[], @Nullable float[]);
+    method public static boolean equals(@Nullable Object[], @Nullable Object[]);
+    method public static void fill(@NonNull long[], long);
+    method public static void fill(@NonNull long[], int, int, long);
+    method public static void fill(@NonNull int[], int);
+    method public static void fill(@NonNull int[], int, int, int);
+    method public static void fill(@NonNull short[], short);
+    method public static void fill(@NonNull short[], int, int, short);
+    method public static void fill(@NonNull char[], char);
+    method public static void fill(@NonNull char[], int, int, char);
+    method public static void fill(@NonNull byte[], byte);
+    method public static void fill(@NonNull byte[], int, int, byte);
+    method public static void fill(@NonNull boolean[], boolean);
+    method public static void fill(@NonNull boolean[], int, int, boolean);
+    method public static void fill(@NonNull double[], double);
+    method public static void fill(@NonNull double[], int, int, double);
+    method public static void fill(@NonNull float[], float);
+    method public static void fill(@NonNull float[], int, int, float);
+    method public static void fill(@NonNull Object[], @Nullable Object);
+    method public static void fill(@NonNull Object[], int, int, @Nullable Object);
+    method public static int hashCode(@Nullable long[]);
+    method public static int hashCode(@Nullable int[]);
+    method public static int hashCode(@Nullable short[]);
+    method public static int hashCode(@Nullable char[]);
+    method public static int hashCode(@Nullable byte[]);
+    method public static int hashCode(@Nullable boolean[]);
+    method public static int hashCode(@Nullable float[]);
+    method public static int hashCode(@Nullable double[]);
+    method public static int hashCode(@Nullable Object[]);
+    method public static <T> void parallelPrefix(@NonNull T[], @NonNull java.util.function.BinaryOperator<T>);
+    method public static <T> void parallelPrefix(@NonNull T[], int, int, @NonNull java.util.function.BinaryOperator<T>);
+    method public static void parallelPrefix(@NonNull long[], @NonNull java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(@NonNull long[], int, int, @NonNull java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(@NonNull double[], @NonNull java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(@NonNull double[], int, int, @NonNull java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(@NonNull int[], @NonNull java.util.function.IntBinaryOperator);
+    method public static void parallelPrefix(@NonNull int[], int, int, @NonNull java.util.function.IntBinaryOperator);
+    method public static <T> void parallelSetAll(@NonNull T[], @NonNull java.util.function.IntFunction<? extends T>);
+    method public static void parallelSetAll(@NonNull int[], @NonNull java.util.function.IntUnaryOperator);
+    method public static void parallelSetAll(@NonNull long[], @NonNull java.util.function.IntToLongFunction);
+    method public static void parallelSetAll(@NonNull double[], @NonNull java.util.function.IntToDoubleFunction);
+    method public static void parallelSort(@NonNull byte[]);
+    method public static void parallelSort(@NonNull byte[], int, int);
+    method public static void parallelSort(@NonNull char[]);
+    method public static void parallelSort(@NonNull char[], int, int);
+    method public static void parallelSort(@NonNull short[]);
+    method public static void parallelSort(@NonNull short[], int, int);
+    method public static void parallelSort(@NonNull int[]);
+    method public static void parallelSort(@NonNull int[], int, int);
+    method public static void parallelSort(@NonNull long[]);
+    method public static void parallelSort(@NonNull long[], int, int);
+    method public static void parallelSort(@NonNull float[]);
+    method public static void parallelSort(@NonNull float[], int, int);
+    method public static void parallelSort(@NonNull double[]);
+    method public static void parallelSort(@NonNull double[], int, int);
+    method public static <T extends java.lang.Comparable<? super T>> void parallelSort(@NonNull T[]);
+    method public static <T extends java.lang.Comparable<? super T>> void parallelSort(@NonNull T[], int, int);
+    method public static <T> void parallelSort(@NonNull T[], @Nullable java.util.Comparator<? super T>);
+    method public static <T> void parallelSort(@NonNull T[], int, int, @Nullable java.util.Comparator<? super T>);
+    method public static <T> void setAll(@NonNull T[], @NonNull java.util.function.IntFunction<? extends T>);
+    method public static void setAll(@NonNull int[], @NonNull java.util.function.IntUnaryOperator);
+    method public static void setAll(@NonNull long[], @NonNull java.util.function.IntToLongFunction);
+    method public static void setAll(@NonNull double[], @NonNull java.util.function.IntToDoubleFunction);
+    method public static void sort(@NonNull int[]);
+    method public static void sort(@NonNull int[], int, int);
+    method public static void sort(@NonNull long[]);
+    method public static void sort(@NonNull long[], int, int);
+    method public static void sort(@NonNull short[]);
+    method public static void sort(@NonNull short[], int, int);
+    method public static void sort(@NonNull char[]);
+    method public static void sort(@NonNull char[], int, int);
+    method public static void sort(@NonNull byte[]);
+    method public static void sort(@NonNull byte[], int, int);
+    method public static void sort(@NonNull float[]);
+    method public static void sort(@NonNull float[], int, int);
+    method public static void sort(@NonNull double[]);
+    method public static void sort(@NonNull double[], int, int);
+    method public static void sort(@NonNull Object[]);
+    method public static void sort(@NonNull Object[], int, int);
+    method public static <T> void sort(@NonNull T[], @Nullable java.util.Comparator<? super T>);
+    method public static <T> void sort(@NonNull T[], int, int, @Nullable java.util.Comparator<? super T>);
+    method @NonNull public static <T> java.util.Spliterator<T> spliterator(@NonNull T[]);
+    method @NonNull public static <T> java.util.Spliterator<T> spliterator(@NonNull T[], int, int);
+    method @NonNull public static java.util.Spliterator.OfInt spliterator(@NonNull int[]);
+    method @NonNull public static java.util.Spliterator.OfInt spliterator(@NonNull int[], int, int);
+    method @NonNull public static java.util.Spliterator.OfLong spliterator(@NonNull long[]);
+    method @NonNull public static java.util.Spliterator.OfLong spliterator(@NonNull long[], int, int);
+    method @NonNull public static java.util.Spliterator.OfDouble spliterator(@NonNull double[]);
+    method @NonNull public static java.util.Spliterator.OfDouble spliterator(@NonNull double[], int, int);
+    method @NonNull public static <T> java.util.stream.Stream<T> stream(@NonNull T[]);
+    method @NonNull public static <T> java.util.stream.Stream<T> stream(@NonNull T[], int, int);
+    method @NonNull public static java.util.stream.IntStream stream(@NonNull int[]);
+    method @NonNull public static java.util.stream.IntStream stream(@NonNull int[], int, int);
+    method @NonNull public static java.util.stream.LongStream stream(@NonNull long[]);
+    method @NonNull public static java.util.stream.LongStream stream(@NonNull long[], int, int);
+    method @NonNull public static java.util.stream.DoubleStream stream(@NonNull double[]);
+    method @NonNull public static java.util.stream.DoubleStream stream(@NonNull double[], int, int);
+    method @NonNull public static String toString(@Nullable long[]);
+    method @NonNull public static String toString(@Nullable int[]);
+    method @NonNull public static String toString(@Nullable short[]);
+    method @NonNull public static String toString(@Nullable char[]);
+    method @NonNull public static String toString(@Nullable byte[]);
+    method @NonNull public static String toString(@Nullable boolean[]);
+    method @NonNull public static String toString(@Nullable float[]);
+    method @NonNull public static String toString(@Nullable double[]);
+    method @NonNull public static String toString(@Nullable Object[]);
+  }
+
+  public class Base64 {
+    method public static java.util.Base64.Decoder getDecoder();
+    method public static java.util.Base64.Encoder getEncoder();
+    method public static java.util.Base64.Decoder getMimeDecoder();
+    method public static java.util.Base64.Encoder getMimeEncoder();
+    method public static java.util.Base64.Encoder getMimeEncoder(int, byte[]);
+    method public static java.util.Base64.Decoder getUrlDecoder();
+    method public static java.util.Base64.Encoder getUrlEncoder();
+  }
+
+  public static class Base64.Decoder {
+    method public byte[] decode(byte[]);
+    method public byte[] decode(String);
+    method public int decode(byte[], byte[]);
+    method public java.nio.ByteBuffer decode(java.nio.ByteBuffer);
+    method public java.io.InputStream wrap(java.io.InputStream);
+  }
+
+  public static class Base64.Encoder {
+    method public byte[] encode(byte[]);
+    method public int encode(byte[], byte[]);
+    method public java.nio.ByteBuffer encode(java.nio.ByteBuffer);
+    method public String encodeToString(byte[]);
+    method public java.util.Base64.Encoder withoutPadding();
+    method public java.io.OutputStream wrap(java.io.OutputStream);
+  }
+
+  public class BitSet implements java.lang.Cloneable java.io.Serializable {
+    ctor public BitSet();
+    ctor public BitSet(int);
+    method public void and(java.util.BitSet);
+    method public void andNot(java.util.BitSet);
+    method public int cardinality();
+    method public void clear(int);
+    method public void clear(int, int);
+    method public void clear();
+    method public Object clone();
+    method public void flip(int);
+    method public void flip(int, int);
+    method public boolean get(int);
+    method public java.util.BitSet get(int, int);
+    method public boolean intersects(java.util.BitSet);
+    method public boolean isEmpty();
+    method public int length();
+    method public int nextClearBit(int);
+    method public int nextSetBit(int);
+    method public void or(java.util.BitSet);
+    method public int previousClearBit(int);
+    method public int previousSetBit(int);
+    method public void set(int);
+    method public void set(int, boolean);
+    method public void set(int, int);
+    method public void set(int, int, boolean);
+    method public int size();
+    method public java.util.stream.IntStream stream();
+    method public byte[] toByteArray();
+    method public long[] toLongArray();
+    method public static java.util.BitSet valueOf(long[]);
+    method public static java.util.BitSet valueOf(java.nio.LongBuffer);
+    method public static java.util.BitSet valueOf(byte[]);
+    method public static java.util.BitSet valueOf(java.nio.ByteBuffer);
+    method public void xor(java.util.BitSet);
+  }
+
+  public abstract class Calendar implements java.lang.Cloneable java.lang.Comparable<java.util.Calendar> java.io.Serializable {
+    ctor protected Calendar();
+    ctor protected Calendar(@NonNull java.util.TimeZone, @NonNull java.util.Locale);
+    method public abstract void add(int, int);
+    method public boolean after(@Nullable Object);
+    method public boolean before(@Nullable Object);
+    method public final void clear();
+    method public final void clear(int);
+    method @NonNull public Object clone();
+    method public int compareTo(@NonNull java.util.Calendar);
+    method protected void complete();
+    method protected abstract void computeFields();
+    method protected abstract void computeTime();
+    method public int get(int);
+    method public int getActualMaximum(int);
+    method public int getActualMinimum(int);
+    method @NonNull public static java.util.Set<java.lang.String> getAvailableCalendarTypes();
+    method @NonNull public static java.util.Locale[] getAvailableLocales();
+    method @NonNull public String getCalendarType();
+    method @Nullable public String getDisplayName(int, int, @NonNull java.util.Locale);
+    method @Nullable public java.util.Map<java.lang.String,java.lang.Integer> getDisplayNames(int, int, @NonNull java.util.Locale);
+    method public int getFirstDayOfWeek();
+    method public abstract int getGreatestMinimum(int);
+    method @NonNull public static java.util.Calendar getInstance();
+    method @NonNull public static java.util.Calendar getInstance(@NonNull java.util.TimeZone);
+    method @NonNull public static java.util.Calendar getInstance(@NonNull java.util.Locale);
+    method @NonNull public static java.util.Calendar getInstance(@NonNull java.util.TimeZone, @NonNull java.util.Locale);
+    method public abstract int getLeastMaximum(int);
+    method public abstract int getMaximum(int);
+    method public int getMinimalDaysInFirstWeek();
+    method public abstract int getMinimum(int);
+    method @NonNull public final java.util.Date getTime();
+    method public long getTimeInMillis();
+    method @NonNull public java.util.TimeZone getTimeZone();
+    method public int getWeekYear();
+    method public int getWeeksInWeekYear();
+    method protected final int internalGet(int);
+    method public boolean isLenient();
+    method public final boolean isSet(int);
+    method public boolean isWeekDateSupported();
+    method public abstract void roll(int, boolean);
+    method public void roll(int, int);
+    method public void set(int, int);
+    method public final void set(int, int, int);
+    method public final void set(int, int, int, int, int);
+    method public final void set(int, int, int, int, int, int);
+    method public void setFirstDayOfWeek(int);
+    method public void setLenient(boolean);
+    method public void setMinimalDaysInFirstWeek(int);
+    method public final void setTime(@NonNull java.util.Date);
+    method public void setTimeInMillis(long);
+    method public void setTimeZone(@NonNull java.util.TimeZone);
+    method public void setWeekDate(int, int, int);
+    method @NonNull public final java.time.Instant toInstant();
+    field public static final int ALL_STYLES = 0; // 0x0
+    field public static final int AM = 0; // 0x0
+    field public static final int AM_PM = 9; // 0x9
+    field public static final int APRIL = 3; // 0x3
+    field public static final int AUGUST = 7; // 0x7
+    field public static final int DATE = 5; // 0x5
+    field public static final int DAY_OF_MONTH = 5; // 0x5
+    field public static final int DAY_OF_WEEK = 7; // 0x7
+    field public static final int DAY_OF_WEEK_IN_MONTH = 8; // 0x8
+    field public static final int DAY_OF_YEAR = 6; // 0x6
+    field public static final int DECEMBER = 11; // 0xb
+    field public static final int DST_OFFSET = 16; // 0x10
+    field public static final int ERA = 0; // 0x0
+    field public static final int FEBRUARY = 1; // 0x1
+    field public static final int FIELD_COUNT = 17; // 0x11
+    field public static final int FRIDAY = 6; // 0x6
+    field public static final int HOUR = 10; // 0xa
+    field public static final int HOUR_OF_DAY = 11; // 0xb
+    field public static final int JANUARY = 0; // 0x0
+    field public static final int JULY = 6; // 0x6
+    field public static final int JUNE = 5; // 0x5
+    field public static final int LONG = 2; // 0x2
+    field public static final int LONG_FORMAT = 2; // 0x2
+    field public static final int LONG_STANDALONE = 32770; // 0x8002
+    field public static final int MARCH = 2; // 0x2
+    field public static final int MAY = 4; // 0x4
+    field public static final int MILLISECOND = 14; // 0xe
+    field public static final int MINUTE = 12; // 0xc
+    field public static final int MONDAY = 2; // 0x2
+    field public static final int MONTH = 2; // 0x2
+    field public static final int NARROW_FORMAT = 4; // 0x4
+    field public static final int NARROW_STANDALONE = 32772; // 0x8004
+    field public static final int NOVEMBER = 10; // 0xa
+    field public static final int OCTOBER = 9; // 0x9
+    field public static final int PM = 1; // 0x1
+    field public static final int SATURDAY = 7; // 0x7
+    field public static final int SECOND = 13; // 0xd
+    field public static final int SEPTEMBER = 8; // 0x8
+    field public static final int SHORT = 1; // 0x1
+    field public static final int SHORT_FORMAT = 1; // 0x1
+    field public static final int SHORT_STANDALONE = 32769; // 0x8001
+    field public static final int SUNDAY = 1; // 0x1
+    field public static final int THURSDAY = 5; // 0x5
+    field public static final int TUESDAY = 3; // 0x3
+    field public static final int UNDECIMBER = 12; // 0xc
+    field public static final int WEDNESDAY = 4; // 0x4
+    field public static final int WEEK_OF_MONTH = 4; // 0x4
+    field public static final int WEEK_OF_YEAR = 3; // 0x3
+    field public static final int YEAR = 1; // 0x1
+    field public static final int ZONE_OFFSET = 15; // 0xf
+    field protected boolean areFieldsSet;
+    field @NonNull protected int[] fields;
+    field @NonNull protected boolean[] isSet;
+    field protected boolean isTimeSet;
+    field protected long time;
+  }
+
+  public static class Calendar.Builder {
+    ctor public Calendar.Builder();
+    method @NonNull public java.util.Calendar build();
+    method @NonNull public java.util.Calendar.Builder set(int, int);
+    method @NonNull public java.util.Calendar.Builder setCalendarType(@NonNull String);
+    method @NonNull public java.util.Calendar.Builder setDate(int, int, int);
+    method @NonNull public java.util.Calendar.Builder setFields(@NonNull int...);
+    method @NonNull public java.util.Calendar.Builder setInstant(long);
+    method @NonNull public java.util.Calendar.Builder setInstant(@NonNull java.util.Date);
+    method @NonNull public java.util.Calendar.Builder setLenient(boolean);
+    method @NonNull public java.util.Calendar.Builder setLocale(@NonNull java.util.Locale);
+    method @NonNull public java.util.Calendar.Builder setTimeOfDay(int, int, int);
+    method @NonNull public java.util.Calendar.Builder setTimeOfDay(int, int, int, int);
+    method @NonNull public java.util.Calendar.Builder setTimeZone(@NonNull java.util.TimeZone);
+    method @NonNull public java.util.Calendar.Builder setWeekDate(int, int, int);
+    method @NonNull public java.util.Calendar.Builder setWeekDefinition(int, int);
+  }
+
+  public interface Collection<E> extends java.lang.Iterable<E> {
+    method public boolean add(E);
+    method public boolean addAll(@NonNull java.util.Collection<? extends E>);
+    method public void clear();
+    method public boolean contains(@Nullable Object);
+    method public boolean containsAll(@NonNull java.util.Collection<?>);
+    method public boolean equals(@Nullable Object);
+    method public int hashCode();
+    method public boolean isEmpty();
+    method @NonNull public java.util.Iterator<E> iterator();
+    method @NonNull public default java.util.stream.Stream<E> parallelStream();
+    method public boolean remove(@Nullable Object);
+    method public boolean removeAll(@NonNull java.util.Collection<?>);
+    method public default boolean removeIf(@NonNull java.util.function.Predicate<? super E>);
+    method public boolean retainAll(@NonNull java.util.Collection<?>);
+    method public int size();
+    method @NonNull public default java.util.Spliterator<E> spliterator();
+    method @NonNull public default java.util.stream.Stream<E> stream();
+    method @NonNull public Object[] toArray();
+    method @NonNull public <T> T[] toArray(@NonNull T[]);
+  }
+
+  public class Collections {
+    method @java.lang.SafeVarargs public static <T> boolean addAll(@NonNull java.util.Collection<? super T>, @NonNull T...);
+    method @NonNull public static <T> java.util.Queue<T> asLifoQueue(@NonNull java.util.Deque<T>);
+    method public static <T> int binarySearch(@NonNull java.util.List<? extends java.lang.Comparable<? super T>>, @NonNull T);
+    method public static <T> int binarySearch(@NonNull java.util.List<? extends T>, T, @Nullable java.util.Comparator<? super T>);
+    method @NonNull public static <E> java.util.Collection<E> checkedCollection(@NonNull java.util.Collection<E>, @NonNull Class<E>);
+    method @NonNull public static <E> java.util.List<E> checkedList(@NonNull java.util.List<E>, @NonNull Class<E>);
+    method @NonNull public static <K, V> java.util.Map<K,V> checkedMap(@NonNull java.util.Map<K,V>, @NonNull Class<K>, @NonNull Class<V>);
+    method @NonNull public static <K, V> java.util.NavigableMap<K,V> checkedNavigableMap(@NonNull java.util.NavigableMap<K,V>, @NonNull Class<K>, @NonNull Class<V>);
+    method @NonNull public static <E> java.util.NavigableSet<E> checkedNavigableSet(@NonNull java.util.NavigableSet<E>, @NonNull Class<E>);
+    method @NonNull public static <E> java.util.Queue<E> checkedQueue(@NonNull java.util.Queue<E>, @NonNull Class<E>);
+    method @NonNull public static <E> java.util.Set<E> checkedSet(@NonNull java.util.Set<E>, @NonNull Class<E>);
+    method @NonNull public static <K, V> java.util.SortedMap<K,V> checkedSortedMap(@NonNull java.util.SortedMap<K,V>, @NonNull Class<K>, @NonNull Class<V>);
+    method @NonNull public static <E> java.util.SortedSet<E> checkedSortedSet(@NonNull java.util.SortedSet<E>, @NonNull Class<E>);
+    method public static <T> void copy(@NonNull java.util.List<? super T>, @NonNull java.util.List<? extends T>);
+    method public static boolean disjoint(@NonNull java.util.Collection<?>, @NonNull java.util.Collection<?>);
+    method @NonNull public static <T> java.util.Enumeration<T> emptyEnumeration();
+    method @NonNull public static <T> java.util.Iterator<T> emptyIterator();
+    method @NonNull public static final <T> java.util.List<T> emptyList();
+    method @NonNull public static <T> java.util.ListIterator<T> emptyListIterator();
+    method @NonNull public static final <K, V> java.util.Map<K,V> emptyMap();
+    method @NonNull public static final <K, V> java.util.NavigableMap<K,V> emptyNavigableMap();
+    method @NonNull public static <E> java.util.NavigableSet<E> emptyNavigableSet();
+    method @NonNull public static final <T> java.util.Set<T> emptySet();
+    method @NonNull public static final <K, V> java.util.SortedMap<K,V> emptySortedMap();
+    method @NonNull public static <E> java.util.SortedSet<E> emptySortedSet();
+    method @NonNull public static <T> java.util.Enumeration<T> enumeration(@NonNull java.util.Collection<T>);
+    method public static <T> void fill(@NonNull java.util.List<? super T>, T);
+    method public static int frequency(@NonNull java.util.Collection<?>, @Nullable Object);
+    method public static int indexOfSubList(@NonNull java.util.List<?>, @NonNull java.util.List<?>);
+    method public static int lastIndexOfSubList(@NonNull java.util.List<?>, @NonNull java.util.List<?>);
+    method @NonNull public static <T> java.util.ArrayList<T> list(@NonNull java.util.Enumeration<T>);
+    method @NonNull public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T max(@NonNull java.util.Collection<? extends T>);
+    method public static <T> T max(@NonNull java.util.Collection<? extends T>, @Nullable java.util.Comparator<? super T>);
+    method @NonNull public static <T extends java.lang.Object & java.lang.Comparable<? super T>> T min(@NonNull java.util.Collection<? extends T>);
+    method public static <T> T min(@NonNull java.util.Collection<? extends T>, @Nullable java.util.Comparator<? super T>);
+    method @NonNull public static <T> java.util.List<T> nCopies(int, T);
+    method @NonNull public static <E> java.util.Set<E> newSetFromMap(@NonNull java.util.Map<E,java.lang.Boolean>);
+    method public static <T> boolean replaceAll(@NonNull java.util.List<T>, T, T);
+    method public static void reverse(@NonNull java.util.List<?>);
+    method @NonNull public static <T> java.util.Comparator<T> reverseOrder();
+    method @NonNull public static <T> java.util.Comparator<T> reverseOrder(@Nullable java.util.Comparator<T>);
+    method public static void rotate(@NonNull java.util.List<?>, int);
+    method public static void shuffle(@NonNull java.util.List<?>);
+    method public static void shuffle(@NonNull java.util.List<?>, @NonNull java.util.Random);
+    method @NonNull public static <T> java.util.Set<T> singleton(T);
+    method @NonNull public static <T> java.util.List<T> singletonList(T);
+    method @NonNull public static <K, V> java.util.Map<K,V> singletonMap(K, V);
+    method public static <T extends java.lang.Comparable<? super T>> void sort(@NonNull java.util.List<T>);
+    method public static <T> void sort(@NonNull java.util.List<T>, @Nullable java.util.Comparator<? super T>);
+    method public static void swap(@NonNull java.util.List<?>, int, int);
+    method @NonNull public static <T> java.util.Collection<T> synchronizedCollection(@NonNull java.util.Collection<T>);
+    method @NonNull public static <T> java.util.List<T> synchronizedList(@NonNull java.util.List<T>);
+    method @NonNull public static <K, V> java.util.Map<K,V> synchronizedMap(@NonNull java.util.Map<K,V>);
+    method @NonNull public static <K, V> java.util.NavigableMap<K,V> synchronizedNavigableMap(@NonNull java.util.NavigableMap<K,V>);
+    method @NonNull public static <T> java.util.NavigableSet<T> synchronizedNavigableSet(@NonNull java.util.NavigableSet<T>);
+    method @NonNull public static <T> java.util.Set<T> synchronizedSet(@NonNull java.util.Set<T>);
+    method @NonNull public static <K, V> java.util.SortedMap<K,V> synchronizedSortedMap(@NonNull java.util.SortedMap<K,V>);
+    method @NonNull public static <T> java.util.SortedSet<T> synchronizedSortedSet(@NonNull java.util.SortedSet<T>);
+    method @NonNull public static <T> java.util.Collection<T> unmodifiableCollection(@NonNull java.util.Collection<? extends T>);
+    method @NonNull public static <T> java.util.List<T> unmodifiableList(@NonNull java.util.List<? extends T>);
+    method @NonNull public static <K, V> java.util.Map<K,V> unmodifiableMap(@NonNull java.util.Map<? extends K,? extends V>);
+    method @NonNull public static <K, V> java.util.NavigableMap<K,V> unmodifiableNavigableMap(@NonNull java.util.NavigableMap<K,? extends V>);
+    method @NonNull public static <T> java.util.NavigableSet<T> unmodifiableNavigableSet(@NonNull java.util.NavigableSet<T>);
+    method @NonNull public static <T> java.util.Set<T> unmodifiableSet(@NonNull java.util.Set<? extends T>);
+    method @NonNull public static <K, V> java.util.SortedMap<K,V> unmodifiableSortedMap(@NonNull java.util.SortedMap<K,? extends V>);
+    method @NonNull public static <T> java.util.SortedSet<T> unmodifiableSortedSet(@NonNull java.util.SortedSet<T>);
+    field @NonNull public static final java.util.List EMPTY_LIST;
+    field @NonNull public static final java.util.Map EMPTY_MAP;
+    field @NonNull public static final java.util.Set EMPTY_SET;
+  }
+
+  @java.lang.FunctionalInterface public interface Comparator<T> {
+    method public int compare(T, T);
+    method public static <T, U> java.util.Comparator<T> comparing(java.util.function.Function<? super T,? extends U>, java.util.Comparator<? super U>);
+    method public static <T, U extends java.lang.Comparable<? super U>> java.util.Comparator<T> comparing(java.util.function.Function<? super T,? extends U>);
+    method public static <T> java.util.Comparator<T> comparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static <T> java.util.Comparator<T> comparingInt(java.util.function.ToIntFunction<? super T>);
+    method public static <T> java.util.Comparator<T> comparingLong(java.util.function.ToLongFunction<? super T>);
+    method public boolean equals(Object);
+    method public static <T extends java.lang.Comparable<? super T>> java.util.Comparator<T> naturalOrder();
+    method public static <T> java.util.Comparator<T> nullsFirst(java.util.Comparator<? super T>);
+    method public static <T> java.util.Comparator<T> nullsLast(java.util.Comparator<? super T>);
+    method public static <T extends java.lang.Comparable<? super T>> java.util.Comparator<T> reverseOrder();
+    method public default java.util.Comparator<T> reversed();
+    method public default java.util.Comparator<T> thenComparing(java.util.Comparator<? super T>);
+    method public default <U> java.util.Comparator<T> thenComparing(java.util.function.Function<? super T,? extends U>, java.util.Comparator<? super U>);
+    method public default <U extends java.lang.Comparable<? super U>> java.util.Comparator<T> thenComparing(java.util.function.Function<? super T,? extends U>);
+    method public default java.util.Comparator<T> thenComparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingInt(java.util.function.ToIntFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingLong(java.util.function.ToLongFunction<? super T>);
+  }
+
+  public class ConcurrentModificationException extends java.lang.RuntimeException {
+    ctor public ConcurrentModificationException();
+    ctor public ConcurrentModificationException(String);
+    ctor public ConcurrentModificationException(Throwable);
+    ctor public ConcurrentModificationException(String, Throwable);
+  }
+
+  public final class Currency implements java.io.Serializable {
+    method public static java.util.Set<java.util.Currency> getAvailableCurrencies();
+    method public String getCurrencyCode();
+    method public int getDefaultFractionDigits();
+    method public String getDisplayName();
+    method public String getDisplayName(java.util.Locale);
+    method public static java.util.Currency getInstance(String);
+    method public static java.util.Currency getInstance(java.util.Locale);
+    method public int getNumericCode();
+    method public String getSymbol();
+    method public String getSymbol(java.util.Locale);
+  }
+
+  public class Date implements java.lang.Cloneable java.lang.Comparable<java.util.Date> java.io.Serializable {
+    ctor public Date();
+    ctor public Date(long);
+    ctor @Deprecated public Date(int, int, int);
+    ctor @Deprecated public Date(int, int, int, int, int);
+    ctor @Deprecated public Date(int, int, int, int, int, int);
+    ctor @Deprecated public Date(String);
+    method @Deprecated public static long UTC(int, int, int, int, int, int);
+    method public boolean after(java.util.Date);
+    method public boolean before(java.util.Date);
+    method public Object clone();
+    method public int compareTo(java.util.Date);
+    method public static java.util.Date from(java.time.Instant);
+    method @Deprecated public int getDate();
+    method @Deprecated public int getDay();
+    method @Deprecated public int getHours();
+    method @Deprecated public int getMinutes();
+    method @Deprecated public int getMonth();
+    method @Deprecated public int getSeconds();
+    method public long getTime();
+    method @Deprecated public int getTimezoneOffset();
+    method @Deprecated public int getYear();
+    method @Deprecated public static long parse(String);
+    method @Deprecated public void setDate(int);
+    method @Deprecated public void setHours(int);
+    method @Deprecated public void setMinutes(int);
+    method @Deprecated public void setMonth(int);
+    method @Deprecated public void setSeconds(int);
+    method public void setTime(long);
+    method @Deprecated public void setYear(int);
+    method @Deprecated public String toGMTString();
+    method public java.time.Instant toInstant();
+    method @Deprecated public String toLocaleString();
+  }
+
+  public interface Deque<E> extends java.util.Queue<E> {
+    method public void addFirst(E);
+    method public void addLast(E);
+    method @NonNull public java.util.Iterator<E> descendingIterator();
+    method public E getFirst();
+    method public E getLast();
+    method public boolean offerFirst(E);
+    method public boolean offerLast(E);
+    method @Nullable public E peekFirst();
+    method @Nullable public E peekLast();
+    method @Nullable public E pollFirst();
+    method @Nullable public E pollLast();
+    method public E pop();
+    method public void push(E);
+    method public E removeFirst();
+    method public boolean removeFirstOccurrence(@Nullable Object);
+    method public E removeLast();
+    method public boolean removeLastOccurrence(@Nullable Object);
+  }
+
+  public abstract class Dictionary<K, V> {
+    ctor public Dictionary();
+    method public abstract java.util.Enumeration<V> elements();
+    method public abstract V get(Object);
+    method public abstract boolean isEmpty();
+    method public abstract java.util.Enumeration<K> keys();
+    method public abstract V put(K, V);
+    method public abstract V remove(Object);
+    method public abstract int size();
+  }
+
+  public class DoubleSummaryStatistics implements java.util.function.DoubleConsumer {
+    ctor public DoubleSummaryStatistics();
+    method public void accept(double);
+    method public void combine(java.util.DoubleSummaryStatistics);
+    method public final double getAverage();
+    method public final long getCount();
+    method public final double getMax();
+    method public final double getMin();
+    method public final double getSum();
+  }
+
+  public class DuplicateFormatFlagsException extends java.util.IllegalFormatException {
+    ctor public DuplicateFormatFlagsException(String);
+    method public String getFlags();
+  }
+
+  public class EmptyStackException extends java.lang.RuntimeException {
+    ctor public EmptyStackException();
+  }
+
+  public class EnumMap<K extends java.lang.Enum<K>, V> extends java.util.AbstractMap<K,V> implements java.lang.Cloneable java.io.Serializable {
+    ctor public EnumMap(Class<K>);
+    ctor public EnumMap(java.util.EnumMap<K,? extends V>);
+    ctor public EnumMap(java.util.Map<K,? extends V>);
+    method public java.util.EnumMap<K,V> clone();
+    method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+  }
+
+  public abstract class EnumSet<E extends java.lang.Enum<E>> extends java.util.AbstractSet<E> implements java.lang.Cloneable java.io.Serializable {
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> allOf(Class<E>);
+    method public java.util.EnumSet<E> clone();
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> complementOf(java.util.EnumSet<E>);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> copyOf(java.util.EnumSet<E>);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> copyOf(java.util.Collection<E>);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> noneOf(Class<E>);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> of(E);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> of(E, E);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> of(E, E, E);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> of(E, E, E, E);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> of(E, E, E, E, E);
+    method @java.lang.SafeVarargs public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> of(E, E...);
+    method public static <E extends java.lang.Enum<E>> java.util.EnumSet<E> range(E, E);
+  }
+
+  public interface Enumeration<E> {
+    method public boolean hasMoreElements();
+    method public E nextElement();
+  }
+
+  public interface EventListener {
+  }
+
+  public abstract class EventListenerProxy<T extends java.util.EventListener> implements java.util.EventListener {
+    ctor public EventListenerProxy(T);
+    method public T getListener();
+  }
+
+  public class EventObject implements java.io.Serializable {
+    ctor public EventObject(Object);
+    method public Object getSource();
+    field protected transient Object source;
+  }
+
+  public class FormatFlagsConversionMismatchException extends java.util.IllegalFormatException {
+    ctor public FormatFlagsConversionMismatchException(String, char);
+    method public char getConversion();
+    method public String getFlags();
+  }
+
+  public interface Formattable {
+    method public void formatTo(java.util.Formatter, int, int, int);
+  }
+
+  public class FormattableFlags {
+    field public static final int ALTERNATE = 4; // 0x4
+    field public static final int LEFT_JUSTIFY = 1; // 0x1
+    field public static final int UPPERCASE = 2; // 0x2
+  }
+
+  public final class Formatter implements java.io.Closeable java.io.Flushable {
+    ctor public Formatter();
+    ctor public Formatter(Appendable);
+    ctor public Formatter(java.util.Locale);
+    ctor public Formatter(Appendable, java.util.Locale);
+    ctor public Formatter(String) throws java.io.FileNotFoundException;
+    ctor public Formatter(String, String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    ctor public Formatter(String, String, java.util.Locale) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    ctor public Formatter(java.io.File) throws java.io.FileNotFoundException;
+    ctor public Formatter(java.io.File, String) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    ctor public Formatter(java.io.File, String, java.util.Locale) throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException;
+    ctor public Formatter(java.io.PrintStream);
+    ctor public Formatter(java.io.OutputStream);
+    ctor public Formatter(java.io.OutputStream, String) throws java.io.UnsupportedEncodingException;
+    ctor public Formatter(java.io.OutputStream, String, java.util.Locale) throws java.io.UnsupportedEncodingException;
+    method public void close();
+    method public void flush();
+    method public java.util.Formatter format(String, java.lang.Object...);
+    method public java.util.Formatter format(java.util.Locale, String, java.lang.Object...);
+    method public java.io.IOException ioException();
+    method public java.util.Locale locale();
+    method public Appendable out();
+  }
+
+  public enum Formatter.BigDecimalLayoutForm {
+    enum_constant public static final java.util.Formatter.BigDecimalLayoutForm DECIMAL_FLOAT;
+    enum_constant public static final java.util.Formatter.BigDecimalLayoutForm SCIENTIFIC;
+  }
+
+  public class FormatterClosedException extends java.lang.IllegalStateException {
+    ctor public FormatterClosedException();
+  }
+
+  public class GregorianCalendar extends java.util.Calendar {
+    ctor public GregorianCalendar();
+    ctor public GregorianCalendar(java.util.TimeZone);
+    ctor public GregorianCalendar(java.util.Locale);
+    ctor public GregorianCalendar(java.util.TimeZone, java.util.Locale);
+    ctor public GregorianCalendar(int, int, int);
+    ctor public GregorianCalendar(int, int, int, int, int);
+    ctor public GregorianCalendar(int, int, int, int, int, int);
+    method public void add(int, int);
+    method protected void computeFields();
+    method protected void computeTime();
+    method public static java.util.GregorianCalendar from(java.time.ZonedDateTime);
+    method public int getGreatestMinimum(int);
+    method public final java.util.Date getGregorianChange();
+    method public int getLeastMaximum(int);
+    method public int getMaximum(int);
+    method public int getMinimum(int);
+    method public boolean isLeapYear(int);
+    method public final boolean isWeekDateSupported();
+    method public void roll(int, boolean);
+    method public void setGregorianChange(java.util.Date);
+    method public java.time.ZonedDateTime toZonedDateTime();
+    field public static final int AD = 1; // 0x1
+    field public static final int BC = 0; // 0x0
+  }
+
+  public class HashMap<K, V> extends java.util.AbstractMap<K,V> implements java.lang.Cloneable java.util.Map<K,V> java.io.Serializable {
+    ctor public HashMap(int, float);
+    ctor public HashMap(int);
+    ctor public HashMap();
+    ctor public HashMap(@NonNull java.util.Map<? extends K,? extends V>);
+    method @NonNull public Object clone();
+    method @NonNull public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+  }
+
+  public class HashSet<E> extends java.util.AbstractSet<E> implements java.lang.Cloneable java.io.Serializable java.util.Set<E> {
+    ctor public HashSet();
+    ctor public HashSet(@NonNull java.util.Collection<? extends E>);
+    ctor public HashSet(int, float);
+    ctor public HashSet(int);
+    method @NonNull public Object clone();
+    method @NonNull public java.util.Iterator<E> iterator();
+    method public int size();
+  }
+
+  public class Hashtable<K, V> extends java.util.Dictionary<K,V> implements java.lang.Cloneable java.util.Map<K,V> java.io.Serializable {
+    ctor public Hashtable(int, float);
+    ctor public Hashtable(int);
+    ctor public Hashtable();
+    ctor public Hashtable(java.util.Map<? extends K,? extends V>);
+    method public void clear();
+    method public Object clone();
+    method public boolean contains(Object);
+    method public boolean containsKey(Object);
+    method public boolean containsValue(Object);
+    method public java.util.Enumeration<V> elements();
+    method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+    method public V get(Object);
+    method public boolean isEmpty();
+    method public java.util.Set<K> keySet();
+    method public java.util.Enumeration<K> keys();
+    method public V put(K, V);
+    method public void putAll(java.util.Map<? extends K,? extends V>);
+    method protected void rehash();
+    method public V remove(Object);
+    method public int size();
+    method public java.util.Collection<V> values();
+  }
+
+  public class IdentityHashMap<K, V> extends java.util.AbstractMap<K,V> implements java.lang.Cloneable java.util.Map<K,V> java.io.Serializable {
+    ctor public IdentityHashMap();
+    ctor public IdentityHashMap(int);
+    ctor public IdentityHashMap(java.util.Map<? extends K,? extends V>);
+    method public Object clone();
+    method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+  }
+
+  public class IllegalFormatCodePointException extends java.util.IllegalFormatException {
+    ctor public IllegalFormatCodePointException(int);
+    method public int getCodePoint();
+  }
+
+  public class IllegalFormatConversionException extends java.util.IllegalFormatException {
+    ctor public IllegalFormatConversionException(char, Class<?>);
+    method public Class<?> getArgumentClass();
+    method public char getConversion();
+  }
+
+  public class IllegalFormatException extends java.lang.IllegalArgumentException {
+  }
+
+  public class IllegalFormatFlagsException extends java.util.IllegalFormatException {
+    ctor public IllegalFormatFlagsException(String);
+    method public String getFlags();
+  }
+
+  public class IllegalFormatPrecisionException extends java.util.IllegalFormatException {
+    ctor public IllegalFormatPrecisionException(int);
+    method public int getPrecision();
+  }
+
+  public class IllegalFormatWidthException extends java.util.IllegalFormatException {
+    ctor public IllegalFormatWidthException(int);
+    method public int getWidth();
+  }
+
+  public class IllformedLocaleException extends java.lang.RuntimeException {
+    ctor public IllformedLocaleException();
+    ctor public IllformedLocaleException(String);
+    ctor public IllformedLocaleException(String, int);
+    method public int getErrorIndex();
+  }
+
+  public class InputMismatchException extends java.util.NoSuchElementException {
+    ctor public InputMismatchException();
+    ctor public InputMismatchException(String);
+  }
+
+  public class IntSummaryStatistics implements java.util.function.IntConsumer {
+    ctor public IntSummaryStatistics();
+    method public void accept(int);
+    method public void combine(java.util.IntSummaryStatistics);
+    method public final double getAverage();
+    method public final long getCount();
+    method public final int getMax();
+    method public final int getMin();
+    method public final long getSum();
+  }
+
+  public class InvalidPropertiesFormatException extends java.io.IOException {
+    ctor public InvalidPropertiesFormatException(Throwable);
+    ctor public InvalidPropertiesFormatException(String);
+  }
+
+  public interface Iterator<E> {
+    method public default void forEachRemaining(@NonNull java.util.function.Consumer<? super E>);
+    method public boolean hasNext();
+    method public E next();
+    method public default void remove();
+  }
+
+  public class LinkedHashMap<K, V> extends java.util.HashMap<K,V> implements java.util.Map<K,V> {
+    ctor public LinkedHashMap(int, float);
+    ctor public LinkedHashMap(int);
+    ctor public LinkedHashMap();
+    ctor public LinkedHashMap(java.util.Map<? extends K,? extends V>);
+    ctor public LinkedHashMap(int, float, boolean);
+    method protected boolean removeEldestEntry(java.util.Map.Entry<K,V>);
+  }
+
+  public class LinkedHashSet<E> extends java.util.HashSet<E> implements java.lang.Cloneable java.io.Serializable java.util.Set<E> {
+    ctor public LinkedHashSet(int, float);
+    ctor public LinkedHashSet(int);
+    ctor public LinkedHashSet();
+    ctor public LinkedHashSet(java.util.Collection<? extends E>);
+  }
+
+  public class LinkedList<E> extends java.util.AbstractSequentialList<E> implements java.lang.Cloneable java.util.Deque<E> java.util.List<E> java.io.Serializable {
+    ctor public LinkedList();
+    ctor public LinkedList(@NonNull java.util.Collection<? extends E>);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method @NonNull public Object clone();
+    method @NonNull public java.util.Iterator<E> descendingIterator();
+    method public E element();
+    method public E getFirst();
+    method public E getLast();
+    method public boolean offer(E);
+    method public boolean offerFirst(E);
+    method public boolean offerLast(E);
+    method @Nullable public E peek();
+    method @Nullable public E peekFirst();
+    method @Nullable public E peekLast();
+    method @Nullable public E poll();
+    method @Nullable public E pollFirst();
+    method @Nullable public E pollLast();
+    method public E pop();
+    method public void push(E);
+    method public E remove();
+    method public E removeFirst();
+    method public boolean removeFirstOccurrence(@Nullable Object);
+    method public E removeLast();
+    method public boolean removeLastOccurrence(@Nullable Object);
+    method public int size();
+  }
+
+  public interface List<E> extends java.util.Collection<E> {
+    method public void add(int, E);
+    method public boolean addAll(int, @NonNull java.util.Collection<? extends E>);
+    method @NonNull public static <E> java.util.List<E> copyOf(@NonNull java.util.Collection<? extends E>);
+    method public E get(int);
+    method public int indexOf(@Nullable Object);
+    method public int lastIndexOf(@Nullable Object);
+    method @NonNull public java.util.ListIterator<E> listIterator();
+    method @NonNull public java.util.ListIterator<E> listIterator(int);
+    method @NonNull public static <E> java.util.List<E> of();
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.List<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull @java.lang.SafeVarargs public static <E> java.util.List<E> of(@NonNull E...);
+    method public E remove(int);
+    method public default void replaceAll(@NonNull java.util.function.UnaryOperator<E>);
+    method public E set(int, E);
+    method public default void sort(@Nullable java.util.Comparator<? super E>);
+    method @NonNull public java.util.List<E> subList(int, int);
+  }
+
+  public interface ListIterator<E> extends java.util.Iterator<E> {
+    method public void add(E);
+    method public boolean hasPrevious();
+    method public int nextIndex();
+    method public E previous();
+    method public int previousIndex();
+    method public void remove();
+    method public void set(E);
+  }
+
+  public abstract class ListResourceBundle extends java.util.ResourceBundle {
+    ctor public ListResourceBundle();
+    method protected abstract Object[][] getContents();
+    method public java.util.Enumeration<java.lang.String> getKeys();
+    method public final Object handleGetObject(String);
+  }
+
+  public final class Locale implements java.lang.Cloneable java.io.Serializable {
+    ctor public Locale(@NonNull String, @NonNull String, @NonNull String);
+    ctor public Locale(@NonNull String, @NonNull String);
+    ctor public Locale(@NonNull String);
+    method @NonNull public Object clone();
+    method @NonNull public static java.util.List<java.util.Locale> filter(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.util.Locale>, @NonNull java.util.Locale.FilteringMode);
+    method @NonNull public static java.util.List<java.util.Locale> filter(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.util.Locale>);
+    method @NonNull public static java.util.List<java.lang.String> filterTags(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.lang.String>, @NonNull java.util.Locale.FilteringMode);
+    method @NonNull public static java.util.List<java.lang.String> filterTags(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public static java.util.Locale forLanguageTag(@NonNull String);
+    method @NonNull public static java.util.Locale[] getAvailableLocales();
+    method @NonNull public String getCountry();
+    method @NonNull public static java.util.Locale getDefault();
+    method @NonNull public static java.util.Locale getDefault(@NonNull java.util.Locale.Category);
+    method @NonNull public String getDisplayCountry();
+    method @NonNull public String getDisplayCountry(@NonNull java.util.Locale);
+    method @NonNull public String getDisplayLanguage();
+    method @NonNull public String getDisplayLanguage(@NonNull java.util.Locale);
+    method @NonNull public String getDisplayName();
+    method @NonNull public String getDisplayName(@NonNull java.util.Locale);
+    method @NonNull public String getDisplayScript();
+    method @NonNull public String getDisplayScript(@NonNull java.util.Locale);
+    method @NonNull public String getDisplayVariant();
+    method @NonNull public String getDisplayVariant(@NonNull java.util.Locale);
+    method @Nullable public String getExtension(char);
+    method @NonNull public java.util.Set<java.lang.Character> getExtensionKeys();
+    method @NonNull public String getISO3Country() throws java.util.MissingResourceException;
+    method @NonNull public String getISO3Language() throws java.util.MissingResourceException;
+    method @NonNull public static String[] getISOCountries();
+    method @NonNull public static String[] getISOLanguages();
+    method @NonNull public String getLanguage();
+    method @NonNull public String getScript();
+    method @NonNull public java.util.Set<java.lang.String> getUnicodeLocaleAttributes();
+    method @NonNull public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
+    method @Nullable public String getUnicodeLocaleType(@NonNull String);
+    method @NonNull public String getVariant();
+    method public boolean hasExtensions();
+    method @Nullable public static java.util.Locale lookup(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.util.Locale>);
+    method @Nullable public static String lookupTag(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Collection<java.lang.String>);
+    method public static void setDefault(@NonNull java.util.Locale);
+    method public static void setDefault(@NonNull java.util.Locale.Category, @NonNull java.util.Locale);
+    method @NonNull public java.util.Locale stripExtensions();
+    method @NonNull public String toLanguageTag();
+    field @NonNull public static final java.util.Locale CANADA;
+    field @NonNull public static final java.util.Locale CANADA_FRENCH;
+    field @NonNull public static final java.util.Locale CHINA;
+    field @NonNull public static final java.util.Locale CHINESE;
+    field @NonNull public static final java.util.Locale ENGLISH;
+    field @NonNull public static final java.util.Locale FRANCE;
+    field @NonNull public static final java.util.Locale FRENCH;
+    field @NonNull public static final java.util.Locale GERMAN;
+    field @NonNull public static final java.util.Locale GERMANY;
+    field @NonNull public static final java.util.Locale ITALIAN;
+    field @NonNull public static final java.util.Locale ITALY;
+    field @NonNull public static final java.util.Locale JAPAN;
+    field @NonNull public static final java.util.Locale JAPANESE;
+    field @NonNull public static final java.util.Locale KOREA;
+    field @NonNull public static final java.util.Locale KOREAN;
+    field @NonNull public static final java.util.Locale PRC;
+    field public static final char PRIVATE_USE_EXTENSION = 120; // 0x0078 'x'
+    field @NonNull public static final java.util.Locale ROOT;
+    field @NonNull public static final java.util.Locale SIMPLIFIED_CHINESE;
+    field @NonNull public static final java.util.Locale TAIWAN;
+    field @NonNull public static final java.util.Locale TRADITIONAL_CHINESE;
+    field @NonNull public static final java.util.Locale UK;
+    field public static final char UNICODE_LOCALE_EXTENSION = 117; // 0x0075 'u'
+    field @NonNull public static final java.util.Locale US;
+  }
+
+  public static final class Locale.Builder {
+    ctor public Locale.Builder();
+    method @NonNull public java.util.Locale.Builder addUnicodeLocaleAttribute(@NonNull String);
+    method @NonNull public java.util.Locale build();
+    method @NonNull public java.util.Locale.Builder clear();
+    method @NonNull public java.util.Locale.Builder clearExtensions();
+    method @NonNull public java.util.Locale.Builder removeUnicodeLocaleAttribute(@NonNull String);
+    method @NonNull public java.util.Locale.Builder setExtension(char, @Nullable String);
+    method @NonNull public java.util.Locale.Builder setLanguage(@Nullable String);
+    method @NonNull public java.util.Locale.Builder setLanguageTag(@NonNull String);
+    method @NonNull public java.util.Locale.Builder setLocale(@NonNull java.util.Locale);
+    method @NonNull public java.util.Locale.Builder setRegion(@Nullable String);
+    method @NonNull public java.util.Locale.Builder setScript(@Nullable String);
+    method @NonNull public java.util.Locale.Builder setUnicodeLocaleKeyword(@NonNull String, @Nullable String);
+    method @NonNull public java.util.Locale.Builder setVariant(@Nullable String);
+  }
+
+  public enum Locale.Category {
+    enum_constant public static final java.util.Locale.Category DISPLAY;
+    enum_constant public static final java.util.Locale.Category FORMAT;
+  }
+
+  public enum Locale.FilteringMode {
+    enum_constant public static final java.util.Locale.FilteringMode AUTOSELECT_FILTERING;
+    enum_constant public static final java.util.Locale.FilteringMode EXTENDED_FILTERING;
+    enum_constant public static final java.util.Locale.FilteringMode IGNORE_EXTENDED_RANGES;
+    enum_constant public static final java.util.Locale.FilteringMode MAP_EXTENDED_RANGES;
+    enum_constant public static final java.util.Locale.FilteringMode REJECT_EXTENDED_RANGES;
+  }
+
+  public static final class Locale.LanguageRange {
+    ctor public Locale.LanguageRange(@NonNull String);
+    ctor public Locale.LanguageRange(@NonNull String, double);
+    method @NonNull public String getRange();
+    method public double getWeight();
+    method @NonNull public static java.util.List<java.util.Locale.LanguageRange> mapEquivalents(@NonNull java.util.List<java.util.Locale.LanguageRange>, @NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>);
+    method @NonNull public static java.util.List<java.util.Locale.LanguageRange> parse(@NonNull String);
+    method @NonNull public static java.util.List<java.util.Locale.LanguageRange> parse(@NonNull String, @NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>);
+    field public static final double MAX_WEIGHT = 1.0;
+    field public static final double MIN_WEIGHT = 0.0;
+  }
+
+  public class LongSummaryStatistics implements java.util.function.IntConsumer java.util.function.LongConsumer {
+    ctor public LongSummaryStatistics();
+    method public void accept(int);
+    method public void accept(long);
+    method public void combine(java.util.LongSummaryStatistics);
+    method public final double getAverage();
+    method public final long getCount();
+    method public final long getMax();
+    method public final long getMin();
+    method public final long getSum();
+  }
+
+  public interface Map<K, V> {
+    method public void clear();
+    method @Nullable public default V compute(K, @NonNull java.util.function.BiFunction<? super K,? super V,? extends V>);
+    method @Nullable public default V computeIfAbsent(K, @NonNull java.util.function.Function<? super K,? extends V>);
+    method @Nullable public default V computeIfPresent(K, @NonNull java.util.function.BiFunction<? super K,? super V,? extends V>);
+    method public boolean containsKey(@Nullable Object);
+    method public boolean containsValue(@Nullable Object);
+    method @NonNull public static <K, V> java.util.Map<K,V> copyOf(@NonNull java.util.Map<? extends K,? extends V>);
+    method @NonNull public static <K, V> java.util.Map.Entry<K,V> entry(@NonNull K, @NonNull V);
+    method @NonNull public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+    method public boolean equals(@Nullable Object);
+    method public default void forEach(@NonNull java.util.function.BiConsumer<? super K,? super V>);
+    method @Nullable public V get(@Nullable Object);
+    method @Nullable public default V getOrDefault(@Nullable Object, @Nullable V);
+    method public int hashCode();
+    method public boolean isEmpty();
+    method @NonNull public java.util.Set<K> keySet();
+    method @Nullable public default V merge(K, @NonNull V, @NonNull java.util.function.BiFunction<? super V,? super V,? extends V>);
+    method @NonNull public static <K, V> java.util.Map<K,V> of();
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull public static <K, V> java.util.Map<K,V> of(@NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V, @NonNull K, @NonNull V);
+    method @NonNull @java.lang.SafeVarargs public static <K, V> java.util.Map<K,V> ofEntries(@NonNull java.util.Map.Entry<? extends K,? extends V>...);
+    method @Nullable public V put(K, V);
+    method public void putAll(@NonNull java.util.Map<? extends K,? extends V>);
+    method @Nullable public default V putIfAbsent(K, V);
+    method @Nullable public V remove(@Nullable Object);
+    method public default boolean remove(@Nullable Object, @Nullable Object);
+    method public default boolean replace(K, @Nullable V, V);
+    method @Nullable public default V replace(K, V);
+    method public default void replaceAll(@NonNull java.util.function.BiFunction<? super K,? super V,? extends V>);
+    method public int size();
+    method @NonNull public java.util.Collection<V> values();
+  }
+
+  public static interface Map.Entry<K, V> {
+    method @NonNull public static <K extends java.lang.Comparable<? super K>, V> java.util.Comparator<java.util.Map.Entry<K,V>> comparingByKey();
+    method @NonNull public static <K, V> java.util.Comparator<java.util.Map.Entry<K,V>> comparingByKey(@NonNull java.util.Comparator<? super K>);
+    method @NonNull public static <K, V extends java.lang.Comparable<? super V>> java.util.Comparator<java.util.Map.Entry<K,V>> comparingByValue();
+    method @NonNull public static <K, V> java.util.Comparator<java.util.Map.Entry<K,V>> comparingByValue(@NonNull java.util.Comparator<? super V>);
+    method public boolean equals(@Nullable Object);
+    method public K getKey();
+    method public V getValue();
+    method public int hashCode();
+    method public V setValue(V);
+  }
+
+  public class MissingFormatArgumentException extends java.util.IllegalFormatException {
+    ctor public MissingFormatArgumentException(String);
+    method public String getFormatSpecifier();
+  }
+
+  public class MissingFormatWidthException extends java.util.IllegalFormatException {
+    ctor public MissingFormatWidthException(String);
+    method public String getFormatSpecifier();
+  }
+
+  public class MissingResourceException extends java.lang.RuntimeException {
+    ctor public MissingResourceException(String, String, String);
+    method public String getClassName();
+    method public String getKey();
+  }
+
+  public interface NavigableMap<K, V> extends java.util.SortedMap<K,V> {
+    method @Nullable public java.util.Map.Entry<K,V> ceilingEntry(K);
+    method @Nullable public K ceilingKey(K);
+    method @NonNull public java.util.NavigableSet<K> descendingKeySet();
+    method @NonNull public java.util.NavigableMap<K,V> descendingMap();
+    method @Nullable public java.util.Map.Entry<K,V> firstEntry();
+    method @Nullable public java.util.Map.Entry<K,V> floorEntry(K);
+    method @Nullable public K floorKey(K);
+    method @NonNull public java.util.NavigableMap<K,V> headMap(K, boolean);
+    method @Nullable public java.util.Map.Entry<K,V> higherEntry(K);
+    method @Nullable public K higherKey(K);
+    method @Nullable public java.util.Map.Entry<K,V> lastEntry();
+    method @Nullable public java.util.Map.Entry<K,V> lowerEntry(K);
+    method @Nullable public K lowerKey(K);
+    method @NonNull public java.util.NavigableSet<K> navigableKeySet();
+    method @Nullable public java.util.Map.Entry<K,V> pollFirstEntry();
+    method @Nullable public java.util.Map.Entry<K,V> pollLastEntry();
+    method @NonNull public java.util.NavigableMap<K,V> subMap(K, boolean, K, boolean);
+    method @NonNull public java.util.NavigableMap<K,V> tailMap(K, boolean);
+  }
+
+  public interface NavigableSet<E> extends java.util.SortedSet<E> {
+    method public E ceiling(E);
+    method public java.util.Iterator<E> descendingIterator();
+    method public java.util.NavigableSet<E> descendingSet();
+    method public E floor(E);
+    method public java.util.NavigableSet<E> headSet(E, boolean);
+    method public E higher(E);
+    method public E lower(E);
+    method public E pollFirst();
+    method public E pollLast();
+    method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
+    method public java.util.NavigableSet<E> tailSet(E, boolean);
+  }
+
+  public class NoSuchElementException extends java.lang.RuntimeException {
+    ctor public NoSuchElementException();
+    ctor public NoSuchElementException(String);
+  }
+
+  public final class Objects {
+    method public static int checkFromIndexSize(int, int, int);
+    method public static int checkFromToIndex(int, int, int);
+    method public static int checkIndex(int, int);
+    method public static <T> int compare(T, T, @NonNull java.util.Comparator<? super T>);
+    method public static boolean deepEquals(@Nullable Object, @Nullable Object);
+    method public static boolean equals(@Nullable Object, @Nullable Object);
+    method public static int hash(@Nullable java.lang.Object...);
+    method public static int hashCode(@Nullable Object);
+    method public static boolean isNull(@Nullable Object);
+    method public static boolean nonNull(@Nullable Object);
+    method @NonNull public static <T> T requireNonNull(@Nullable T);
+    method @NonNull public static <T> T requireNonNull(@Nullable T, @NonNull String);
+    method @NonNull public static <T> T requireNonNull(@Nullable T, @NonNull java.util.function.Supplier<java.lang.String>);
+    method @NonNull public static <T> T requireNonNullElse(@Nullable T, @NonNull T);
+    method @NonNull public static <T> T requireNonNullElseGet(@Nullable T, @NonNull java.util.function.Supplier<? extends T>);
+    method @NonNull public static String toString(@Nullable Object);
+    method @NonNull public static String toString(@Nullable Object, @NonNull String);
+  }
+
+  public class Observable {
+    ctor public Observable();
+    method public void addObserver(java.util.Observer);
+    method protected void clearChanged();
+    method public int countObservers();
+    method public void deleteObserver(java.util.Observer);
+    method public void deleteObservers();
+    method public boolean hasChanged();
+    method public void notifyObservers();
+    method public void notifyObservers(Object);
+    method protected void setChanged();
+  }
+
+  public interface Observer {
+    method public void update(java.util.Observable, Object);
+  }
+
+  public final class Optional<T> {
+    method public static <T> java.util.Optional<T> empty();
+    method public java.util.Optional<T> filter(java.util.function.Predicate<? super T>);
+    method public <U> java.util.Optional<U> flatMap(java.util.function.Function<? super T,java.util.Optional<U>>);
+    method public T get();
+    method public void ifPresent(java.util.function.Consumer<? super T>);
+    method public boolean isPresent();
+    method public <U> java.util.Optional<U> map(java.util.function.Function<? super T,? extends U>);
+    method public static <T> java.util.Optional<T> of(T);
+    method public static <T> java.util.Optional<T> ofNullable(T);
+    method public T orElse(T);
+    method public T orElseGet(java.util.function.Supplier<? extends T>);
+    method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws X;
+  }
+
+  public final class OptionalDouble {
+    method public static java.util.OptionalDouble empty();
+    method public double getAsDouble();
+    method public void ifPresent(java.util.function.DoubleConsumer);
+    method public boolean isPresent();
+    method public static java.util.OptionalDouble of(double);
+    method public double orElse(double);
+    method public double orElseGet(java.util.function.DoubleSupplier);
+    method public <X extends java.lang.Throwable> double orElseThrow(java.util.function.Supplier<X>) throws X;
+  }
+
+  public final class OptionalInt {
+    method public static java.util.OptionalInt empty();
+    method public int getAsInt();
+    method public void ifPresent(java.util.function.IntConsumer);
+    method public boolean isPresent();
+    method public static java.util.OptionalInt of(int);
+    method public int orElse(int);
+    method public int orElseGet(java.util.function.IntSupplier);
+    method public <X extends java.lang.Throwable> int orElseThrow(java.util.function.Supplier<X>) throws X;
+  }
+
+  public final class OptionalLong {
+    method public static java.util.OptionalLong empty();
+    method public long getAsLong();
+    method public void ifPresent(java.util.function.LongConsumer);
+    method public boolean isPresent();
+    method public static java.util.OptionalLong of(long);
+    method public long orElse(long);
+    method public long orElseGet(java.util.function.LongSupplier);
+    method public <X extends java.lang.Throwable> long orElseThrow(java.util.function.Supplier<X>) throws X;
+  }
+
+  public interface PrimitiveIterator<T, T_CONS> extends java.util.Iterator<T> {
+    method public void forEachRemaining(T_CONS);
+  }
+
+  public static interface PrimitiveIterator.OfDouble extends java.util.PrimitiveIterator<java.lang.Double,java.util.function.DoubleConsumer> {
+    method public default void forEachRemaining(java.util.function.DoubleConsumer);
+    method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+    method public default Double next();
+    method public double nextDouble();
+  }
+
+  public static interface PrimitiveIterator.OfInt extends java.util.PrimitiveIterator<java.lang.Integer,java.util.function.IntConsumer> {
+    method public default void forEachRemaining(java.util.function.IntConsumer);
+    method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+    method public default Integer next();
+    method public int nextInt();
+  }
+
+  public static interface PrimitiveIterator.OfLong extends java.util.PrimitiveIterator<java.lang.Long,java.util.function.LongConsumer> {
+    method public default void forEachRemaining(java.util.function.LongConsumer);
+    method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+    method public default Long next();
+    method public long nextLong();
+  }
+
+  public class PriorityQueue<E> extends java.util.AbstractQueue<E> implements java.io.Serializable {
+    ctor public PriorityQueue();
+    ctor public PriorityQueue(int);
+    ctor public PriorityQueue(java.util.Comparator<? super E>);
+    ctor public PriorityQueue(int, java.util.Comparator<? super E>);
+    ctor public PriorityQueue(java.util.Collection<? extends E>);
+    ctor public PriorityQueue(java.util.PriorityQueue<? extends E>);
+    ctor public PriorityQueue(java.util.SortedSet<? extends E>);
+    method public java.util.Comparator<? super E> comparator();
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public E peek();
+    method public E poll();
+    method public int size();
+    method public final java.util.Spliterator<E> spliterator();
+  }
+
+  public class Properties extends java.util.Hashtable<java.lang.Object,java.lang.Object> {
+    ctor public Properties();
+    ctor public Properties(java.util.Properties);
+    method public String getProperty(String);
+    method public String getProperty(String, String);
+    method public void list(java.io.PrintStream);
+    method public void list(java.io.PrintWriter);
+    method public void load(java.io.Reader) throws java.io.IOException;
+    method public void load(java.io.InputStream) throws java.io.IOException;
+    method public void loadFromXML(java.io.InputStream) throws java.io.IOException, java.util.InvalidPropertiesFormatException;
+    method public java.util.Enumeration<?> propertyNames();
+    method @Deprecated public void save(java.io.OutputStream, String);
+    method public Object setProperty(String, String);
+    method public void store(java.io.Writer, String) throws java.io.IOException;
+    method public void store(java.io.OutputStream, String) throws java.io.IOException;
+    method public void storeToXML(java.io.OutputStream, String) throws java.io.IOException;
+    method public void storeToXML(java.io.OutputStream, String, String) throws java.io.IOException;
+    method public java.util.Set<java.lang.String> stringPropertyNames();
+    field protected java.util.Properties defaults;
+  }
+
+  public final class PropertyPermission extends java.security.BasicPermission {
+    ctor public PropertyPermission(String, String);
+  }
+
+  public class PropertyResourceBundle extends java.util.ResourceBundle {
+    ctor public PropertyResourceBundle(java.io.InputStream) throws java.io.IOException;
+    ctor public PropertyResourceBundle(java.io.Reader) throws java.io.IOException;
+    method public java.util.Enumeration<java.lang.String> getKeys();
+    method public Object handleGetObject(String);
+  }
+
+  public interface Queue<E> extends java.util.Collection<E> {
+    method public E element();
+    method public boolean offer(E);
+    method @Nullable public E peek();
+    method @Nullable public E poll();
+    method public E remove();
+  }
+
+  public class Random implements java.io.Serializable {
+    ctor public Random();
+    ctor public Random(long);
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
+    method protected int next(int);
+    method public boolean nextBoolean();
+    method public void nextBytes(byte[]);
+    method public double nextDouble();
+    method public float nextFloat();
+    method public double nextGaussian();
+    method public int nextInt();
+    method public int nextInt(int);
+    method public long nextLong();
+    method public void setSeed(long);
+  }
+
+  public interface RandomAccess {
+  }
+
+  public abstract class ResourceBundle {
+    ctor public ResourceBundle();
+    method public static final void clearCache();
+    method public static final void clearCache(ClassLoader);
+    method public boolean containsKey(String);
+    method public String getBaseBundleName();
+    method public static final java.util.ResourceBundle getBundle(String);
+    method public static final java.util.ResourceBundle getBundle(String, java.util.ResourceBundle.Control);
+    method public static final java.util.ResourceBundle getBundle(String, java.util.Locale);
+    method public static final java.util.ResourceBundle getBundle(String, java.util.Locale, java.util.ResourceBundle.Control);
+    method public static java.util.ResourceBundle getBundle(String, java.util.Locale, ClassLoader);
+    method public static java.util.ResourceBundle getBundle(String, java.util.Locale, ClassLoader, java.util.ResourceBundle.Control);
+    method public abstract java.util.Enumeration<java.lang.String> getKeys();
+    method public java.util.Locale getLocale();
+    method public final Object getObject(String);
+    method public final String getString(String);
+    method public final String[] getStringArray(String);
+    method protected abstract Object handleGetObject(String);
+    method protected java.util.Set<java.lang.String> handleKeySet();
+    method public java.util.Set<java.lang.String> keySet();
+    method protected void setParent(java.util.ResourceBundle);
+    field protected java.util.ResourceBundle parent;
+  }
+
+  public static class ResourceBundle.Control {
+    ctor protected ResourceBundle.Control();
+    method public java.util.List<java.util.Locale> getCandidateLocales(String, java.util.Locale);
+    method public static final java.util.ResourceBundle.Control getControl(java.util.List<java.lang.String>);
+    method public java.util.Locale getFallbackLocale(String, java.util.Locale);
+    method public java.util.List<java.lang.String> getFormats(String);
+    method public static final java.util.ResourceBundle.Control getNoFallbackControl(java.util.List<java.lang.String>);
+    method public long getTimeToLive(String, java.util.Locale);
+    method public boolean needsReload(String, java.util.Locale, String, ClassLoader, java.util.ResourceBundle, long);
+    method public java.util.ResourceBundle newBundle(String, java.util.Locale, String, ClassLoader, boolean) throws java.io.IOException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public String toBundleName(String, java.util.Locale);
+    method public final String toResourceName(String, String);
+    field public static final java.util.List<java.lang.String> FORMAT_CLASS;
+    field public static final java.util.List<java.lang.String> FORMAT_DEFAULT;
+    field public static final java.util.List<java.lang.String> FORMAT_PROPERTIES;
+    field public static final long TTL_DONT_CACHE = -1L; // 0xffffffffffffffffL
+    field public static final long TTL_NO_EXPIRATION_CONTROL = -2L; // 0xfffffffffffffffeL
+  }
+
+  public final class Scanner implements java.io.Closeable java.util.Iterator<java.lang.String> {
+    ctor public Scanner(Readable);
+    ctor public Scanner(java.io.InputStream);
+    ctor public Scanner(java.io.InputStream, String);
+    ctor public Scanner(java.io.File) throws java.io.FileNotFoundException;
+    ctor public Scanner(java.io.File, String) throws java.io.FileNotFoundException;
+    ctor public Scanner(java.nio.file.Path) throws java.io.IOException;
+    ctor public Scanner(java.nio.file.Path, String) throws java.io.IOException;
+    ctor public Scanner(String);
+    ctor public Scanner(java.nio.channels.ReadableByteChannel);
+    ctor public Scanner(java.nio.channels.ReadableByteChannel, String);
+    method public void close();
+    method public java.util.regex.Pattern delimiter();
+    method public String findInLine(String);
+    method public String findInLine(java.util.regex.Pattern);
+    method public String findWithinHorizon(String, int);
+    method public String findWithinHorizon(java.util.regex.Pattern, int);
+    method public boolean hasNext();
+    method public boolean hasNext(String);
+    method public boolean hasNext(java.util.regex.Pattern);
+    method public boolean hasNextBigDecimal();
+    method public boolean hasNextBigInteger();
+    method public boolean hasNextBigInteger(int);
+    method public boolean hasNextBoolean();
+    method public boolean hasNextByte();
+    method public boolean hasNextByte(int);
+    method public boolean hasNextDouble();
+    method public boolean hasNextFloat();
+    method public boolean hasNextInt();
+    method public boolean hasNextInt(int);
+    method public boolean hasNextLine();
+    method public boolean hasNextLong();
+    method public boolean hasNextLong(int);
+    method public boolean hasNextShort();
+    method public boolean hasNextShort(int);
+    method public java.io.IOException ioException();
+    method public java.util.Locale locale();
+    method public java.util.regex.MatchResult match();
+    method public String next();
+    method public String next(String);
+    method public String next(java.util.regex.Pattern);
+    method public java.math.BigDecimal nextBigDecimal();
+    method public java.math.BigInteger nextBigInteger();
+    method public java.math.BigInteger nextBigInteger(int);
+    method public boolean nextBoolean();
+    method public byte nextByte();
+    method public byte nextByte(int);
+    method public double nextDouble();
+    method public float nextFloat();
+    method public int nextInt();
+    method public int nextInt(int);
+    method public String nextLine();
+    method public long nextLong();
+    method public long nextLong(int);
+    method public short nextShort();
+    method public short nextShort(int);
+    method public int radix();
+    method public java.util.Scanner reset();
+    method public java.util.Scanner skip(java.util.regex.Pattern);
+    method public java.util.Scanner skip(String);
+    method public java.util.Scanner useDelimiter(java.util.regex.Pattern);
+    method public java.util.Scanner useDelimiter(String);
+    method public java.util.Scanner useLocale(java.util.Locale);
+    method public java.util.Scanner useRadix(int);
+  }
+
+  public class ServiceConfigurationError extends java.lang.Error {
+    ctor public ServiceConfigurationError(String);
+    ctor public ServiceConfigurationError(String, Throwable);
+  }
+
+  public final class ServiceLoader<S> implements java.lang.Iterable<S> {
+    method public java.util.Iterator<S> iterator();
+    method public static <S> java.util.ServiceLoader<S> load(Class<S>, ClassLoader);
+    method public static <S> java.util.ServiceLoader<S> load(Class<S>);
+    method public static <S> java.util.ServiceLoader<S> loadInstalled(Class<S>);
+    method public void reload();
+  }
+
+  public interface Set<E> extends java.util.Collection<E> {
+    method @NonNull public static <E> java.util.Set<E> copyOf(@NonNull java.util.Collection<? extends E>);
+    method @NonNull public static <E> java.util.Set<E> of();
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull public static <E> java.util.Set<E> of(@NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E, @NonNull E);
+    method @NonNull @java.lang.SafeVarargs public static <E> java.util.Set<E> of(@NonNull E...);
+  }
+
+  public class SimpleTimeZone extends java.util.TimeZone {
+    ctor public SimpleTimeZone(int, String);
+    ctor public SimpleTimeZone(int, String, int, int, int, int, int, int, int, int);
+    ctor public SimpleTimeZone(int, String, int, int, int, int, int, int, int, int, int);
+    ctor public SimpleTimeZone(int, String, int, int, int, int, int, int, int, int, int, int, int);
+    method public int getOffset(int, int, int, int, int, int);
+    method public int getRawOffset();
+    method public boolean inDaylightTime(java.util.Date);
+    method public void setDSTSavings(int);
+    method public void setEndRule(int, int, int, int);
+    method public void setEndRule(int, int, int);
+    method public void setEndRule(int, int, int, int, boolean);
+    method public void setRawOffset(int);
+    method public void setStartRule(int, int, int, int);
+    method public void setStartRule(int, int, int);
+    method public void setStartRule(int, int, int, int, boolean);
+    method public void setStartYear(int);
+    method public boolean useDaylightTime();
+    field public static final int STANDARD_TIME = 1; // 0x1
+    field public static final int UTC_TIME = 2; // 0x2
+    field public static final int WALL_TIME = 0; // 0x0
+  }
+
+  public interface SortedMap<K, V> extends java.util.Map<K,V> {
+    method @Nullable public java.util.Comparator<? super K> comparator();
+    method public K firstKey();
+    method @NonNull public java.util.SortedMap<K,V> headMap(K);
+    method public K lastKey();
+    method @NonNull public java.util.SortedMap<K,V> subMap(K, K);
+    method @NonNull public java.util.SortedMap<K,V> tailMap(K);
+  }
+
+  public interface SortedSet<E> extends java.util.Set<E> {
+    method public java.util.Comparator<? super E> comparator();
+    method public E first();
+    method public java.util.SortedSet<E> headSet(E);
+    method public E last();
+    method public java.util.SortedSet<E> subSet(E, E);
+    method public java.util.SortedSet<E> tailSet(E);
+  }
+
+  public interface Spliterator<T> {
+    method public int characteristics();
+    method public long estimateSize();
+    method public default void forEachRemaining(java.util.function.Consumer<? super T>);
+    method public default java.util.Comparator<? super T> getComparator();
+    method public default long getExactSizeIfKnown();
+    method public default boolean hasCharacteristics(int);
+    method public boolean tryAdvance(java.util.function.Consumer<? super T>);
+    method public java.util.Spliterator<T> trySplit();
+    field public static final int CONCURRENT = 4096; // 0x1000
+    field public static final int DISTINCT = 1; // 0x1
+    field public static final int IMMUTABLE = 1024; // 0x400
+    field public static final int NONNULL = 256; // 0x100
+    field public static final int ORDERED = 16; // 0x10
+    field public static final int SIZED = 64; // 0x40
+    field public static final int SORTED = 4; // 0x4
+    field public static final int SUBSIZED = 16384; // 0x4000
+  }
+
+  public static interface Spliterator.OfDouble extends java.util.Spliterator.OfPrimitive<java.lang.Double,java.util.function.DoubleConsumer,java.util.Spliterator.OfDouble> {
+    method public default void forEachRemaining(java.util.function.DoubleConsumer);
+    method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+    method public boolean tryAdvance(java.util.function.DoubleConsumer);
+    method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Double>);
+    method public java.util.Spliterator.OfDouble trySplit();
+  }
+
+  public static interface Spliterator.OfInt extends java.util.Spliterator.OfPrimitive<java.lang.Integer,java.util.function.IntConsumer,java.util.Spliterator.OfInt> {
+    method public default void forEachRemaining(java.util.function.IntConsumer);
+    method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+    method public boolean tryAdvance(java.util.function.IntConsumer);
+    method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Integer>);
+    method public java.util.Spliterator.OfInt trySplit();
+  }
+
+  public static interface Spliterator.OfLong extends java.util.Spliterator.OfPrimitive<java.lang.Long,java.util.function.LongConsumer,java.util.Spliterator.OfLong> {
+    method public default void forEachRemaining(java.util.function.LongConsumer);
+    method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+    method public boolean tryAdvance(java.util.function.LongConsumer);
+    method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Long>);
+    method public java.util.Spliterator.OfLong trySplit();
+  }
+
+  public static interface Spliterator.OfPrimitive<T, T_CONS, T_SPLITR extends java.util.Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>> extends java.util.Spliterator<T> {
+    method public default void forEachRemaining(T_CONS);
+    method public boolean tryAdvance(T_CONS);
+    method public T_SPLITR trySplit();
+  }
+
+  public final class Spliterators {
+    method public static java.util.Spliterator.OfDouble emptyDoubleSpliterator();
+    method public static java.util.Spliterator.OfInt emptyIntSpliterator();
+    method public static java.util.Spliterator.OfLong emptyLongSpliterator();
+    method public static <T> java.util.Spliterator<T> emptySpliterator();
+    method public static <T> java.util.Iterator<T> iterator(java.util.Spliterator<? extends T>);
+    method public static java.util.PrimitiveIterator.OfInt iterator(java.util.Spliterator.OfInt);
+    method public static java.util.PrimitiveIterator.OfLong iterator(java.util.Spliterator.OfLong);
+    method public static java.util.PrimitiveIterator.OfDouble iterator(java.util.Spliterator.OfDouble);
+    method public static <T> java.util.Spliterator<T> spliterator(Object[], int);
+    method public static <T> java.util.Spliterator<T> spliterator(Object[], int, int, int);
+    method public static java.util.Spliterator.OfInt spliterator(int[], int);
+    method public static java.util.Spliterator.OfInt spliterator(int[], int, int, int);
+    method public static java.util.Spliterator.OfLong spliterator(long[], int);
+    method public static java.util.Spliterator.OfLong spliterator(long[], int, int, int);
+    method public static java.util.Spliterator.OfDouble spliterator(double[], int);
+    method public static java.util.Spliterator.OfDouble spliterator(double[], int, int, int);
+    method public static <T> java.util.Spliterator<T> spliterator(java.util.Collection<? extends T>, int);
+    method public static <T> java.util.Spliterator<T> spliterator(java.util.Iterator<? extends T>, long, int);
+    method public static java.util.Spliterator.OfInt spliterator(java.util.PrimitiveIterator.OfInt, long, int);
+    method public static java.util.Spliterator.OfLong spliterator(java.util.PrimitiveIterator.OfLong, long, int);
+    method public static java.util.Spliterator.OfDouble spliterator(java.util.PrimitiveIterator.OfDouble, long, int);
+    method public static <T> java.util.Spliterator<T> spliteratorUnknownSize(java.util.Iterator<? extends T>, int);
+    method public static java.util.Spliterator.OfInt spliteratorUnknownSize(java.util.PrimitiveIterator.OfInt, int);
+    method public static java.util.Spliterator.OfLong spliteratorUnknownSize(java.util.PrimitiveIterator.OfLong, int);
+    method public static java.util.Spliterator.OfDouble spliteratorUnknownSize(java.util.PrimitiveIterator.OfDouble, int);
+  }
+
+  public abstract static class Spliterators.AbstractDoubleSpliterator implements java.util.Spliterator.OfDouble {
+    ctor protected Spliterators.AbstractDoubleSpliterator(long, int);
+    method public int characteristics();
+    method public long estimateSize();
+    method public java.util.Spliterator.OfDouble trySplit();
+  }
+
+  public abstract static class Spliterators.AbstractIntSpliterator implements java.util.Spliterator.OfInt {
+    ctor protected Spliterators.AbstractIntSpliterator(long, int);
+    method public int characteristics();
+    method public long estimateSize();
+    method public java.util.Spliterator.OfInt trySplit();
+  }
+
+  public abstract static class Spliterators.AbstractLongSpliterator implements java.util.Spliterator.OfLong {
+    ctor protected Spliterators.AbstractLongSpliterator(long, int);
+    method public int characteristics();
+    method public long estimateSize();
+    method public java.util.Spliterator.OfLong trySplit();
+  }
+
+  public abstract static class Spliterators.AbstractSpliterator<T> implements java.util.Spliterator<T> {
+    ctor protected Spliterators.AbstractSpliterator(long, int);
+    method public int characteristics();
+    method public long estimateSize();
+    method public java.util.Spliterator<T> trySplit();
+  }
+
+  public final class SplittableRandom {
+    ctor public SplittableRandom(long);
+    ctor public SplittableRandom();
+    method public java.util.stream.DoubleStream doubles(long);
+    method public java.util.stream.DoubleStream doubles();
+    method public java.util.stream.DoubleStream doubles(long, double, double);
+    method public java.util.stream.DoubleStream doubles(double, double);
+    method public java.util.stream.IntStream ints(long);
+    method public java.util.stream.IntStream ints();
+    method public java.util.stream.IntStream ints(long, int, int);
+    method public java.util.stream.IntStream ints(int, int);
+    method public java.util.stream.LongStream longs(long);
+    method public java.util.stream.LongStream longs();
+    method public java.util.stream.LongStream longs(long, long, long);
+    method public java.util.stream.LongStream longs(long, long);
+    method public boolean nextBoolean();
+    method public double nextDouble();
+    method public double nextDouble(double);
+    method public double nextDouble(double, double);
+    method public int nextInt();
+    method public int nextInt(int);
+    method public int nextInt(int, int);
+    method public long nextLong();
+    method public long nextLong(long);
+    method public long nextLong(long, long);
+    method public java.util.SplittableRandom split();
+  }
+
+  public class Stack<E> extends java.util.Vector<E> {
+    ctor public Stack();
+    method public boolean empty();
+    method public E peek();
+    method public E pop();
+    method public E push(E);
+    method public int search(Object);
+  }
+
+  public final class StringJoiner {
+    ctor public StringJoiner(CharSequence);
+    ctor public StringJoiner(CharSequence, CharSequence, CharSequence);
+    method public java.util.StringJoiner add(CharSequence);
+    method public int length();
+    method public java.util.StringJoiner merge(java.util.StringJoiner);
+    method public java.util.StringJoiner setEmptyValue(CharSequence);
+  }
+
+  public class StringTokenizer implements java.util.Enumeration<java.lang.Object> {
+    ctor public StringTokenizer(String, String, boolean);
+    ctor public StringTokenizer(String, String);
+    ctor public StringTokenizer(String);
+    method public int countTokens();
+    method public boolean hasMoreElements();
+    method public boolean hasMoreTokens();
+    method public Object nextElement();
+    method public String nextToken();
+    method public String nextToken(String);
+  }
+
+  public abstract class TimeZone implements java.lang.Cloneable java.io.Serializable {
+    ctor public TimeZone();
+    method public Object clone();
+    method public static String[] getAvailableIDs(int);
+    method public static String[] getAvailableIDs();
+    method public int getDSTSavings();
+    method public static java.util.TimeZone getDefault();
+    method public final String getDisplayName();
+    method public final String getDisplayName(java.util.Locale);
+    method public final String getDisplayName(boolean, int);
+    method public String getDisplayName(boolean, int, java.util.Locale);
+    method public String getID();
+    method public abstract int getOffset(int, int, int, int, int, int);
+    method public int getOffset(long);
+    method public abstract int getRawOffset();
+    method public static java.util.TimeZone getTimeZone(String);
+    method public static java.util.TimeZone getTimeZone(java.time.ZoneId);
+    method public boolean hasSameRules(java.util.TimeZone);
+    method public abstract boolean inDaylightTime(java.util.Date);
+    method public boolean observesDaylightTime();
+    method public static void setDefault(java.util.TimeZone);
+    method public void setID(String);
+    method public abstract void setRawOffset(int);
+    method public java.time.ZoneId toZoneId();
+    method public abstract boolean useDaylightTime();
+    field public static final int LONG = 1; // 0x1
+    field public static final int SHORT = 0; // 0x0
+  }
+
+  public class Timer {
+    ctor public Timer();
+    ctor public Timer(boolean);
+    ctor public Timer(String);
+    ctor public Timer(String, boolean);
+    method public void cancel();
+    method public int purge();
+    method public void schedule(java.util.TimerTask, long);
+    method public void schedule(java.util.TimerTask, java.util.Date);
+    method public void schedule(java.util.TimerTask, long, long);
+    method public void schedule(java.util.TimerTask, java.util.Date, long);
+    method public void scheduleAtFixedRate(java.util.TimerTask, long, long);
+    method public void scheduleAtFixedRate(java.util.TimerTask, java.util.Date, long);
+  }
+
+  public abstract class TimerTask implements java.lang.Runnable {
+    ctor protected TimerTask();
+    method public boolean cancel();
+    method public long scheduledExecutionTime();
+  }
+
+  public class TooManyListenersException extends java.lang.Exception {
+    ctor public TooManyListenersException();
+    ctor public TooManyListenersException(String);
+  }
+
+  public class TreeMap<K, V> extends java.util.AbstractMap<K,V> implements java.lang.Cloneable java.util.NavigableMap<K,V> java.io.Serializable {
+    ctor public TreeMap();
+    ctor public TreeMap(@Nullable java.util.Comparator<? super K>);
+    ctor public TreeMap(@NonNull java.util.Map<? extends K,? extends V>);
+    ctor public TreeMap(@NonNull java.util.SortedMap<K,? extends V>);
+    method @Nullable public java.util.Map.Entry<K,V> ceilingEntry(K);
+    method @Nullable public K ceilingKey(K);
+    method @NonNull public Object clone();
+    method @Nullable public java.util.Comparator<? super K> comparator();
+    method @NonNull public java.util.NavigableSet<K> descendingKeySet();
+    method @NonNull public java.util.NavigableMap<K,V> descendingMap();
+    method @NonNull public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+    method @Nullable public java.util.Map.Entry<K,V> firstEntry();
+    method public K firstKey();
+    method @Nullable public java.util.Map.Entry<K,V> floorEntry(K);
+    method @Nullable public K floorKey(K);
+    method @NonNull public java.util.NavigableMap<K,V> headMap(K, boolean);
+    method @NonNull public java.util.SortedMap<K,V> headMap(K);
+    method @Nullable public java.util.Map.Entry<K,V> higherEntry(K);
+    method @Nullable public K higherKey(K);
+    method @Nullable public java.util.Map.Entry<K,V> lastEntry();
+    method public K lastKey();
+    method @Nullable public java.util.Map.Entry<K,V> lowerEntry(K);
+    method @Nullable public K lowerKey(K);
+    method @NonNull public java.util.NavigableSet<K> navigableKeySet();
+    method @Nullable public java.util.Map.Entry<K,V> pollFirstEntry();
+    method @Nullable public java.util.Map.Entry<K,V> pollLastEntry();
+    method @NonNull public java.util.NavigableMap<K,V> subMap(K, boolean, K, boolean);
+    method @NonNull public java.util.SortedMap<K,V> subMap(K, K);
+    method @NonNull public java.util.NavigableMap<K,V> tailMap(K, boolean);
+    method @NonNull public java.util.SortedMap<K,V> tailMap(K);
+  }
+
+  public class TreeSet<E> extends java.util.AbstractSet<E> implements java.lang.Cloneable java.util.NavigableSet<E> java.io.Serializable {
+    ctor public TreeSet();
+    ctor public TreeSet(java.util.Comparator<? super E>);
+    ctor public TreeSet(java.util.Collection<? extends E>);
+    ctor public TreeSet(java.util.SortedSet<E>);
+    method public E ceiling(E);
+    method public Object clone();
+    method public java.util.Comparator<? super E> comparator();
+    method public java.util.Iterator<E> descendingIterator();
+    method public java.util.NavigableSet<E> descendingSet();
+    method public E first();
+    method public E floor(E);
+    method public java.util.NavigableSet<E> headSet(E, boolean);
+    method public java.util.SortedSet<E> headSet(E);
+    method public E higher(E);
+    method public java.util.Iterator<E> iterator();
+    method public E last();
+    method public E lower(E);
+    method public E pollFirst();
+    method public E pollLast();
+    method public int size();
+    method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
+    method public java.util.SortedSet<E> subSet(E, E);
+    method public java.util.NavigableSet<E> tailSet(E, boolean);
+    method public java.util.SortedSet<E> tailSet(E);
+  }
+
+  public final class UUID implements java.lang.Comparable<java.util.UUID> java.io.Serializable {
+    ctor public UUID(long, long);
+    method public int clockSequence();
+    method public int compareTo(java.util.UUID);
+    method public static java.util.UUID fromString(String);
+    method public long getLeastSignificantBits();
+    method public long getMostSignificantBits();
+    method public static java.util.UUID nameUUIDFromBytes(byte[]);
+    method public long node();
+    method public static java.util.UUID randomUUID();
+    method public long timestamp();
+    method public int variant();
+    method public int version();
+  }
+
+  public class UnknownFormatConversionException extends java.util.IllegalFormatException {
+    ctor public UnknownFormatConversionException(String);
+    method public String getConversion();
+  }
+
+  public class UnknownFormatFlagsException extends java.util.IllegalFormatException {
+    ctor public UnknownFormatFlagsException(String);
+    method public String getFlags();
+  }
+
+  public class Vector<E> extends java.util.AbstractList<E> implements java.lang.Cloneable java.util.List<E> java.util.RandomAccess java.io.Serializable {
+    ctor public Vector(int, int);
+    ctor public Vector(int);
+    ctor public Vector();
+    ctor public Vector(@NonNull java.util.Collection<? extends E>);
+    method public void addElement(E);
+    method public int capacity();
+    method @NonNull public Object clone();
+    method public void copyInto(@NonNull Object[]);
+    method public E elementAt(int);
+    method @NonNull public java.util.Enumeration<E> elements();
+    method public void ensureCapacity(int);
+    method public E firstElement();
+    method public void forEach(@NonNull java.util.function.Consumer<? super E>);
+    method public E get(int);
+    method public int indexOf(@Nullable Object, int);
+    method public void insertElementAt(E, int);
+    method public E lastElement();
+    method public int lastIndexOf(@Nullable Object, int);
+    method public void removeAllElements();
+    method public boolean removeElement(@Nullable Object);
+    method public void removeElementAt(int);
+    method public void setElementAt(E, int);
+    method public void setSize(int);
+    method public int size();
+    method public void trimToSize();
+    field protected int capacityIncrement;
+    field protected int elementCount;
+    field @NonNull protected Object[] elementData;
+  }
+
+  public class WeakHashMap<K, V> extends java.util.AbstractMap<K,V> implements java.util.Map<K,V> {
+    ctor public WeakHashMap(int, float);
+    ctor public WeakHashMap(int);
+    ctor public WeakHashMap();
+    ctor public WeakHashMap(@NonNull java.util.Map<? extends K,? extends V>);
+    method @NonNull public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+  }
+
+}
+
+package java.util.concurrent {
+
+  public abstract class AbstractExecutorService implements java.util.concurrent.ExecutorService {
+    ctor public AbstractExecutorService();
+    method public <T> java.util.List<java.util.concurrent.Future<T>> invokeAll(java.util.Collection<? extends java.util.concurrent.Callable<T>>) throws java.lang.InterruptedException;
+    method public <T> java.util.List<java.util.concurrent.Future<T>> invokeAll(java.util.Collection<? extends java.util.concurrent.Callable<T>>, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public <T> T invokeAny(java.util.Collection<? extends java.util.concurrent.Callable<T>>) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method public <T> T invokeAny(java.util.Collection<? extends java.util.concurrent.Callable<T>>, long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method protected <T> java.util.concurrent.RunnableFuture<T> newTaskFor(Runnable, T);
+    method protected <T> java.util.concurrent.RunnableFuture<T> newTaskFor(java.util.concurrent.Callable<T>);
+    method public java.util.concurrent.Future<?> submit(Runnable);
+    method public <T> java.util.concurrent.Future<T> submit(Runnable, T);
+    method public <T> java.util.concurrent.Future<T> submit(java.util.concurrent.Callable<T>);
+  }
+
+  public class ArrayBlockingQueue<E> extends java.util.AbstractQueue<E> implements java.util.concurrent.BlockingQueue<E> java.io.Serializable {
+    ctor public ArrayBlockingQueue(int);
+    ctor public ArrayBlockingQueue(int, boolean);
+    ctor public ArrayBlockingQueue(int, boolean, java.util.Collection<? extends E>);
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E peek();
+    method public E poll();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void put(E) throws java.lang.InterruptedException;
+    method public int remainingCapacity();
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+  }
+
+  public interface BlockingDeque<E> extends java.util.concurrent.BlockingQueue<E> java.util.Deque<E> {
+    method public boolean offerFirst(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public boolean offerLast(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E pollFirst(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E pollLast(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void putFirst(E) throws java.lang.InterruptedException;
+    method public void putLast(E) throws java.lang.InterruptedException;
+    method public E takeFirst() throws java.lang.InterruptedException;
+    method public E takeLast() throws java.lang.InterruptedException;
+  }
+
+  public interface BlockingQueue<E> extends java.util.Queue<E> {
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void put(E) throws java.lang.InterruptedException;
+    method public int remainingCapacity();
+    method public E take() throws java.lang.InterruptedException;
+  }
+
+  public class BrokenBarrierException extends java.lang.Exception {
+    ctor public BrokenBarrierException();
+    ctor public BrokenBarrierException(String);
+  }
+
+  @java.lang.FunctionalInterface public interface Callable<V> {
+    method public V call() throws java.lang.Exception;
+  }
+
+  public class CancellationException extends java.lang.IllegalStateException {
+    ctor public CancellationException();
+    ctor public CancellationException(String);
+  }
+
+  public class CompletableFuture<T> implements java.util.concurrent.CompletionStage<T> java.util.concurrent.Future<T> {
+    ctor public CompletableFuture();
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+    method public static java.util.concurrent.CompletableFuture<java.lang.Void> allOf(java.util.concurrent.CompletableFuture<?>...);
+    method public static java.util.concurrent.CompletableFuture<java.lang.Object> anyOf(java.util.concurrent.CompletableFuture<?>...);
+    method public <U> java.util.concurrent.CompletableFuture<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T,U>);
+    method public <U> java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T,U>);
+    method public <U> java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T,U>, java.util.concurrent.Executor);
+    method public boolean cancel(boolean);
+    method public boolean complete(T);
+    method public java.util.concurrent.CompletableFuture<T> completeAsync(java.util.function.Supplier<? extends T>, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletableFuture<T> completeAsync(java.util.function.Supplier<? extends T>);
+    method public boolean completeExceptionally(Throwable);
+    method public java.util.concurrent.CompletableFuture<T> completeOnTimeout(T, long, java.util.concurrent.TimeUnit);
+    method public static <U> java.util.concurrent.CompletableFuture<U> completedFuture(U);
+    method public static <U> java.util.concurrent.CompletionStage<U> completedStage(U);
+    method public java.util.concurrent.CompletableFuture<T> copy();
+    method public java.util.concurrent.Executor defaultExecutor();
+    method public static java.util.concurrent.Executor delayedExecutor(long, java.util.concurrent.TimeUnit, java.util.concurrent.Executor);
+    method public static java.util.concurrent.Executor delayedExecutor(long, java.util.concurrent.TimeUnit);
+    method public java.util.concurrent.CompletableFuture<T> exceptionally(java.util.function.Function<java.lang.Throwable,? extends T>);
+    method public static <U> java.util.concurrent.CompletableFuture<U> failedFuture(Throwable);
+    method public static <U> java.util.concurrent.CompletionStage<U> failedStage(Throwable);
+    method public T get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method public T get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public T getNow(T);
+    method public int getNumberOfDependents();
+    method public <U> java.util.concurrent.CompletableFuture<U> handle(java.util.function.BiFunction<? super T,java.lang.Throwable,? extends U>);
+    method public <U> java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T,java.lang.Throwable,? extends U>);
+    method public <U> java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T,java.lang.Throwable,? extends U>, java.util.concurrent.Executor);
+    method public boolean isCancelled();
+    method public boolean isCompletedExceptionally();
+    method public boolean isDone();
+    method public T join();
+    method public java.util.concurrent.CompletionStage<T> minimalCompletionStage();
+    method public <U> java.util.concurrent.CompletableFuture<U> newIncompleteFuture();
+    method public void obtrudeException(Throwable);
+    method public void obtrudeValue(T);
+    method public java.util.concurrent.CompletableFuture<T> orTimeout(long, java.util.concurrent.TimeUnit);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, Runnable, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, Runnable, java.util.concurrent.Executor);
+    method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(Runnable);
+    method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(Runnable, java.util.concurrent.Executor);
+    method public static <U> java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>);
+    method public static <U> java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+    method public <U> java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T,? super U>);
+    method public <U> java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T,? super U>);
+    method public <U> java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T,? super U>, java.util.concurrent.Executor);
+    method public <U> java.util.concurrent.CompletableFuture<U> thenApply(java.util.function.Function<? super T,? extends U>);
+    method public <U> java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T,? extends U>);
+    method public <U> java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T,? extends U>, java.util.concurrent.Executor);
+    method public <U, V> java.util.concurrent.CompletableFuture<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T,? super U,? extends V>);
+    method public <U, V> java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T,? super U,? extends V>);
+    method public <U, V> java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T,? super U,? extends V>, java.util.concurrent.Executor);
+    method public <U> java.util.concurrent.CompletableFuture<U> thenCompose(java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<U>>);
+    method public <U> java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<U>>);
+    method public <U> java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRun(Runnable);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(Runnable);
+    method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(Runnable, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+    method public java.util.concurrent.CompletableFuture<T> whenComplete(java.util.function.BiConsumer<? super T,? super java.lang.Throwable>);
+    method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T,? super java.lang.Throwable>);
+    method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T,? super java.lang.Throwable>, java.util.concurrent.Executor);
+  }
+
+  public static interface CompletableFuture.AsynchronousCompletionTask {
+  }
+
+  public class CompletionException extends java.lang.RuntimeException {
+    ctor protected CompletionException();
+    ctor protected CompletionException(String);
+    ctor public CompletionException(String, Throwable);
+    ctor public CompletionException(Throwable);
+  }
+
+  public interface CompletionService<V> {
+    method public java.util.concurrent.Future<V> poll();
+    method public java.util.concurrent.Future<V> poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public java.util.concurrent.Future<V> submit(java.util.concurrent.Callable<V>);
+    method public java.util.concurrent.Future<V> submit(Runnable, V);
+    method public java.util.concurrent.Future<V> take() throws java.lang.InterruptedException;
+  }
+
+  public interface CompletionStage<T> {
+    method public java.util.concurrent.CompletionStage<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+    method public <U> java.util.concurrent.CompletionStage<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T,U>);
+    method public <U> java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T,U>);
+    method public <U> java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T,U>, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletionStage<T> exceptionally(java.util.function.Function<java.lang.Throwable,? extends T>);
+    method public <U> java.util.concurrent.CompletionStage<U> handle(java.util.function.BiFunction<? super T,java.lang.Throwable,? extends U>);
+    method public <U> java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T,java.lang.Throwable,? extends U>);
+    method public <U> java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T,java.lang.Throwable,? extends U>, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, Runnable, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, Runnable);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, Runnable, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+    method public <U> java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T,? super U>);
+    method public <U> java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T,? super U>);
+    method public <U> java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T,? super U>, java.util.concurrent.Executor);
+    method public <U> java.util.concurrent.CompletionStage<U> thenApply(java.util.function.Function<? super T,? extends U>);
+    method public <U> java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T,? extends U>);
+    method public <U> java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T,? extends U>, java.util.concurrent.Executor);
+    method public <U, V> java.util.concurrent.CompletionStage<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T,? super U,? extends V>);
+    method public <U, V> java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T,? super U,? extends V>);
+    method public <U, V> java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T,? super U,? extends V>, java.util.concurrent.Executor);
+    method public <U> java.util.concurrent.CompletionStage<U> thenCompose(java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<U>>);
+    method public <U> java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<U>>);
+    method public <U> java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T,? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> thenRun(Runnable);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(Runnable);
+    method public java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(Runnable, java.util.concurrent.Executor);
+    method public java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+    method public java.util.concurrent.CompletionStage<T> whenComplete(java.util.function.BiConsumer<? super T,? super java.lang.Throwable>);
+    method public java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T,? super java.lang.Throwable>);
+    method public java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T,? super java.lang.Throwable>, java.util.concurrent.Executor);
+  }
+
+  public class ConcurrentHashMap<K, V> extends java.util.AbstractMap<K,V> implements java.util.concurrent.ConcurrentMap<K,V> java.io.Serializable {
+    ctor public ConcurrentHashMap();
+    ctor public ConcurrentHashMap(int);
+    ctor public ConcurrentHashMap(@NonNull java.util.Map<? extends K,? extends V>);
+    ctor public ConcurrentHashMap(int, float);
+    ctor public ConcurrentHashMap(int, float, int);
+    method public boolean contains(@NonNull Object);
+    method @NonNull public java.util.Enumeration<V> elements();
+    method @NonNull public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+    method public void forEach(long, @NonNull java.util.function.BiConsumer<? super K,? super V>);
+    method public <U> void forEach(long, @NonNull java.util.function.BiFunction<? super K,? super V,? extends U>, @NonNull java.util.function.Consumer<? super U>);
+    method public void forEachEntry(long, @NonNull java.util.function.Consumer<? super java.util.Map.Entry<K,V>>);
+    method public <U> void forEachEntry(long, @NonNull java.util.function.Function<java.util.Map.Entry<K,V>,? extends U>, @NonNull java.util.function.Consumer<? super U>);
+    method public void forEachKey(long, @NonNull java.util.function.Consumer<? super K>);
+    method public <U> void forEachKey(long, @NonNull java.util.function.Function<? super K,? extends U>, @NonNull java.util.function.Consumer<? super U>);
+    method public void forEachValue(long, @NonNull java.util.function.Consumer<? super V>);
+    method public <U> void forEachValue(long, @NonNull java.util.function.Function<? super V,? extends U>, @NonNull java.util.function.Consumer<? super U>);
+    method @NonNull public java.util.concurrent.ConcurrentHashMap.KeySetView<K,V> keySet(@NonNull V);
+    method @NonNull public java.util.Enumeration<K> keys();
+    method public long mappingCount();
+    method @NonNull public static <K> java.util.concurrent.ConcurrentHashMap.KeySetView<K,java.lang.Boolean> newKeySet();
+    method @NonNull public static <K> java.util.concurrent.ConcurrentHashMap.KeySetView<K,java.lang.Boolean> newKeySet(int);
+    method @Nullable public <U> U reduce(long, @NonNull java.util.function.BiFunction<? super K,? super V,? extends U>, @NonNull java.util.function.BiFunction<? super U,? super U,? extends U>);
+    method @Nullable public java.util.Map.Entry<K,V> reduceEntries(long, @NonNull java.util.function.BiFunction<java.util.Map.Entry<K,V>,java.util.Map.Entry<K,V>,? extends java.util.Map.Entry<K,V>>);
+    method @Nullable public <U> U reduceEntries(long, @NonNull java.util.function.Function<java.util.Map.Entry<K,V>,? extends U>, @NonNull java.util.function.BiFunction<? super U,? super U,? extends U>);
+    method public double reduceEntriesToDouble(long, @NonNull java.util.function.ToDoubleFunction<java.util.Map.Entry<K,V>>, double, @NonNull java.util.function.DoubleBinaryOperator);
+    method public int reduceEntriesToInt(long, @NonNull java.util.function.ToIntFunction<java.util.Map.Entry<K,V>>, int, @NonNull java.util.function.IntBinaryOperator);
+    method public long reduceEntriesToLong(long, @NonNull java.util.function.ToLongFunction<java.util.Map.Entry<K,V>>, long, @NonNull java.util.function.LongBinaryOperator);
+    method @Nullable public K reduceKeys(long, @NonNull java.util.function.BiFunction<? super K,? super K,? extends K>);
+    method @Nullable public <U> U reduceKeys(long, @NonNull java.util.function.Function<? super K,? extends U>, @NonNull java.util.function.BiFunction<? super U,? super U,? extends U>);
+    method public double reduceKeysToDouble(long, @NonNull java.util.function.ToDoubleFunction<? super K>, double, @NonNull java.util.function.DoubleBinaryOperator);
+    method public int reduceKeysToInt(long, @NonNull java.util.function.ToIntFunction<? super K>, int, @NonNull java.util.function.IntBinaryOperator);
+    method public long reduceKeysToLong(long, @NonNull java.util.function.ToLongFunction<? super K>, long, @NonNull java.util.function.LongBinaryOperator);
+    method public double reduceToDouble(long, @NonNull java.util.function.ToDoubleBiFunction<? super K,? super V>, double, @NonNull java.util.function.DoubleBinaryOperator);
+    method public int reduceToInt(long, @NonNull java.util.function.ToIntBiFunction<? super K,? super V>, int, @NonNull java.util.function.IntBinaryOperator);
+    method public long reduceToLong(long, @NonNull java.util.function.ToLongBiFunction<? super K,? super V>, long, @NonNull java.util.function.LongBinaryOperator);
+    method @Nullable public V reduceValues(long, @NonNull java.util.function.BiFunction<? super V,? super V,? extends V>);
+    method @Nullable public <U> U reduceValues(long, @NonNull java.util.function.Function<? super V,? extends U>, @NonNull java.util.function.BiFunction<? super U,? super U,? extends U>);
+    method public double reduceValuesToDouble(long, @NonNull java.util.function.ToDoubleFunction<? super V>, double, @NonNull java.util.function.DoubleBinaryOperator);
+    method public int reduceValuesToInt(long, @NonNull java.util.function.ToIntFunction<? super V>, int, @NonNull java.util.function.IntBinaryOperator);
+    method public long reduceValuesToLong(long, @NonNull java.util.function.ToLongFunction<? super V>, long, @NonNull java.util.function.LongBinaryOperator);
+    method @Nullable public <U> U search(long, @NonNull java.util.function.BiFunction<? super K,? super V,? extends U>);
+    method @Nullable public <U> U searchEntries(long, @NonNull java.util.function.Function<java.util.Map.Entry<K,V>,? extends U>);
+    method @Nullable public <U> U searchKeys(long, @NonNull java.util.function.Function<? super K,? extends U>);
+    method @Nullable public <U> U searchValues(long, @NonNull java.util.function.Function<? super V,? extends U>);
+  }
+
+  public static class ConcurrentHashMap.KeySetView<K, V> implements java.util.Collection<K> java.io.Serializable java.util.Set<K> {
+    method public boolean add(@NonNull K);
+    method public boolean addAll(@NonNull java.util.Collection<? extends K>);
+    method public final void clear();
+    method public boolean contains(@NonNull Object);
+    method public final boolean containsAll(@NonNull java.util.Collection<?>);
+    method public void forEach(@NonNull java.util.function.Consumer<? super K>);
+    method @NonNull public java.util.concurrent.ConcurrentHashMap<K,V> getMap();
+    method @Nullable public V getMappedValue();
+    method public final boolean isEmpty();
+    method @NonNull public java.util.Iterator<K> iterator();
+    method public boolean remove(@NonNull Object);
+    method public final boolean removeAll(@NonNull java.util.Collection<?>);
+    method public final boolean retainAll(@NonNull java.util.Collection<?>);
+    method public final int size();
+    method @NonNull public java.util.Spliterator<K> spliterator();
+    method @NonNull public final Object[] toArray();
+    method @NonNull public final <T> T[] toArray(@NonNull T[]);
+    method @NonNull public final String toString();
+  }
+
+  public class ConcurrentLinkedDeque<E> extends java.util.AbstractCollection<E> implements java.util.Deque<E> java.io.Serializable {
+    ctor public ConcurrentLinkedDeque();
+    ctor public ConcurrentLinkedDeque(java.util.Collection<? extends E>);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public java.util.Iterator<E> descendingIterator();
+    method public E element();
+    method public E getFirst();
+    method public E getLast();
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public boolean offerFirst(E);
+    method public boolean offerLast(E);
+    method public E peek();
+    method public E peekFirst();
+    method public E peekLast();
+    method public E poll();
+    method public E pollFirst();
+    method public E pollLast();
+    method public E pop();
+    method public void push(E);
+    method public E remove();
+    method public E removeFirst();
+    method public boolean removeFirstOccurrence(Object);
+    method public E removeLast();
+    method public boolean removeLastOccurrence(Object);
+    method public int size();
+  }
+
+  public class ConcurrentLinkedQueue<E> extends java.util.AbstractQueue<E> implements java.util.Queue<E> java.io.Serializable {
+    ctor public ConcurrentLinkedQueue();
+    ctor public ConcurrentLinkedQueue(java.util.Collection<? extends E>);
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public E peek();
+    method public E poll();
+    method public int size();
+  }
+
+  public interface ConcurrentMap<K, V> extends java.util.Map<K,V> {
+    method public V putIfAbsent(K, V);
+    method public boolean remove(Object, Object);
+    method public boolean replace(K, V, V);
+    method public V replace(K, V);
+  }
+
+  public interface ConcurrentNavigableMap<K, V> extends java.util.concurrent.ConcurrentMap<K,V> java.util.NavigableMap<K,V> {
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> descendingMap();
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> headMap(K, boolean);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> headMap(K);
+    method public java.util.NavigableSet<K> keySet();
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> subMap(K, boolean, K, boolean);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> subMap(K, K);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> tailMap(K, boolean);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> tailMap(K);
+  }
+
+  public class ConcurrentSkipListMap<K, V> extends java.util.AbstractMap<K,V> implements java.lang.Cloneable java.util.concurrent.ConcurrentNavigableMap<K,V> java.io.Serializable {
+    ctor public ConcurrentSkipListMap();
+    ctor public ConcurrentSkipListMap(java.util.Comparator<? super K>);
+    ctor public ConcurrentSkipListMap(java.util.Map<? extends K,? extends V>);
+    ctor public ConcurrentSkipListMap(java.util.SortedMap<K,? extends V>);
+    method public java.util.Map.Entry<K,V> ceilingEntry(K);
+    method public K ceilingKey(K);
+    method public java.util.concurrent.ConcurrentSkipListMap<K,V> clone();
+    method public java.util.Comparator<? super K> comparator();
+    method public java.util.NavigableSet<K> descendingKeySet();
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> descendingMap();
+    method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
+    method public java.util.Map.Entry<K,V> firstEntry();
+    method public K firstKey();
+    method public java.util.Map.Entry<K,V> floorEntry(K);
+    method public K floorKey(K);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> headMap(K, boolean);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> headMap(K);
+    method public java.util.Map.Entry<K,V> higherEntry(K);
+    method public K higherKey(K);
+    method public java.util.NavigableSet<K> keySet();
+    method public java.util.Map.Entry<K,V> lastEntry();
+    method public K lastKey();
+    method public java.util.Map.Entry<K,V> lowerEntry(K);
+    method public K lowerKey(K);
+    method public java.util.NavigableSet<K> navigableKeySet();
+    method public java.util.Map.Entry<K,V> pollFirstEntry();
+    method public java.util.Map.Entry<K,V> pollLastEntry();
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> subMap(K, boolean, K, boolean);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> subMap(K, K);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> tailMap(K, boolean);
+    method public java.util.concurrent.ConcurrentNavigableMap<K,V> tailMap(K);
+  }
+
+  public class ConcurrentSkipListSet<E> extends java.util.AbstractSet<E> implements java.lang.Cloneable java.util.NavigableSet<E> java.io.Serializable {
+    ctor public ConcurrentSkipListSet();
+    ctor public ConcurrentSkipListSet(java.util.Comparator<? super E>);
+    ctor public ConcurrentSkipListSet(java.util.Collection<? extends E>);
+    ctor public ConcurrentSkipListSet(java.util.SortedSet<E>);
+    method public E ceiling(E);
+    method public java.util.concurrent.ConcurrentSkipListSet<E> clone();
+    method public java.util.Comparator<? super E> comparator();
+    method public java.util.Iterator<E> descendingIterator();
+    method public java.util.NavigableSet<E> descendingSet();
+    method public E first();
+    method public E floor(E);
+    method public java.util.NavigableSet<E> headSet(E, boolean);
+    method public java.util.NavigableSet<E> headSet(E);
+    method public E higher(E);
+    method public java.util.Iterator<E> iterator();
+    method public E last();
+    method public E lower(E);
+    method public E pollFirst();
+    method public E pollLast();
+    method public int size();
+    method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
+    method public java.util.NavigableSet<E> subSet(E, E);
+    method public java.util.NavigableSet<E> tailSet(E, boolean);
+    method public java.util.NavigableSet<E> tailSet(E);
+  }
+
+  public class CopyOnWriteArrayList<E> implements java.lang.Cloneable java.util.List<E> java.util.RandomAccess java.io.Serializable {
+    ctor public CopyOnWriteArrayList();
+    ctor public CopyOnWriteArrayList(@NonNull java.util.Collection<? extends E>);
+    ctor public CopyOnWriteArrayList(@NonNull E[]);
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(@NonNull java.util.Collection<? extends E>);
+    method public boolean addAll(int, @NonNull java.util.Collection<? extends E>);
+    method public int addAllAbsent(@NonNull java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
+    method @NonNull public Object clone();
+    method public boolean contains(@Nullable Object);
+    method public boolean containsAll(@NonNull java.util.Collection<?>);
+    method public void forEach(@NonNull java.util.function.Consumer<? super E>);
+    method public E get(int);
+    method public int indexOf(@Nullable Object);
+    method public int indexOf(@Nullable E, int);
+    method public boolean isEmpty();
+    method @NonNull public java.util.Iterator<E> iterator();
+    method public int lastIndexOf(@Nullable Object);
+    method public int lastIndexOf(@Nullable E, int);
+    method @NonNull public java.util.ListIterator<E> listIterator();
+    method @NonNull public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(@Nullable Object);
+    method public boolean removeAll(@NonNull java.util.Collection<?>);
+    method public boolean retainAll(@NonNull java.util.Collection<?>);
+    method public E set(int, E);
+    method public int size();
+    method @NonNull public java.util.List<E> subList(int, int);
+    method @NonNull public Object[] toArray();
+    method @NonNull public <T> T[] toArray(@NonNull T[]);
+  }
+
+  public class CopyOnWriteArraySet<E> extends java.util.AbstractSet<E> implements java.io.Serializable {
+    ctor public CopyOnWriteArraySet();
+    ctor public CopyOnWriteArraySet(java.util.Collection<? extends E>);
+    method public void forEach(java.util.function.Consumer<? super E>);
+    method public java.util.Iterator<E> iterator();
+    method public int size();
+  }
+
+  public class CountDownLatch {
+    ctor public CountDownLatch(int);
+    method public void await() throws java.lang.InterruptedException;
+    method public boolean await(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void countDown();
+    method public long getCount();
+  }
+
+  public abstract class CountedCompleter<T> extends java.util.concurrent.ForkJoinTask<T> {
+    ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>, int);
+    ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>);
+    ctor protected CountedCompleter();
+    method public final void addToPendingCount(int);
+    method public final boolean compareAndSetPendingCount(int, int);
+    method public void complete(T);
+    method public abstract void compute();
+    method public final int decrementPendingCountUnlessZero();
+    method protected final boolean exec();
+    method public final java.util.concurrent.CountedCompleter<?> firstComplete();
+    method public final java.util.concurrent.CountedCompleter<?> getCompleter();
+    method public final int getPendingCount();
+    method public T getRawResult();
+    method public final java.util.concurrent.CountedCompleter<?> getRoot();
+    method public final void helpComplete(int);
+    method public final java.util.concurrent.CountedCompleter<?> nextComplete();
+    method public void onCompletion(java.util.concurrent.CountedCompleter<?>);
+    method public boolean onExceptionalCompletion(Throwable, java.util.concurrent.CountedCompleter<?>);
+    method public final void propagateCompletion();
+    method public final void quietlyCompleteRoot();
+    method public final void setPendingCount(int);
+    method protected void setRawResult(T);
+    method public final void tryComplete();
+  }
+
+  public class CyclicBarrier {
+    ctor public CyclicBarrier(int, Runnable);
+    ctor public CyclicBarrier(int);
+    method public int await() throws java.util.concurrent.BrokenBarrierException, java.lang.InterruptedException;
+    method public int await(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.BrokenBarrierException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public int getNumberWaiting();
+    method public int getParties();
+    method public boolean isBroken();
+    method public void reset();
+  }
+
+  public class DelayQueue<E extends java.util.concurrent.Delayed> extends java.util.AbstractQueue<E> implements java.util.concurrent.BlockingQueue<E> {
+    ctor public DelayQueue();
+    ctor public DelayQueue(java.util.Collection<? extends E>);
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit);
+    method public E peek();
+    method public E poll();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void put(E);
+    method public int remainingCapacity();
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+  }
+
+  public interface Delayed extends java.lang.Comparable<java.util.concurrent.Delayed> {
+    method public long getDelay(java.util.concurrent.TimeUnit);
+  }
+
+  public class Exchanger<V> {
+    ctor public Exchanger();
+    method public V exchange(V) throws java.lang.InterruptedException;
+    method public V exchange(V, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+  }
+
+  public class ExecutionException extends java.lang.Exception {
+    ctor protected ExecutionException();
+    ctor protected ExecutionException(String);
+    ctor public ExecutionException(String, Throwable);
+    ctor public ExecutionException(Throwable);
+  }
+
+  public interface Executor {
+    method public void execute(Runnable);
+  }
+
+  public class ExecutorCompletionService<V> implements java.util.concurrent.CompletionService<V> {
+    ctor public ExecutorCompletionService(java.util.concurrent.Executor);
+    ctor public ExecutorCompletionService(java.util.concurrent.Executor, java.util.concurrent.BlockingQueue<java.util.concurrent.Future<V>>);
+    method public java.util.concurrent.Future<V> poll();
+    method public java.util.concurrent.Future<V> poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public java.util.concurrent.Future<V> submit(java.util.concurrent.Callable<V>);
+    method public java.util.concurrent.Future<V> submit(Runnable, V);
+    method public java.util.concurrent.Future<V> take() throws java.lang.InterruptedException;
+  }
+
+  public interface ExecutorService extends java.util.concurrent.Executor {
+    method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public <T> java.util.List<java.util.concurrent.Future<T>> invokeAll(java.util.Collection<? extends java.util.concurrent.Callable<T>>) throws java.lang.InterruptedException;
+    method public <T> java.util.List<java.util.concurrent.Future<T>> invokeAll(java.util.Collection<? extends java.util.concurrent.Callable<T>>, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public <T> T invokeAny(java.util.Collection<? extends java.util.concurrent.Callable<T>>) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method public <T> T invokeAny(java.util.Collection<? extends java.util.concurrent.Callable<T>>, long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public boolean isShutdown();
+    method public boolean isTerminated();
+    method public void shutdown();
+    method public java.util.List<java.lang.Runnable> shutdownNow();
+    method public <T> java.util.concurrent.Future<T> submit(java.util.concurrent.Callable<T>);
+    method public <T> java.util.concurrent.Future<T> submit(Runnable, T);
+    method public java.util.concurrent.Future<?> submit(Runnable);
+  }
+
+  public class Executors {
+    method public static <T> java.util.concurrent.Callable<T> callable(Runnable, T);
+    method public static java.util.concurrent.Callable<java.lang.Object> callable(Runnable);
+    method public static java.util.concurrent.Callable<java.lang.Object> callable(java.security.PrivilegedAction<?>);
+    method public static java.util.concurrent.Callable<java.lang.Object> callable(java.security.PrivilegedExceptionAction<?>);
+    method public static java.util.concurrent.ThreadFactory defaultThreadFactory();
+    method public static java.util.concurrent.ExecutorService newCachedThreadPool();
+    method public static java.util.concurrent.ExecutorService newCachedThreadPool(java.util.concurrent.ThreadFactory);
+    method public static java.util.concurrent.ExecutorService newFixedThreadPool(int);
+    method public static java.util.concurrent.ExecutorService newFixedThreadPool(int, java.util.concurrent.ThreadFactory);
+    method public static java.util.concurrent.ScheduledExecutorService newScheduledThreadPool(int);
+    method public static java.util.concurrent.ScheduledExecutorService newScheduledThreadPool(int, java.util.concurrent.ThreadFactory);
+    method public static java.util.concurrent.ExecutorService newSingleThreadExecutor();
+    method public static java.util.concurrent.ExecutorService newSingleThreadExecutor(java.util.concurrent.ThreadFactory);
+    method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor();
+    method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor(java.util.concurrent.ThreadFactory);
+    method public static java.util.concurrent.ExecutorService newWorkStealingPool(int);
+    method public static java.util.concurrent.ExecutorService newWorkStealingPool();
+    method public static <T> java.util.concurrent.Callable<T> privilegedCallable(java.util.concurrent.Callable<T>);
+    method public static <T> java.util.concurrent.Callable<T> privilegedCallableUsingCurrentClassLoader(java.util.concurrent.Callable<T>);
+    method public static java.util.concurrent.ThreadFactory privilegedThreadFactory();
+    method public static java.util.concurrent.ExecutorService unconfigurableExecutorService(java.util.concurrent.ExecutorService);
+    method public static java.util.concurrent.ScheduledExecutorService unconfigurableScheduledExecutorService(java.util.concurrent.ScheduledExecutorService);
+  }
+
+  public final class Flow {
+    method public static int defaultBufferSize();
+  }
+
+  public static interface Flow.Processor<T, R> extends java.util.concurrent.Flow.Subscriber<T> java.util.concurrent.Flow.Publisher<R> {
+  }
+
+  @java.lang.FunctionalInterface public static interface Flow.Publisher<T> {
+    method public void subscribe(java.util.concurrent.Flow.Subscriber<? super T>);
+  }
+
+  public static interface Flow.Subscriber<T> {
+    method public void onComplete();
+    method public void onError(Throwable);
+    method public void onNext(T);
+    method public void onSubscribe(java.util.concurrent.Flow.Subscription);
+  }
+
+  public static interface Flow.Subscription {
+    method public void cancel();
+    method public void request(long);
+  }
+
+  public class ForkJoinPool extends java.util.concurrent.AbstractExecutorService {
+    ctor public ForkJoinPool();
+    ctor public ForkJoinPool(int);
+    ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
+    method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
+    method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public static java.util.concurrent.ForkJoinPool commonPool();
+    method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>);
+    method public void execute(java.util.concurrent.ForkJoinTask<?>);
+    method public void execute(Runnable);
+    method public int getActiveThreadCount();
+    method public boolean getAsyncMode();
+    method public static int getCommonPoolParallelism();
+    method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory();
+    method public int getParallelism();
+    method public int getPoolSize();
+    method public int getQueuedSubmissionCount();
+    method public long getQueuedTaskCount();
+    method public int getRunningThreadCount();
+    method public long getStealCount();
+    method public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler();
+    method public boolean hasQueuedSubmissions();
+    method public <T> T invoke(java.util.concurrent.ForkJoinTask<T>);
+    method public <T> java.util.List<java.util.concurrent.Future<T>> invokeAll(java.util.Collection<? extends java.util.concurrent.Callable<T>>);
+    method public boolean isQuiescent();
+    method public boolean isShutdown();
+    method public boolean isTerminated();
+    method public boolean isTerminating();
+    method public static void managedBlock(java.util.concurrent.ForkJoinPool.ManagedBlocker) throws java.lang.InterruptedException;
+    method protected java.util.concurrent.ForkJoinTask<?> pollSubmission();
+    method public void shutdown();
+    method public java.util.List<java.lang.Runnable> shutdownNow();
+    method public <T> java.util.concurrent.ForkJoinTask<T> submit(java.util.concurrent.ForkJoinTask<T>);
+    method public <T> java.util.concurrent.ForkJoinTask<T> submit(java.util.concurrent.Callable<T>);
+    method public <T> java.util.concurrent.ForkJoinTask<T> submit(Runnable, T);
+    method public java.util.concurrent.ForkJoinTask<?> submit(Runnable);
+    field public static final java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory defaultForkJoinWorkerThreadFactory;
+  }
+
+  public static interface ForkJoinPool.ForkJoinWorkerThreadFactory {
+    method public java.util.concurrent.ForkJoinWorkerThread newThread(java.util.concurrent.ForkJoinPool);
+  }
+
+  public static interface ForkJoinPool.ManagedBlocker {
+    method public boolean block() throws java.lang.InterruptedException;
+    method public boolean isReleasable();
+  }
+
+  public abstract class ForkJoinTask<V> implements java.util.concurrent.Future<V> java.io.Serializable {
+    ctor public ForkJoinTask();
+    method public static java.util.concurrent.ForkJoinTask<?> adapt(Runnable);
+    method public static <T> java.util.concurrent.ForkJoinTask<T> adapt(Runnable, T);
+    method public static <T> java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>);
+    method public boolean cancel(boolean);
+    method public final boolean compareAndSetForkJoinTaskTag(short, short);
+    method public void complete(V);
+    method public void completeExceptionally(Throwable);
+    method protected abstract boolean exec();
+    method public final java.util.concurrent.ForkJoinTask<V> fork();
+    method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public final Throwable getException();
+    method public final short getForkJoinTaskTag();
+    method public static java.util.concurrent.ForkJoinPool getPool();
+    method public static int getQueuedTaskCount();
+    method public abstract V getRawResult();
+    method public static int getSurplusQueuedTaskCount();
+    method public static void helpQuiesce();
+    method public static boolean inForkJoinPool();
+    method public final V invoke();
+    method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>, java.util.concurrent.ForkJoinTask<?>);
+    method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>...);
+    method public static <T extends java.util.concurrent.ForkJoinTask<?>> java.util.Collection<T> invokeAll(java.util.Collection<T>);
+    method public final boolean isCancelled();
+    method public final boolean isCompletedAbnormally();
+    method public final boolean isCompletedNormally();
+    method public final boolean isDone();
+    method public final V join();
+    method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask();
+    method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask();
+    method protected static java.util.concurrent.ForkJoinTask<?> pollTask();
+    method public final void quietlyComplete();
+    method public final void quietlyInvoke();
+    method public final void quietlyJoin();
+    method public void reinitialize();
+    method public final short setForkJoinTaskTag(short);
+    method protected abstract void setRawResult(V);
+    method public boolean tryUnfork();
+  }
+
+  public class ForkJoinWorkerThread extends java.lang.Thread {
+    ctor protected ForkJoinWorkerThread(java.util.concurrent.ForkJoinPool);
+    method public java.util.concurrent.ForkJoinPool getPool();
+    method public int getPoolIndex();
+    method protected void onStart();
+    method protected void onTermination(Throwable);
+  }
+
+  public interface Future<V> {
+    method public boolean cancel(boolean);
+    method public V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method public V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public boolean isCancelled();
+    method public boolean isDone();
+  }
+
+  public class FutureTask<V> implements java.util.concurrent.RunnableFuture<V> {
+    ctor public FutureTask(java.util.concurrent.Callable<V>);
+    ctor public FutureTask(Runnable, V);
+    method public boolean cancel(boolean);
+    method protected void done();
+    method public V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method public V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public boolean isCancelled();
+    method public boolean isDone();
+    method public void run();
+    method protected boolean runAndReset();
+    method protected void set(V);
+    method protected void setException(Throwable);
+  }
+
+  public class LinkedBlockingDeque<E> extends java.util.AbstractQueue<E> implements java.util.concurrent.BlockingDeque<E> java.io.Serializable {
+    ctor public LinkedBlockingDeque();
+    ctor public LinkedBlockingDeque(int);
+    ctor public LinkedBlockingDeque(java.util.Collection<? extends E>);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public java.util.Iterator<E> descendingIterator();
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public E getFirst();
+    method public E getLast();
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public boolean offerFirst(E);
+    method public boolean offerFirst(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public boolean offerLast(E);
+    method public boolean offerLast(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E peek();
+    method public E peekFirst();
+    method public E peekLast();
+    method public E poll();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E pollFirst();
+    method public E pollFirst(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E pollLast();
+    method public E pollLast(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E pop();
+    method public void push(E);
+    method public void put(E) throws java.lang.InterruptedException;
+    method public void putFirst(E) throws java.lang.InterruptedException;
+    method public void putLast(E) throws java.lang.InterruptedException;
+    method public int remainingCapacity();
+    method public E removeFirst();
+    method public boolean removeFirstOccurrence(Object);
+    method public E removeLast();
+    method public boolean removeLastOccurrence(Object);
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+    method public E takeFirst() throws java.lang.InterruptedException;
+    method public E takeLast() throws java.lang.InterruptedException;
+  }
+
+  public class LinkedBlockingQueue<E> extends java.util.AbstractQueue<E> implements java.util.concurrent.BlockingQueue<E> java.io.Serializable {
+    ctor public LinkedBlockingQueue();
+    ctor public LinkedBlockingQueue(int);
+    ctor public LinkedBlockingQueue(java.util.Collection<? extends E>);
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public boolean offer(E);
+    method public E peek();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E poll();
+    method public void put(E) throws java.lang.InterruptedException;
+    method public int remainingCapacity();
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+  }
+
+  public class LinkedTransferQueue<E> extends java.util.AbstractQueue<E> implements java.io.Serializable java.util.concurrent.TransferQueue<E> {
+    ctor public LinkedTransferQueue();
+    ctor public LinkedTransferQueue(java.util.Collection<? extends E>);
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public int getWaitingConsumerCount();
+    method public boolean hasWaitingConsumer();
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit);
+    method public boolean offer(E);
+    method public E peek();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E poll();
+    method public void put(E);
+    method public int remainingCapacity();
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+    method public void transfer(E) throws java.lang.InterruptedException;
+    method public boolean tryTransfer(E);
+    method public boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+  }
+
+  public class Phaser {
+    ctor public Phaser();
+    ctor public Phaser(int);
+    ctor public Phaser(java.util.concurrent.Phaser);
+    ctor public Phaser(java.util.concurrent.Phaser, int);
+    method public int arrive();
+    method public int arriveAndAwaitAdvance();
+    method public int arriveAndDeregister();
+    method public int awaitAdvance(int);
+    method public int awaitAdvanceInterruptibly(int) throws java.lang.InterruptedException;
+    method public int awaitAdvanceInterruptibly(int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public int bulkRegister(int);
+    method public void forceTermination();
+    method public int getArrivedParties();
+    method public java.util.concurrent.Phaser getParent();
+    method public final int getPhase();
+    method public int getRegisteredParties();
+    method public java.util.concurrent.Phaser getRoot();
+    method public int getUnarrivedParties();
+    method public boolean isTerminated();
+    method protected boolean onAdvance(int, int);
+    method public int register();
+  }
+
+  public class PriorityBlockingQueue<E> extends java.util.AbstractQueue<E> implements java.util.concurrent.BlockingQueue<E> java.io.Serializable {
+    ctor public PriorityBlockingQueue();
+    ctor public PriorityBlockingQueue(int);
+    ctor public PriorityBlockingQueue(int, java.util.Comparator<? super E>);
+    ctor public PriorityBlockingQueue(java.util.Collection<? extends E>);
+    method public java.util.Comparator<? super E> comparator();
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit);
+    method public E peek();
+    method public E poll();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void put(E);
+    method public int remainingCapacity();
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+  }
+
+  public abstract class RecursiveAction extends java.util.concurrent.ForkJoinTask<java.lang.Void> {
+    ctor public RecursiveAction();
+    method protected abstract void compute();
+    method protected final boolean exec();
+    method public final Void getRawResult();
+    method protected final void setRawResult(Void);
+  }
+
+  public abstract class RecursiveTask<V> extends java.util.concurrent.ForkJoinTask<V> {
+    ctor public RecursiveTask();
+    method protected abstract V compute();
+    method protected final boolean exec();
+    method public final V getRawResult();
+    method protected final void setRawResult(V);
+  }
+
+  public class RejectedExecutionException extends java.lang.RuntimeException {
+    ctor public RejectedExecutionException();
+    ctor public RejectedExecutionException(String);
+    ctor public RejectedExecutionException(String, Throwable);
+    ctor public RejectedExecutionException(Throwable);
+  }
+
+  public interface RejectedExecutionHandler {
+    method public void rejectedExecution(Runnable, java.util.concurrent.ThreadPoolExecutor);
+  }
+
+  public interface RunnableFuture<V> extends java.lang.Runnable java.util.concurrent.Future<V> {
+  }
+
+  public interface RunnableScheduledFuture<V> extends java.util.concurrent.RunnableFuture<V> java.util.concurrent.ScheduledFuture<V> {
+    method public boolean isPeriodic();
+  }
+
+  public interface ScheduledExecutorService extends java.util.concurrent.ExecutorService {
+    method public java.util.concurrent.ScheduledFuture<?> schedule(Runnable, long, java.util.concurrent.TimeUnit);
+    method public <V> java.util.concurrent.ScheduledFuture<V> schedule(java.util.concurrent.Callable<V>, long, java.util.concurrent.TimeUnit);
+    method public java.util.concurrent.ScheduledFuture<?> scheduleAtFixedRate(Runnable, long, long, java.util.concurrent.TimeUnit);
+    method public java.util.concurrent.ScheduledFuture<?> scheduleWithFixedDelay(Runnable, long, long, java.util.concurrent.TimeUnit);
+  }
+
+  public interface ScheduledFuture<V> extends java.util.concurrent.Delayed java.util.concurrent.Future<V> {
+  }
+
+  public class ScheduledThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor implements java.util.concurrent.ScheduledExecutorService {
+    ctor public ScheduledThreadPoolExecutor(int);
+    ctor public ScheduledThreadPoolExecutor(int, java.util.concurrent.ThreadFactory);
+    ctor public ScheduledThreadPoolExecutor(int, java.util.concurrent.RejectedExecutionHandler);
+    ctor public ScheduledThreadPoolExecutor(int, java.util.concurrent.ThreadFactory, java.util.concurrent.RejectedExecutionHandler);
+    method protected <V> java.util.concurrent.RunnableScheduledFuture<V> decorateTask(Runnable, java.util.concurrent.RunnableScheduledFuture<V>);
+    method protected <V> java.util.concurrent.RunnableScheduledFuture<V> decorateTask(java.util.concurrent.Callable<V>, java.util.concurrent.RunnableScheduledFuture<V>);
+    method public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy();
+    method public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy();
+    method public boolean getRemoveOnCancelPolicy();
+    method public java.util.concurrent.ScheduledFuture<?> schedule(Runnable, long, java.util.concurrent.TimeUnit);
+    method public <V> java.util.concurrent.ScheduledFuture<V> schedule(java.util.concurrent.Callable<V>, long, java.util.concurrent.TimeUnit);
+    method public java.util.concurrent.ScheduledFuture<?> scheduleAtFixedRate(Runnable, long, long, java.util.concurrent.TimeUnit);
+    method public java.util.concurrent.ScheduledFuture<?> scheduleWithFixedDelay(Runnable, long, long, java.util.concurrent.TimeUnit);
+    method public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean);
+    method public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean);
+    method public void setRemoveOnCancelPolicy(boolean);
+  }
+
+  public class Semaphore implements java.io.Serializable {
+    ctor public Semaphore(int);
+    ctor public Semaphore(int, boolean);
+    method public void acquire() throws java.lang.InterruptedException;
+    method public void acquire(int) throws java.lang.InterruptedException;
+    method public void acquireUninterruptibly();
+    method public void acquireUninterruptibly(int);
+    method public int availablePermits();
+    method public int drainPermits();
+    method public final int getQueueLength();
+    method protected java.util.Collection<java.lang.Thread> getQueuedThreads();
+    method public final boolean hasQueuedThreads();
+    method public boolean isFair();
+    method protected void reducePermits(int);
+    method public void release();
+    method public void release(int);
+    method public boolean tryAcquire();
+    method public boolean tryAcquire(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public boolean tryAcquire(int);
+    method public boolean tryAcquire(int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+  }
+
+  public class SynchronousQueue<E> extends java.util.AbstractQueue<E> implements java.util.concurrent.BlockingQueue<E> java.io.Serializable {
+    ctor public SynchronousQueue();
+    ctor public SynchronousQueue(boolean);
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public boolean offer(E);
+    method public E peek();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E poll();
+    method public void put(E) throws java.lang.InterruptedException;
+    method public int remainingCapacity();
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+  }
+
+  public interface ThreadFactory {
+    method public Thread newThread(Runnable);
+  }
+
+  public class ThreadLocalRandom extends java.util.Random {
+    method public static java.util.concurrent.ThreadLocalRandom current();
+    method public double nextDouble(double);
+    method public double nextDouble(double, double);
+    method public int nextInt(int, int);
+    method public long nextLong(long);
+    method public long nextLong(long, long);
+  }
+
+  public class ThreadPoolExecutor extends java.util.concurrent.AbstractExecutorService {
+    ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>);
+    ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>, java.util.concurrent.ThreadFactory);
+    ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>, java.util.concurrent.RejectedExecutionHandler);
+    ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>, java.util.concurrent.ThreadFactory, java.util.concurrent.RejectedExecutionHandler);
+    method protected void afterExecute(Runnable, Throwable);
+    method public void allowCoreThreadTimeOut(boolean);
+    method public boolean allowsCoreThreadTimeOut();
+    method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method protected void beforeExecute(Thread, Runnable);
+    method public void execute(Runnable);
+    method protected void finalize();
+    method public int getActiveCount();
+    method public long getCompletedTaskCount();
+    method public int getCorePoolSize();
+    method public long getKeepAliveTime(java.util.concurrent.TimeUnit);
+    method public int getLargestPoolSize();
+    method public int getMaximumPoolSize();
+    method public int getPoolSize();
+    method public java.util.concurrent.BlockingQueue<java.lang.Runnable> getQueue();
+    method public java.util.concurrent.RejectedExecutionHandler getRejectedExecutionHandler();
+    method public long getTaskCount();
+    method public java.util.concurrent.ThreadFactory getThreadFactory();
+    method public boolean isShutdown();
+    method public boolean isTerminated();
+    method public boolean isTerminating();
+    method public int prestartAllCoreThreads();
+    method public boolean prestartCoreThread();
+    method public void purge();
+    method public boolean remove(Runnable);
+    method public void setCorePoolSize(int);
+    method public void setKeepAliveTime(long, java.util.concurrent.TimeUnit);
+    method public void setMaximumPoolSize(int);
+    method public void setRejectedExecutionHandler(java.util.concurrent.RejectedExecutionHandler);
+    method public void setThreadFactory(java.util.concurrent.ThreadFactory);
+    method public void shutdown();
+    method public java.util.List<java.lang.Runnable> shutdownNow();
+    method protected void terminated();
+  }
+
+  public static class ThreadPoolExecutor.AbortPolicy implements java.util.concurrent.RejectedExecutionHandler {
+    ctor public ThreadPoolExecutor.AbortPolicy();
+    method public void rejectedExecution(Runnable, java.util.concurrent.ThreadPoolExecutor);
+  }
+
+  public static class ThreadPoolExecutor.CallerRunsPolicy implements java.util.concurrent.RejectedExecutionHandler {
+    ctor public ThreadPoolExecutor.CallerRunsPolicy();
+    method public void rejectedExecution(Runnable, java.util.concurrent.ThreadPoolExecutor);
+  }
+
+  public static class ThreadPoolExecutor.DiscardOldestPolicy implements java.util.concurrent.RejectedExecutionHandler {
+    ctor public ThreadPoolExecutor.DiscardOldestPolicy();
+    method public void rejectedExecution(Runnable, java.util.concurrent.ThreadPoolExecutor);
+  }
+
+  public static class ThreadPoolExecutor.DiscardPolicy implements java.util.concurrent.RejectedExecutionHandler {
+    ctor public ThreadPoolExecutor.DiscardPolicy();
+    method public void rejectedExecution(Runnable, java.util.concurrent.ThreadPoolExecutor);
+  }
+
+  public enum TimeUnit {
+    method public long convert(long, java.util.concurrent.TimeUnit);
+    method public void sleep(long) throws java.lang.InterruptedException;
+    method public void timedJoin(Thread, long) throws java.lang.InterruptedException;
+    method public void timedWait(Object, long) throws java.lang.InterruptedException;
+    method public long toDays(long);
+    method public long toHours(long);
+    method public long toMicros(long);
+    method public long toMillis(long);
+    method public long toMinutes(long);
+    method public long toNanos(long);
+    method public long toSeconds(long);
+    enum_constant public static final java.util.concurrent.TimeUnit DAYS;
+    enum_constant public static final java.util.concurrent.TimeUnit HOURS;
+    enum_constant public static final java.util.concurrent.TimeUnit MICROSECONDS;
+    enum_constant public static final java.util.concurrent.TimeUnit MILLISECONDS;
+    enum_constant public static final java.util.concurrent.TimeUnit MINUTES;
+    enum_constant public static final java.util.concurrent.TimeUnit NANOSECONDS;
+    enum_constant public static final java.util.concurrent.TimeUnit SECONDS;
+  }
+
+  public class TimeoutException extends java.lang.Exception {
+    ctor public TimeoutException();
+    ctor public TimeoutException(String);
+  }
+
+  public interface TransferQueue<E> extends java.util.concurrent.BlockingQueue<E> {
+    method public int getWaitingConsumerCount();
+    method public boolean hasWaitingConsumer();
+    method public void transfer(E) throws java.lang.InterruptedException;
+    method public boolean tryTransfer(E);
+    method public boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+  }
+
+}
+
+package java.util.concurrent.atomic {
+
+  public class AtomicBoolean implements java.io.Serializable {
+    ctor public AtomicBoolean(boolean);
+    ctor public AtomicBoolean();
+    method public final boolean compareAndSet(boolean, boolean);
+    method public final boolean get();
+    method public final boolean getAndSet(boolean);
+    method public final void lazySet(boolean);
+    method public final void set(boolean);
+    method public boolean weakCompareAndSet(boolean, boolean);
+  }
+
+  public class AtomicInteger extends java.lang.Number implements java.io.Serializable {
+    ctor public AtomicInteger(int);
+    ctor public AtomicInteger();
+    method public final int accumulateAndGet(int, java.util.function.IntBinaryOperator);
+    method public final int addAndGet(int);
+    method public final boolean compareAndSet(int, int);
+    method public final int decrementAndGet();
+    method public double doubleValue();
+    method public float floatValue();
+    method public final int get();
+    method public final int getAndAccumulate(int, java.util.function.IntBinaryOperator);
+    method public final int getAndAdd(int);
+    method public final int getAndDecrement();
+    method public final int getAndIncrement();
+    method public final int getAndSet(int);
+    method public final int getAndUpdate(java.util.function.IntUnaryOperator);
+    method public final int incrementAndGet();
+    method public int intValue();
+    method public final void lazySet(int);
+    method public long longValue();
+    method public final void set(int);
+    method public final int updateAndGet(java.util.function.IntUnaryOperator);
+    method public final boolean weakCompareAndSet(int, int);
+  }
+
+  public class AtomicIntegerArray implements java.io.Serializable {
+    ctor public AtomicIntegerArray(int);
+    ctor public AtomicIntegerArray(int[]);
+    method public final int accumulateAndGet(int, int, java.util.function.IntBinaryOperator);
+    method public final int addAndGet(int, int);
+    method public final boolean compareAndSet(int, int, int);
+    method public final int decrementAndGet(int);
+    method public final int get(int);
+    method public final int getAndAccumulate(int, int, java.util.function.IntBinaryOperator);
+    method public final int getAndAdd(int, int);
+    method public final int getAndDecrement(int);
+    method public final int getAndIncrement(int);
+    method public final int getAndSet(int, int);
+    method public final int getAndUpdate(int, java.util.function.IntUnaryOperator);
+    method public final int incrementAndGet(int);
+    method public final void lazySet(int, int);
+    method public final int length();
+    method public final void set(int, int);
+    method public final int updateAndGet(int, java.util.function.IntUnaryOperator);
+    method public final boolean weakCompareAndSet(int, int, int);
+  }
+
+  public abstract class AtomicIntegerFieldUpdater<T> {
+    ctor protected AtomicIntegerFieldUpdater();
+    method public final int accumulateAndGet(T, int, java.util.function.IntBinaryOperator);
+    method public int addAndGet(T, int);
+    method public abstract boolean compareAndSet(T, int, int);
+    method public int decrementAndGet(T);
+    method public abstract int get(T);
+    method public final int getAndAccumulate(T, int, java.util.function.IntBinaryOperator);
+    method public int getAndAdd(T, int);
+    method public int getAndDecrement(T);
+    method public int getAndIncrement(T);
+    method public int getAndSet(T, int);
+    method public final int getAndUpdate(T, java.util.function.IntUnaryOperator);
+    method public int incrementAndGet(T);
+    method public abstract void lazySet(T, int);
+    method public static <U> java.util.concurrent.atomic.AtomicIntegerFieldUpdater<U> newUpdater(Class<U>, String);
+    method public abstract void set(T, int);
+    method public final int updateAndGet(T, java.util.function.IntUnaryOperator);
+    method public abstract boolean weakCompareAndSet(T, int, int);
+  }
+
+  public class AtomicLong extends java.lang.Number implements java.io.Serializable {
+    ctor public AtomicLong(long);
+    ctor public AtomicLong();
+    method public final long accumulateAndGet(long, java.util.function.LongBinaryOperator);
+    method public final long addAndGet(long);
+    method public final boolean compareAndSet(long, long);
+    method public final long decrementAndGet();
+    method public double doubleValue();
+    method public float floatValue();
+    method public final long get();
+    method public final long getAndAccumulate(long, java.util.function.LongBinaryOperator);
+    method public final long getAndAdd(long);
+    method public final long getAndDecrement();
+    method public final long getAndIncrement();
+    method public final long getAndSet(long);
+    method public final long getAndUpdate(java.util.function.LongUnaryOperator);
+    method public final long incrementAndGet();
+    method public int intValue();
+    method public final void lazySet(long);
+    method public long longValue();
+    method public final void set(long);
+    method public final long updateAndGet(java.util.function.LongUnaryOperator);
+    method public final boolean weakCompareAndSet(long, long);
+  }
+
+  public class AtomicLongArray implements java.io.Serializable {
+    ctor public AtomicLongArray(int);
+    ctor public AtomicLongArray(long[]);
+    method public final long accumulateAndGet(int, long, java.util.function.LongBinaryOperator);
+    method public long addAndGet(int, long);
+    method public final boolean compareAndSet(int, long, long);
+    method public final long decrementAndGet(int);
+    method public final long get(int);
+    method public final long getAndAccumulate(int, long, java.util.function.LongBinaryOperator);
+    method public final long getAndAdd(int, long);
+    method public final long getAndDecrement(int);
+    method public final long getAndIncrement(int);
+    method public final long getAndSet(int, long);
+    method public final long getAndUpdate(int, java.util.function.LongUnaryOperator);
+    method public final long incrementAndGet(int);
+    method public final void lazySet(int, long);
+    method public final int length();
+    method public final void set(int, long);
+    method public final long updateAndGet(int, java.util.function.LongUnaryOperator);
+    method public final boolean weakCompareAndSet(int, long, long);
+  }
+
+  public abstract class AtomicLongFieldUpdater<T> {
+    ctor protected AtomicLongFieldUpdater();
+    method public final long accumulateAndGet(T, long, java.util.function.LongBinaryOperator);
+    method public long addAndGet(T, long);
+    method public abstract boolean compareAndSet(T, long, long);
+    method public long decrementAndGet(T);
+    method public abstract long get(T);
+    method public final long getAndAccumulate(T, long, java.util.function.LongBinaryOperator);
+    method public long getAndAdd(T, long);
+    method public long getAndDecrement(T);
+    method public long getAndIncrement(T);
+    method public long getAndSet(T, long);
+    method public final long getAndUpdate(T, java.util.function.LongUnaryOperator);
+    method public long incrementAndGet(T);
+    method public abstract void lazySet(T, long);
+    method public static <U> java.util.concurrent.atomic.AtomicLongFieldUpdater<U> newUpdater(Class<U>, String);
+    method public abstract void set(T, long);
+    method public final long updateAndGet(T, java.util.function.LongUnaryOperator);
+    method public abstract boolean weakCompareAndSet(T, long, long);
+  }
+
+  public class AtomicMarkableReference<V> {
+    ctor public AtomicMarkableReference(V, boolean);
+    method public boolean attemptMark(V, boolean);
+    method public boolean compareAndSet(V, V, boolean, boolean);
+    method public V get(boolean[]);
+    method public V getReference();
+    method public boolean isMarked();
+    method public void set(V, boolean);
+    method public boolean weakCompareAndSet(V, V, boolean, boolean);
+  }
+
+  public class AtomicReference<V> implements java.io.Serializable {
+    ctor public AtomicReference(V);
+    ctor public AtomicReference();
+    method public final V accumulateAndGet(V, java.util.function.BinaryOperator<V>);
+    method public final boolean compareAndSet(V, V);
+    method public final V get();
+    method public final V getAndAccumulate(V, java.util.function.BinaryOperator<V>);
+    method public final V getAndSet(V);
+    method public final V getAndUpdate(java.util.function.UnaryOperator<V>);
+    method public final void lazySet(V);
+    method public final void set(V);
+    method public final V updateAndGet(java.util.function.UnaryOperator<V>);
+    method public final boolean weakCompareAndSet(V, V);
+  }
+
+  public class AtomicReferenceArray<E> implements java.io.Serializable {
+    ctor public AtomicReferenceArray(int);
+    ctor public AtomicReferenceArray(E[]);
+    method public final E accumulateAndGet(int, E, java.util.function.BinaryOperator<E>);
+    method public final boolean compareAndSet(int, E, E);
+    method public final E get(int);
+    method public final E getAndAccumulate(int, E, java.util.function.BinaryOperator<E>);
+    method public final E getAndSet(int, E);
+    method public final E getAndUpdate(int, java.util.function.UnaryOperator<E>);
+    method public final void lazySet(int, E);
+    method public final int length();
+    method public final void set(int, E);
+    method public final E updateAndGet(int, java.util.function.UnaryOperator<E>);
+    method public final boolean weakCompareAndSet(int, E, E);
+  }
+
+  public abstract class AtomicReferenceFieldUpdater<T, V> {
+    ctor protected AtomicReferenceFieldUpdater();
+    method public final V accumulateAndGet(T, V, java.util.function.BinaryOperator<V>);
+    method public abstract boolean compareAndSet(T, V, V);
+    method public abstract V get(T);
+    method public final V getAndAccumulate(T, V, java.util.function.BinaryOperator<V>);
+    method public V getAndSet(T, V);
+    method public final V getAndUpdate(T, java.util.function.UnaryOperator<V>);
+    method public abstract void lazySet(T, V);
+    method public static <U, W> java.util.concurrent.atomic.AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U>, Class<W>, String);
+    method public abstract void set(T, V);
+    method public final V updateAndGet(T, java.util.function.UnaryOperator<V>);
+    method public abstract boolean weakCompareAndSet(T, V, V);
+  }
+
+  public class AtomicStampedReference<V> {
+    ctor public AtomicStampedReference(V, int);
+    method public boolean attemptStamp(V, int);
+    method public boolean compareAndSet(V, V, int, int);
+    method public V get(int[]);
+    method public V getReference();
+    method public int getStamp();
+    method public void set(V, int);
+    method public boolean weakCompareAndSet(V, V, int, int);
+  }
+
+  public class DoubleAccumulator extends java.lang.Number implements java.io.Serializable {
+    ctor public DoubleAccumulator(java.util.function.DoubleBinaryOperator, double);
+    method public void accumulate(double);
+    method public double doubleValue();
+    method public float floatValue();
+    method public double get();
+    method public double getThenReset();
+    method public int intValue();
+    method public long longValue();
+    method public void reset();
+  }
+
+  public class DoubleAdder extends java.lang.Number implements java.io.Serializable {
+    ctor public DoubleAdder();
+    method public void add(double);
+    method public double doubleValue();
+    method public float floatValue();
+    method public int intValue();
+    method public long longValue();
+    method public void reset();
+    method public double sum();
+    method public double sumThenReset();
+  }
+
+  public class LongAccumulator extends java.lang.Number implements java.io.Serializable {
+    ctor public LongAccumulator(java.util.function.LongBinaryOperator, long);
+    method public void accumulate(long);
+    method public double doubleValue();
+    method public float floatValue();
+    method public long get();
+    method public long getThenReset();
+    method public int intValue();
+    method public long longValue();
+    method public void reset();
+  }
+
+  public class LongAdder extends java.lang.Number implements java.io.Serializable {
+    ctor public LongAdder();
+    method public void add(long);
+    method public void decrement();
+    method public double doubleValue();
+    method public float floatValue();
+    method public void increment();
+    method public int intValue();
+    method public long longValue();
+    method public void reset();
+    method public long sum();
+    method public long sumThenReset();
+  }
+
+}
+
+package java.util.concurrent.locks {
+
+  public abstract class AbstractOwnableSynchronizer implements java.io.Serializable {
+    ctor protected AbstractOwnableSynchronizer();
+    method protected final Thread getExclusiveOwnerThread();
+    method protected final void setExclusiveOwnerThread(Thread);
+  }
+
+  public abstract class AbstractQueuedLongSynchronizer extends java.util.concurrent.locks.AbstractOwnableSynchronizer implements java.io.Serializable {
+    ctor protected AbstractQueuedLongSynchronizer();
+    method public final void acquire(long);
+    method public final void acquireInterruptibly(long) throws java.lang.InterruptedException;
+    method public final void acquireShared(long);
+    method public final void acquireSharedInterruptibly(long) throws java.lang.InterruptedException;
+    method protected final boolean compareAndSetState(long, long);
+    method public final java.util.Collection<java.lang.Thread> getExclusiveQueuedThreads();
+    method public final Thread getFirstQueuedThread();
+    method public final int getQueueLength();
+    method public final java.util.Collection<java.lang.Thread> getQueuedThreads();
+    method public final java.util.Collection<java.lang.Thread> getSharedQueuedThreads();
+    method protected final long getState();
+    method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
+    method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
+    method public final boolean hasContended();
+    method public final boolean hasQueuedPredecessors();
+    method public final boolean hasQueuedThreads();
+    method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
+    method protected boolean isHeldExclusively();
+    method public final boolean isQueued(Thread);
+    method public final boolean owns(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
+    method public final boolean release(long);
+    method public final boolean releaseShared(long);
+    method protected final void setState(long);
+    method protected boolean tryAcquire(long);
+    method public final boolean tryAcquireNanos(long, long) throws java.lang.InterruptedException;
+    method protected long tryAcquireShared(long);
+    method public final boolean tryAcquireSharedNanos(long, long) throws java.lang.InterruptedException;
+    method protected boolean tryRelease(long);
+    method protected boolean tryReleaseShared(long);
+  }
+
+  public class AbstractQueuedLongSynchronizer.ConditionObject implements java.util.concurrent.locks.Condition java.io.Serializable {
+    ctor public AbstractQueuedLongSynchronizer.ConditionObject();
+    method public final void await() throws java.lang.InterruptedException;
+    method public final boolean await(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public final long awaitNanos(long) throws java.lang.InterruptedException;
+    method public final void awaitUninterruptibly();
+    method public final boolean awaitUntil(java.util.Date) throws java.lang.InterruptedException;
+    method protected final int getWaitQueueLength();
+    method protected final java.util.Collection<java.lang.Thread> getWaitingThreads();
+    method protected final boolean hasWaiters();
+    method public final void signal();
+    method public final void signalAll();
+  }
+
+  public abstract class AbstractQueuedSynchronizer extends java.util.concurrent.locks.AbstractOwnableSynchronizer implements java.io.Serializable {
+    ctor protected AbstractQueuedSynchronizer();
+    method public final void acquire(int);
+    method public final void acquireInterruptibly(int) throws java.lang.InterruptedException;
+    method public final void acquireShared(int);
+    method public final void acquireSharedInterruptibly(int) throws java.lang.InterruptedException;
+    method protected final boolean compareAndSetState(int, int);
+    method public final java.util.Collection<java.lang.Thread> getExclusiveQueuedThreads();
+    method public final Thread getFirstQueuedThread();
+    method public final int getQueueLength();
+    method public final java.util.Collection<java.lang.Thread> getQueuedThreads();
+    method public final java.util.Collection<java.lang.Thread> getSharedQueuedThreads();
+    method protected final int getState();
+    method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
+    method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
+    method public final boolean hasContended();
+    method public final boolean hasQueuedPredecessors();
+    method public final boolean hasQueuedThreads();
+    method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
+    method protected boolean isHeldExclusively();
+    method public final boolean isQueued(Thread);
+    method public final boolean owns(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
+    method public final boolean release(int);
+    method public final boolean releaseShared(int);
+    method protected final void setState(int);
+    method protected boolean tryAcquire(int);
+    method public final boolean tryAcquireNanos(int, long) throws java.lang.InterruptedException;
+    method protected int tryAcquireShared(int);
+    method public final boolean tryAcquireSharedNanos(int, long) throws java.lang.InterruptedException;
+    method protected boolean tryRelease(int);
+    method protected boolean tryReleaseShared(int);
+  }
+
+  public class AbstractQueuedSynchronizer.ConditionObject implements java.util.concurrent.locks.Condition java.io.Serializable {
+    ctor public AbstractQueuedSynchronizer.ConditionObject();
+    method public final void await() throws java.lang.InterruptedException;
+    method public final boolean await(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public final long awaitNanos(long) throws java.lang.InterruptedException;
+    method public final void awaitUninterruptibly();
+    method public final boolean awaitUntil(java.util.Date) throws java.lang.InterruptedException;
+    method protected final int getWaitQueueLength();
+    method protected final java.util.Collection<java.lang.Thread> getWaitingThreads();
+    method protected final boolean hasWaiters();
+    method public final void signal();
+    method public final void signalAll();
+  }
+
+  public interface Condition {
+    method public void await() throws java.lang.InterruptedException;
+    method public boolean await(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public long awaitNanos(long) throws java.lang.InterruptedException;
+    method public void awaitUninterruptibly();
+    method public boolean awaitUntil(java.util.Date) throws java.lang.InterruptedException;
+    method public void signal();
+    method public void signalAll();
+  }
+
+  public interface Lock {
+    method public void lock();
+    method public void lockInterruptibly() throws java.lang.InterruptedException;
+    method public java.util.concurrent.locks.Condition newCondition();
+    method public boolean tryLock();
+    method public boolean tryLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void unlock();
+  }
+
+  public class LockSupport {
+    method public static Object getBlocker(Thread);
+    method public static void park(Object);
+    method public static void park();
+    method public static void parkNanos(Object, long);
+    method public static void parkNanos(long);
+    method public static void parkUntil(Object, long);
+    method public static void parkUntil(long);
+    method public static void unpark(Thread);
+  }
+
+  public interface ReadWriteLock {
+    method public java.util.concurrent.locks.Lock readLock();
+    method public java.util.concurrent.locks.Lock writeLock();
+  }
+
+  public class ReentrantLock implements java.util.concurrent.locks.Lock java.io.Serializable {
+    ctor public ReentrantLock();
+    ctor public ReentrantLock(boolean);
+    method public int getHoldCount();
+    method protected Thread getOwner();
+    method public final int getQueueLength();
+    method protected java.util.Collection<java.lang.Thread> getQueuedThreads();
+    method public int getWaitQueueLength(java.util.concurrent.locks.Condition);
+    method protected java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.Condition);
+    method public final boolean hasQueuedThread(Thread);
+    method public final boolean hasQueuedThreads();
+    method public boolean hasWaiters(java.util.concurrent.locks.Condition);
+    method public final boolean isFair();
+    method public boolean isHeldByCurrentThread();
+    method public boolean isLocked();
+    method public void lock();
+    method public void lockInterruptibly() throws java.lang.InterruptedException;
+    method public java.util.concurrent.locks.Condition newCondition();
+    method public boolean tryLock();
+    method public boolean tryLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void unlock();
+  }
+
+  public class ReentrantReadWriteLock implements java.util.concurrent.locks.ReadWriteLock java.io.Serializable {
+    ctor public ReentrantReadWriteLock();
+    ctor public ReentrantReadWriteLock(boolean);
+    method protected Thread getOwner();
+    method public final int getQueueLength();
+    method protected java.util.Collection<java.lang.Thread> getQueuedReaderThreads();
+    method protected java.util.Collection<java.lang.Thread> getQueuedThreads();
+    method protected java.util.Collection<java.lang.Thread> getQueuedWriterThreads();
+    method public int getReadHoldCount();
+    method public int getReadLockCount();
+    method public int getWaitQueueLength(java.util.concurrent.locks.Condition);
+    method protected java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.Condition);
+    method public int getWriteHoldCount();
+    method public final boolean hasQueuedThread(Thread);
+    method public final boolean hasQueuedThreads();
+    method public boolean hasWaiters(java.util.concurrent.locks.Condition);
+    method public final boolean isFair();
+    method public boolean isWriteLocked();
+    method public boolean isWriteLockedByCurrentThread();
+    method public java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock readLock();
+    method public java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock writeLock();
+  }
+
+  public static class ReentrantReadWriteLock.ReadLock implements java.util.concurrent.locks.Lock java.io.Serializable {
+    ctor protected ReentrantReadWriteLock.ReadLock(java.util.concurrent.locks.ReentrantReadWriteLock);
+    method public void lock();
+    method public void lockInterruptibly() throws java.lang.InterruptedException;
+    method public java.util.concurrent.locks.Condition newCondition();
+    method public boolean tryLock();
+    method public boolean tryLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void unlock();
+  }
+
+  public static class ReentrantReadWriteLock.WriteLock implements java.util.concurrent.locks.Lock java.io.Serializable {
+    ctor protected ReentrantReadWriteLock.WriteLock(java.util.concurrent.locks.ReentrantReadWriteLock);
+    method public int getHoldCount();
+    method public boolean isHeldByCurrentThread();
+    method public void lock();
+    method public void lockInterruptibly() throws java.lang.InterruptedException;
+    method public java.util.concurrent.locks.Condition newCondition();
+    method public boolean tryLock();
+    method public boolean tryLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void unlock();
+  }
+
+  public class StampedLock implements java.io.Serializable {
+    ctor public StampedLock();
+    method public java.util.concurrent.locks.Lock asReadLock();
+    method public java.util.concurrent.locks.ReadWriteLock asReadWriteLock();
+    method public java.util.concurrent.locks.Lock asWriteLock();
+    method public int getReadLockCount();
+    method public boolean isReadLocked();
+    method public boolean isWriteLocked();
+    method public long readLock();
+    method public long readLockInterruptibly() throws java.lang.InterruptedException;
+    method public long tryConvertToOptimisticRead(long);
+    method public long tryConvertToReadLock(long);
+    method public long tryConvertToWriteLock(long);
+    method public long tryOptimisticRead();
+    method public long tryReadLock();
+    method public long tryReadLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public boolean tryUnlockRead();
+    method public boolean tryUnlockWrite();
+    method public long tryWriteLock();
+    method public long tryWriteLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public void unlock(long);
+    method public void unlockRead(long);
+    method public void unlockWrite(long);
+    method public boolean validate(long);
+    method public long writeLock();
+    method public long writeLockInterruptibly() throws java.lang.InterruptedException;
+  }
+
+}
+
+package java.util.function {
+
+  @java.lang.FunctionalInterface public interface BiConsumer<T, U> {
+    method public void accept(T, U);
+    method public default java.util.function.BiConsumer<T,U> andThen(java.util.function.BiConsumer<? super T,? super U>);
+  }
+
+  @java.lang.FunctionalInterface public interface BiFunction<T, U, R> {
+    method public default <V> java.util.function.BiFunction<T,U,V> andThen(java.util.function.Function<? super R,? extends V>);
+    method public R apply(T, U);
+  }
+
+  @java.lang.FunctionalInterface public interface BiPredicate<T, U> {
+    method public default java.util.function.BiPredicate<T,U> and(java.util.function.BiPredicate<? super T,? super U>);
+    method public default java.util.function.BiPredicate<T,U> negate();
+    method public default java.util.function.BiPredicate<T,U> or(java.util.function.BiPredicate<? super T,? super U>);
+    method public boolean test(T, U);
+  }
+
+  @java.lang.FunctionalInterface public interface BinaryOperator<T> extends java.util.function.BiFunction<T,T,T> {
+    method public static <T> java.util.function.BinaryOperator<T> maxBy(java.util.Comparator<? super T>);
+    method public static <T> java.util.function.BinaryOperator<T> minBy(java.util.Comparator<? super T>);
+  }
+
+  @java.lang.FunctionalInterface public interface BooleanSupplier {
+    method public boolean getAsBoolean();
+  }
+
+  @java.lang.FunctionalInterface public interface Consumer<T> {
+    method public void accept(T);
+    method public default java.util.function.Consumer<T> andThen(java.util.function.Consumer<? super T>);
+  }
+
+  @java.lang.FunctionalInterface public interface DoubleBinaryOperator {
+    method public double applyAsDouble(double, double);
+  }
+
+  @java.lang.FunctionalInterface public interface DoubleConsumer {
+    method public void accept(double);
+    method public default java.util.function.DoubleConsumer andThen(java.util.function.DoubleConsumer);
+  }
+
+  @java.lang.FunctionalInterface public interface DoubleFunction<R> {
+    method public R apply(double);
+  }
+
+  @java.lang.FunctionalInterface public interface DoublePredicate {
+    method public default java.util.function.DoublePredicate and(java.util.function.DoublePredicate);
+    method public default java.util.function.DoublePredicate negate();
+    method public default java.util.function.DoublePredicate or(java.util.function.DoublePredicate);
+    method public boolean test(double);
+  }
+
+  @java.lang.FunctionalInterface public interface DoubleSupplier {
+    method public double getAsDouble();
+  }
+
+  @java.lang.FunctionalInterface public interface DoubleToIntFunction {
+    method public int applyAsInt(double);
+  }
+
+  @java.lang.FunctionalInterface public interface DoubleToLongFunction {
+    method public long applyAsLong(double);
+  }
+
+  @java.lang.FunctionalInterface public interface DoubleUnaryOperator {
+    method public default java.util.function.DoubleUnaryOperator andThen(java.util.function.DoubleUnaryOperator);
+    method public double applyAsDouble(double);
+    method public default java.util.function.DoubleUnaryOperator compose(java.util.function.DoubleUnaryOperator);
+    method public static java.util.function.DoubleUnaryOperator identity();
+  }
+
+  @java.lang.FunctionalInterface public interface Function<T, R> {
+    method public default <V> java.util.function.Function<T,V> andThen(java.util.function.Function<? super R,? extends V>);
+    method public R apply(T);
+    method public default <V> java.util.function.Function<V,R> compose(java.util.function.Function<? super V,? extends T>);
+    method public static <T> java.util.function.Function<T,T> identity();
+  }
+
+  @java.lang.FunctionalInterface public interface IntBinaryOperator {
+    method public int applyAsInt(int, int);
+  }
+
+  @java.lang.FunctionalInterface public interface IntConsumer {
+    method public void accept(int);
+    method public default java.util.function.IntConsumer andThen(java.util.function.IntConsumer);
+  }
+
+  @java.lang.FunctionalInterface public interface IntFunction<R> {
+    method public R apply(int);
+  }
+
+  @java.lang.FunctionalInterface public interface IntPredicate {
+    method public default java.util.function.IntPredicate and(java.util.function.IntPredicate);
+    method public default java.util.function.IntPredicate negate();
+    method public default java.util.function.IntPredicate or(java.util.function.IntPredicate);
+    method public boolean test(int);
+  }
+
+  @java.lang.FunctionalInterface public interface IntSupplier {
+    method public int getAsInt();
+  }
+
+  @java.lang.FunctionalInterface public interface IntToDoubleFunction {
+    method public double applyAsDouble(int);
+  }
+
+  @java.lang.FunctionalInterface public interface IntToLongFunction {
+    method public long applyAsLong(int);
+  }
+
+  @java.lang.FunctionalInterface public interface IntUnaryOperator {
+    method public default java.util.function.IntUnaryOperator andThen(java.util.function.IntUnaryOperator);
+    method public int applyAsInt(int);
+    method public default java.util.function.IntUnaryOperator compose(java.util.function.IntUnaryOperator);
+    method public static java.util.function.IntUnaryOperator identity();
+  }
+
+  @java.lang.FunctionalInterface public interface LongBinaryOperator {
+    method public long applyAsLong(long, long);
+  }
+
+  @java.lang.FunctionalInterface public interface LongConsumer {
+    method public void accept(long);
+    method public default java.util.function.LongConsumer andThen(java.util.function.LongConsumer);
+  }
+
+  @java.lang.FunctionalInterface public interface LongFunction<R> {
+    method public R apply(long);
+  }
+
+  @java.lang.FunctionalInterface public interface LongPredicate {
+    method public default java.util.function.LongPredicate and(java.util.function.LongPredicate);
+    method public default java.util.function.LongPredicate negate();
+    method public default java.util.function.LongPredicate or(java.util.function.LongPredicate);
+    method public boolean test(long);
+  }
+
+  @java.lang.FunctionalInterface public interface LongSupplier {
+    method public long getAsLong();
+  }
+
+  @java.lang.FunctionalInterface public interface LongToDoubleFunction {
+    method public double applyAsDouble(long);
+  }
+
+  @java.lang.FunctionalInterface public interface LongToIntFunction {
+    method public int applyAsInt(long);
+  }
+
+  @java.lang.FunctionalInterface public interface LongUnaryOperator {
+    method public default java.util.function.LongUnaryOperator andThen(java.util.function.LongUnaryOperator);
+    method public long applyAsLong(long);
+    method public default java.util.function.LongUnaryOperator compose(java.util.function.LongUnaryOperator);
+    method public static java.util.function.LongUnaryOperator identity();
+  }
+
+  @java.lang.FunctionalInterface public interface ObjDoubleConsumer<T> {
+    method public void accept(T, double);
+  }
+
+  @java.lang.FunctionalInterface public interface ObjIntConsumer<T> {
+    method public void accept(T, int);
+  }
+
+  @java.lang.FunctionalInterface public interface ObjLongConsumer<T> {
+    method public void accept(T, long);
+  }
+
+  @java.lang.FunctionalInterface public interface Predicate<T> {
+    method public default java.util.function.Predicate<T> and(java.util.function.Predicate<? super T>);
+    method public static <T> java.util.function.Predicate<T> isEqual(Object);
+    method public default java.util.function.Predicate<T> negate();
+    method public default java.util.function.Predicate<T> or(java.util.function.Predicate<? super T>);
+    method public boolean test(T);
+  }
+
+  @java.lang.FunctionalInterface public interface Supplier<T> {
+    method public T get();
+  }
+
+  @java.lang.FunctionalInterface public interface ToDoubleBiFunction<T, U> {
+    method public double applyAsDouble(T, U);
+  }
+
+  @java.lang.FunctionalInterface public interface ToDoubleFunction<T> {
+    method public double applyAsDouble(T);
+  }
+
+  @java.lang.FunctionalInterface public interface ToIntBiFunction<T, U> {
+    method public int applyAsInt(T, U);
+  }
+
+  @java.lang.FunctionalInterface public interface ToIntFunction<T> {
+    method public int applyAsInt(T);
+  }
+
+  @java.lang.FunctionalInterface public interface ToLongBiFunction<T, U> {
+    method public long applyAsLong(T, U);
+  }
+
+  @java.lang.FunctionalInterface public interface ToLongFunction<T> {
+    method public long applyAsLong(T);
+  }
+
+  @java.lang.FunctionalInterface public interface UnaryOperator<T> extends java.util.function.Function<T,T> {
+    method public static <T> java.util.function.UnaryOperator<T> identity();
+  }
+
+}
+
+package java.util.jar {
+
+  public class Attributes implements java.lang.Cloneable java.util.Map<java.lang.Object,java.lang.Object> {
+    ctor public Attributes();
+    ctor public Attributes(int);
+    ctor public Attributes(java.util.jar.Attributes);
+    method public void clear();
+    method public Object clone();
+    method public boolean containsKey(Object);
+    method public boolean containsValue(Object);
+    method public java.util.Set<java.util.Map.Entry<java.lang.Object,java.lang.Object>> entrySet();
+    method public Object get(Object);
+    method public String getValue(String);
+    method public String getValue(java.util.jar.Attributes.Name);
+    method public boolean isEmpty();
+    method public java.util.Set<java.lang.Object> keySet();
+    method public Object put(Object, Object);
+    method public void putAll(java.util.Map<?,?>);
+    method public String putValue(String, String);
+    method public Object remove(Object);
+    method public int size();
+    method public java.util.Collection<java.lang.Object> values();
+    field protected java.util.Map<java.lang.Object,java.lang.Object> map;
+  }
+
+  public static class Attributes.Name {
+    ctor public Attributes.Name(String);
+    field public static final java.util.jar.Attributes.Name CLASS_PATH;
+    field public static final java.util.jar.Attributes.Name CONTENT_TYPE;
+    field @Deprecated public static final java.util.jar.Attributes.Name EXTENSION_INSTALLATION;
+    field public static final java.util.jar.Attributes.Name EXTENSION_LIST;
+    field public static final java.util.jar.Attributes.Name EXTENSION_NAME;
+    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_TITLE;
+    field @Deprecated public static final java.util.jar.Attributes.Name IMPLEMENTATION_URL;
+    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR;
+    field @Deprecated public static final java.util.jar.Attributes.Name IMPLEMENTATION_VENDOR_ID;
+    field public static final java.util.jar.Attributes.Name IMPLEMENTATION_VERSION;
+    field public static final java.util.jar.Attributes.Name MAIN_CLASS;
+    field public static final java.util.jar.Attributes.Name MANIFEST_VERSION;
+    field public static final java.util.jar.Attributes.Name SEALED;
+    field public static final java.util.jar.Attributes.Name SIGNATURE_VERSION;
+    field public static final java.util.jar.Attributes.Name SPECIFICATION_TITLE;
+    field public static final java.util.jar.Attributes.Name SPECIFICATION_VENDOR;
+    field public static final java.util.jar.Attributes.Name SPECIFICATION_VERSION;
+  }
+
+  public class JarEntry extends java.util.zip.ZipEntry {
+    ctor public JarEntry(String);
+    ctor public JarEntry(java.util.zip.ZipEntry);
+    ctor public JarEntry(java.util.jar.JarEntry);
+    method public java.util.jar.Attributes getAttributes() throws java.io.IOException;
+    method public java.security.cert.Certificate[] getCertificates();
+    method public java.security.CodeSigner[] getCodeSigners();
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+  }
+
+  public class JarException extends java.util.zip.ZipException {
+    ctor public JarException();
+    ctor public JarException(String);
+  }
+
+  public class JarFile extends java.util.zip.ZipFile {
+    ctor public JarFile(String) throws java.io.IOException;
+    ctor public JarFile(String, boolean) throws java.io.IOException;
+    ctor public JarFile(java.io.File) throws java.io.IOException;
+    ctor public JarFile(java.io.File, boolean) throws java.io.IOException;
+    ctor public JarFile(java.io.File, boolean, int) throws java.io.IOException;
+    method public java.util.Enumeration<java.util.jar.JarEntry> entries();
+    method public java.util.jar.JarEntry getJarEntry(String);
+    method public java.util.jar.Manifest getManifest() throws java.io.IOException;
+    method public java.util.stream.Stream<java.util.jar.JarEntry> stream();
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+    field public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+  }
+
+  public class JarInputStream extends java.util.zip.ZipInputStream {
+    ctor public JarInputStream(java.io.InputStream) throws java.io.IOException;
+    ctor public JarInputStream(java.io.InputStream, boolean) throws java.io.IOException;
+    method public java.util.jar.Manifest getManifest();
+    method public java.util.jar.JarEntry getNextJarEntry() throws java.io.IOException;
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+  }
+
+  public class JarOutputStream extends java.util.zip.ZipOutputStream {
+    ctor public JarOutputStream(java.io.OutputStream, java.util.jar.Manifest) throws java.io.IOException;
+    ctor public JarOutputStream(java.io.OutputStream) throws java.io.IOException;
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+  }
+
+  public class Manifest implements java.lang.Cloneable {
+    ctor public Manifest();
+    ctor public Manifest(java.io.InputStream) throws java.io.IOException;
+    ctor public Manifest(java.util.jar.Manifest);
+    method public void clear();
+    method public Object clone();
+    method public java.util.jar.Attributes getAttributes(String);
+    method public java.util.Map<java.lang.String,java.util.jar.Attributes> getEntries();
+    method public java.util.jar.Attributes getMainAttributes();
+    method public void read(java.io.InputStream) throws java.io.IOException;
+    method public void write(java.io.OutputStream) throws java.io.IOException;
+  }
+
+  public abstract class Pack200 {
+    method public static java.util.jar.Pack200.Packer newPacker();
+    method public static java.util.jar.Pack200.Unpacker newUnpacker();
+  }
+
+  public static interface Pack200.Packer {
+    method @Deprecated public default void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public void pack(java.util.jar.JarFile, java.io.OutputStream) throws java.io.IOException;
+    method public void pack(java.util.jar.JarInputStream, java.io.OutputStream) throws java.io.IOException;
+    method public java.util.SortedMap<java.lang.String,java.lang.String> properties();
+    method @Deprecated public default void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    field public static final String CLASS_ATTRIBUTE_PFX = "pack.class.attribute.";
+    field public static final String CODE_ATTRIBUTE_PFX = "pack.code.attribute.";
+    field public static final String DEFLATE_HINT = "pack.deflate.hint";
+    field public static final String EFFORT = "pack.effort";
+    field public static final String ERROR = "error";
+    field public static final String FALSE = "false";
+    field public static final String FIELD_ATTRIBUTE_PFX = "pack.field.attribute.";
+    field public static final String KEEP = "keep";
+    field public static final String KEEP_FILE_ORDER = "pack.keep.file.order";
+    field public static final String LATEST = "latest";
+    field public static final String METHOD_ATTRIBUTE_PFX = "pack.method.attribute.";
+    field public static final String MODIFICATION_TIME = "pack.modification.time";
+    field public static final String PASS = "pass";
+    field public static final String PASS_FILE_PFX = "pack.pass.file.";
+    field public static final String PROGRESS = "pack.progress";
+    field public static final String SEGMENT_LIMIT = "pack.segment.limit";
+    field public static final String STRIP = "strip";
+    field public static final String TRUE = "true";
+    field public static final String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute";
+  }
+
+  public static interface Pack200.Unpacker {
+    method @Deprecated public default void addPropertyChangeListener(java.beans.PropertyChangeListener);
+    method public java.util.SortedMap<java.lang.String,java.lang.String> properties();
+    method @Deprecated public default void removePropertyChangeListener(java.beans.PropertyChangeListener);
+    method public void unpack(java.io.InputStream, java.util.jar.JarOutputStream) throws java.io.IOException;
+    method public void unpack(java.io.File, java.util.jar.JarOutputStream) throws java.io.IOException;
+    field public static final String DEFLATE_HINT = "unpack.deflate.hint";
+    field public static final String FALSE = "false";
+    field public static final String KEEP = "keep";
+    field public static final String PROGRESS = "unpack.progress";
+    field public static final String TRUE = "true";
+  }
+
+}
+
+package java.util.logging {
+
+  public class ConsoleHandler extends java.util.logging.StreamHandler {
+    ctor public ConsoleHandler();
+    method public void close();
+  }
+
+  public class ErrorManager {
+    ctor public ErrorManager();
+    method public void error(String, Exception, int);
+    field public static final int CLOSE_FAILURE = 3; // 0x3
+    field public static final int FLUSH_FAILURE = 2; // 0x2
+    field public static final int FORMAT_FAILURE = 5; // 0x5
+    field public static final int GENERIC_FAILURE = 0; // 0x0
+    field public static final int OPEN_FAILURE = 4; // 0x4
+    field public static final int WRITE_FAILURE = 1; // 0x1
+  }
+
+  public class FileHandler extends java.util.logging.StreamHandler {
+    ctor public FileHandler() throws java.io.IOException, java.lang.SecurityException;
+    ctor public FileHandler(String) throws java.io.IOException, java.lang.SecurityException;
+    ctor public FileHandler(String, boolean) throws java.io.IOException, java.lang.SecurityException;
+    ctor public FileHandler(String, int, int) throws java.io.IOException, java.lang.SecurityException;
+    ctor public FileHandler(String, int, int, boolean) throws java.io.IOException, java.lang.SecurityException;
+  }
+
+  @java.lang.FunctionalInterface public interface Filter {
+    method public boolean isLoggable(java.util.logging.LogRecord);
+  }
+
+  public abstract class Formatter {
+    ctor protected Formatter();
+    method public abstract String format(java.util.logging.LogRecord);
+    method public String formatMessage(java.util.logging.LogRecord);
+    method public String getHead(java.util.logging.Handler);
+    method public String getTail(java.util.logging.Handler);
+  }
+
+  public abstract class Handler {
+    ctor protected Handler();
+    method public abstract void close() throws java.lang.SecurityException;
+    method public abstract void flush();
+    method public String getEncoding();
+    method public java.util.logging.ErrorManager getErrorManager();
+    method public java.util.logging.Filter getFilter();
+    method public java.util.logging.Formatter getFormatter();
+    method public java.util.logging.Level getLevel();
+    method public boolean isLoggable(java.util.logging.LogRecord);
+    method public abstract void publish(java.util.logging.LogRecord);
+    method protected void reportError(String, Exception, int);
+    method public void setEncoding(String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
+    method public void setErrorManager(java.util.logging.ErrorManager);
+    method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+    method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+    method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
+  }
+
+  public class Level implements java.io.Serializable {
+    ctor protected Level(@NonNull String, int);
+    ctor protected Level(@NonNull String, int, @Nullable String);
+    method @NonNull public String getLocalizedName();
+    method @NonNull public String getName();
+    method @Nullable public String getResourceBundleName();
+    method public final int intValue();
+    method @NonNull public static java.util.logging.Level parse(@NonNull String) throws java.lang.IllegalArgumentException;
+    method @NonNull public final String toString();
+    field @NonNull public static final java.util.logging.Level ALL;
+    field @NonNull public static final java.util.logging.Level CONFIG;
+    field @NonNull public static final java.util.logging.Level FINE;
+    field @NonNull public static final java.util.logging.Level FINER;
+    field @NonNull public static final java.util.logging.Level FINEST;
+    field @NonNull public static final java.util.logging.Level INFO;
+    field @NonNull public static final java.util.logging.Level OFF;
+    field @NonNull public static final java.util.logging.Level SEVERE;
+    field @NonNull public static final java.util.logging.Level WARNING;
+  }
+
+  public class LogManager {
+    ctor protected LogManager();
+    method public boolean addLogger(java.util.logging.Logger);
+    method @Deprecated public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public void checkAccess() throws java.lang.SecurityException;
+    method public static java.util.logging.LogManager getLogManager();
+    method public java.util.logging.Logger getLogger(String);
+    method public java.util.Enumeration<java.lang.String> getLoggerNames();
+    method public static java.util.logging.LoggingMXBean getLoggingMXBean();
+    method public String getProperty(String);
+    method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
+    method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
+    method @Deprecated public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+    method public void reset() throws java.lang.SecurityException;
+    field public static final String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
+  }
+
+  public class LogRecord implements java.io.Serializable {
+    ctor public LogRecord(java.util.logging.Level, String);
+    method public java.util.logging.Level getLevel();
+    method public String getLoggerName();
+    method public String getMessage();
+    method public long getMillis();
+    method public Object[] getParameters();
+    method public java.util.ResourceBundle getResourceBundle();
+    method public String getResourceBundleName();
+    method public long getSequenceNumber();
+    method public String getSourceClassName();
+    method public String getSourceMethodName();
+    method public int getThreadID();
+    method public Throwable getThrown();
+    method public void setLevel(java.util.logging.Level);
+    method public void setLoggerName(String);
+    method public void setMessage(String);
+    method public void setMillis(long);
+    method public void setParameters(Object[]);
+    method public void setResourceBundle(java.util.ResourceBundle);
+    method public void setResourceBundleName(String);
+    method public void setSequenceNumber(long);
+    method public void setSourceClassName(String);
+    method public void setSourceMethodName(String);
+    method public void setThreadID(int);
+    method public void setThrown(Throwable);
+  }
+
+  public class Logger {
+    ctor protected Logger(@Nullable String, @Nullable String);
+    method public void addHandler(@NonNull java.util.logging.Handler) throws java.lang.SecurityException;
+    method public void config(@Nullable String);
+    method public void config(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public void entering(@Nullable String, @Nullable String);
+    method public void entering(@Nullable String, @Nullable String, @Nullable Object);
+    method public void entering(@Nullable String, @Nullable String, @Nullable Object[]);
+    method public void exiting(@Nullable String, @Nullable String);
+    method public void exiting(@Nullable String, @Nullable String, @Nullable Object);
+    method public void fine(@Nullable String);
+    method public void fine(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public void finer(@Nullable String);
+    method public void finer(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public void finest(@Nullable String);
+    method public void finest(@NonNull java.util.function.Supplier<java.lang.String>);
+    method @NonNull public static java.util.logging.Logger getAnonymousLogger();
+    method @NonNull public static java.util.logging.Logger getAnonymousLogger(@Nullable String);
+    method @Nullable public java.util.logging.Filter getFilter();
+    method @NonNull public static final java.util.logging.Logger getGlobal();
+    method @NonNull public java.util.logging.Handler[] getHandlers();
+    method @Nullable public java.util.logging.Level getLevel();
+    method @NonNull public static java.util.logging.Logger getLogger(@NonNull String);
+    method @NonNull public static java.util.logging.Logger getLogger(@NonNull String, @Nullable String);
+    method @Nullable public String getName();
+    method @Nullable public java.util.logging.Logger getParent();
+    method @Nullable public java.util.ResourceBundle getResourceBundle();
+    method @Nullable public String getResourceBundleName();
+    method public boolean getUseParentHandlers();
+    method public void info(@Nullable String);
+    method public void info(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public boolean isLoggable(@NonNull java.util.logging.Level);
+    method public void log(@NonNull java.util.logging.LogRecord);
+    method public void log(@NonNull java.util.logging.Level, @Nullable String);
+    method public void log(@NonNull java.util.logging.Level, @NonNull java.util.function.Supplier<java.lang.String>);
+    method public void log(@NonNull java.util.logging.Level, @Nullable String, @Nullable Object);
+    method public void log(@NonNull java.util.logging.Level, @Nullable String, @Nullable Object[]);
+    method public void log(@NonNull java.util.logging.Level, @Nullable String, @Nullable Throwable);
+    method public void log(@NonNull java.util.logging.Level, @Nullable Throwable, @NonNull java.util.function.Supplier<java.lang.String>);
+    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String);
+    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @NonNull java.util.function.Supplier<java.lang.String>);
+    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object);
+    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Object[]);
+    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable Throwable);
+    method public void logp(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable Throwable, @NonNull java.util.function.Supplier<java.lang.String>);
+    method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String);
+    method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable Object);
+    method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable Object[]);
+    method public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable java.util.ResourceBundle, @Nullable String, @Nullable java.lang.Object...);
+    method @Deprecated public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable String, @Nullable String, @Nullable Throwable);
+    method public void logrb(@NonNull java.util.logging.Level, @Nullable String, @Nullable String, @Nullable java.util.ResourceBundle, @Nullable String, @Nullable Throwable);
+    method public void removeHandler(@Nullable java.util.logging.Handler) throws java.lang.SecurityException;
+    method public void setFilter(@Nullable java.util.logging.Filter) throws java.lang.SecurityException;
+    method public void setLevel(@Nullable java.util.logging.Level) throws java.lang.SecurityException;
+    method public void setParent(@NonNull java.util.logging.Logger);
+    method public void setResourceBundle(@NonNull java.util.ResourceBundle);
+    method public void setUseParentHandlers(boolean);
+    method public void severe(@Nullable String);
+    method public void severe(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public void throwing(@Nullable String, @Nullable String, @Nullable Throwable);
+    method public void warning(@Nullable String);
+    method public void warning(@NonNull java.util.function.Supplier<java.lang.String>);
+    field @NonNull public static final String GLOBAL_LOGGER_NAME = "global";
+    field @Deprecated @NonNull public static final java.util.logging.Logger global;
+  }
+
+  public interface LoggingMXBean {
+    method public String getLoggerLevel(String);
+    method public java.util.List<java.lang.String> getLoggerNames();
+    method public String getParentLoggerName(String);
+    method public void setLoggerLevel(String, String);
+  }
+
+  public final class LoggingPermission extends java.security.BasicPermission {
+    ctor public LoggingPermission(String, String) throws java.lang.IllegalArgumentException;
+  }
+
+  public class MemoryHandler extends java.util.logging.Handler {
+    ctor public MemoryHandler();
+    ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
+    method public void close() throws java.lang.SecurityException;
+    method public void flush();
+    method public java.util.logging.Level getPushLevel();
+    method public void publish(java.util.logging.LogRecord);
+    method public void push();
+    method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
+  }
+
+  public class SimpleFormatter extends java.util.logging.Formatter {
+    ctor public SimpleFormatter();
+    method public String format(java.util.logging.LogRecord);
+  }
+
+  public class SocketHandler extends java.util.logging.StreamHandler {
+    ctor public SocketHandler() throws java.io.IOException;
+    ctor public SocketHandler(String, int) throws java.io.IOException;
+  }
+
+  public class StreamHandler extends java.util.logging.Handler {
+    ctor public StreamHandler();
+    ctor public StreamHandler(java.io.OutputStream, java.util.logging.Formatter);
+    method public void close() throws java.lang.SecurityException;
+    method public void flush();
+    method public void publish(java.util.logging.LogRecord);
+    method protected void setOutputStream(java.io.OutputStream) throws java.lang.SecurityException;
+  }
+
+  public class XMLFormatter extends java.util.logging.Formatter {
+    ctor public XMLFormatter();
+    method public String format(java.util.logging.LogRecord);
+  }
+
+}
+
+package java.util.prefs {
+
+  public abstract class AbstractPreferences extends java.util.prefs.Preferences {
+    ctor protected AbstractPreferences(java.util.prefs.AbstractPreferences, String);
+    method public String absolutePath();
+    method public void addNodeChangeListener(java.util.prefs.NodeChangeListener);
+    method public void addPreferenceChangeListener(java.util.prefs.PreferenceChangeListener);
+    method protected final java.util.prefs.AbstractPreferences[] cachedChildren();
+    method protected abstract java.util.prefs.AbstractPreferences childSpi(String);
+    method public String[] childrenNames() throws java.util.prefs.BackingStoreException;
+    method protected abstract String[] childrenNamesSpi() throws java.util.prefs.BackingStoreException;
+    method public void clear() throws java.util.prefs.BackingStoreException;
+    method public void exportNode(java.io.OutputStream) throws java.util.prefs.BackingStoreException, java.io.IOException;
+    method public void exportSubtree(java.io.OutputStream) throws java.util.prefs.BackingStoreException, java.io.IOException;
+    method public void flush() throws java.util.prefs.BackingStoreException;
+    method protected abstract void flushSpi() throws java.util.prefs.BackingStoreException;
+    method public String get(String, String);
+    method public boolean getBoolean(String, boolean);
+    method public byte[] getByteArray(String, byte[]);
+    method protected java.util.prefs.AbstractPreferences getChild(String) throws java.util.prefs.BackingStoreException;
+    method public double getDouble(String, double);
+    method public float getFloat(String, float);
+    method public int getInt(String, int);
+    method public long getLong(String, long);
+    method protected abstract String getSpi(String);
+    method protected boolean isRemoved();
+    method public boolean isUserNode();
+    method public String[] keys() throws java.util.prefs.BackingStoreException;
+    method protected abstract String[] keysSpi() throws java.util.prefs.BackingStoreException;
+    method public String name();
+    method public java.util.prefs.Preferences node(String);
+    method public boolean nodeExists(String) throws java.util.prefs.BackingStoreException;
+    method public java.util.prefs.Preferences parent();
+    method public void put(String, String);
+    method public void putBoolean(String, boolean);
+    method public void putByteArray(String, byte[]);
+    method public void putDouble(String, double);
+    method public void putFloat(String, float);
+    method public void putInt(String, int);
+    method public void putLong(String, long);
+    method protected abstract void putSpi(String, String);
+    method public void remove(String);
+    method public void removeNode() throws java.util.prefs.BackingStoreException;
+    method public void removeNodeChangeListener(java.util.prefs.NodeChangeListener);
+    method protected abstract void removeNodeSpi() throws java.util.prefs.BackingStoreException;
+    method public void removePreferenceChangeListener(java.util.prefs.PreferenceChangeListener);
+    method protected abstract void removeSpi(String);
+    method public void sync() throws java.util.prefs.BackingStoreException;
+    method protected abstract void syncSpi() throws java.util.prefs.BackingStoreException;
+    field protected final Object lock;
+    field protected boolean newNode;
+  }
+
+  public class BackingStoreException extends java.lang.Exception {
+    ctor public BackingStoreException(String);
+    ctor public BackingStoreException(Throwable);
+  }
+
+  public class InvalidPreferencesFormatException extends java.lang.Exception {
+    ctor public InvalidPreferencesFormatException(Throwable);
+    ctor public InvalidPreferencesFormatException(String);
+    ctor public InvalidPreferencesFormatException(String, Throwable);
+  }
+
+  public class NodeChangeEvent extends java.util.EventObject {
+    ctor public NodeChangeEvent(java.util.prefs.Preferences, java.util.prefs.Preferences);
+    method public java.util.prefs.Preferences getChild();
+    method public java.util.prefs.Preferences getParent();
+  }
+
+  public interface NodeChangeListener extends java.util.EventListener {
+    method public void childAdded(java.util.prefs.NodeChangeEvent);
+    method public void childRemoved(java.util.prefs.NodeChangeEvent);
+  }
+
+  public class PreferenceChangeEvent extends java.util.EventObject {
+    ctor public PreferenceChangeEvent(java.util.prefs.Preferences, String, String);
+    method public String getKey();
+    method public String getNewValue();
+    method public java.util.prefs.Preferences getNode();
+  }
+
+  @java.lang.FunctionalInterface public interface PreferenceChangeListener extends java.util.EventListener {
+    method public void preferenceChange(java.util.prefs.PreferenceChangeEvent);
+  }
+
+  public abstract class Preferences {
+    ctor protected Preferences();
+    method public abstract String absolutePath();
+    method public abstract void addNodeChangeListener(java.util.prefs.NodeChangeListener);
+    method public abstract void addPreferenceChangeListener(java.util.prefs.PreferenceChangeListener);
+    method public abstract String[] childrenNames() throws java.util.prefs.BackingStoreException;
+    method public abstract void clear() throws java.util.prefs.BackingStoreException;
+    method public abstract void exportNode(java.io.OutputStream) throws java.util.prefs.BackingStoreException, java.io.IOException;
+    method public abstract void exportSubtree(java.io.OutputStream) throws java.util.prefs.BackingStoreException, java.io.IOException;
+    method public abstract void flush() throws java.util.prefs.BackingStoreException;
+    method public abstract String get(String, String);
+    method public abstract boolean getBoolean(String, boolean);
+    method public abstract byte[] getByteArray(String, byte[]);
+    method public abstract double getDouble(String, double);
+    method public abstract float getFloat(String, float);
+    method public abstract int getInt(String, int);
+    method public abstract long getLong(String, long);
+    method public static void importPreferences(java.io.InputStream) throws java.io.IOException, java.util.prefs.InvalidPreferencesFormatException;
+    method public abstract boolean isUserNode();
+    method public abstract String[] keys() throws java.util.prefs.BackingStoreException;
+    method public abstract String name();
+    method public abstract java.util.prefs.Preferences node(String);
+    method public abstract boolean nodeExists(String) throws java.util.prefs.BackingStoreException;
+    method public abstract java.util.prefs.Preferences parent();
+    method public abstract void put(String, String);
+    method public abstract void putBoolean(String, boolean);
+    method public abstract void putByteArray(String, byte[]);
+    method public abstract void putDouble(String, double);
+    method public abstract void putFloat(String, float);
+    method public abstract void putInt(String, int);
+    method public abstract void putLong(String, long);
+    method public abstract void remove(String);
+    method public abstract void removeNode() throws java.util.prefs.BackingStoreException;
+    method public abstract void removeNodeChangeListener(java.util.prefs.NodeChangeListener);
+    method public abstract void removePreferenceChangeListener(java.util.prefs.PreferenceChangeListener);
+    method public abstract void sync() throws java.util.prefs.BackingStoreException;
+    method public static java.util.prefs.Preferences systemNodeForPackage(Class<?>);
+    method public static java.util.prefs.Preferences systemRoot();
+    method public abstract String toString();
+    method public static java.util.prefs.Preferences userNodeForPackage(Class<?>);
+    method public static java.util.prefs.Preferences userRoot();
+    field public static final int MAX_KEY_LENGTH = 80; // 0x50
+    field public static final int MAX_NAME_LENGTH = 80; // 0x50
+    field public static final int MAX_VALUE_LENGTH = 8192; // 0x2000
+  }
+
+  public interface PreferencesFactory {
+    method public java.util.prefs.Preferences systemRoot();
+    method public java.util.prefs.Preferences userRoot();
+  }
+
+}
+
+package java.util.regex {
+
+  public interface MatchResult {
+    method public int end();
+    method public int end(int);
+    method public String group();
+    method public String group(int);
+    method public int groupCount();
+    method public int start();
+    method public int start(int);
+  }
+
+  public final class Matcher implements java.util.regex.MatchResult {
+    method @NonNull public java.util.regex.Matcher appendReplacement(@NonNull StringBuffer, @NonNull String);
+    method @NonNull public StringBuffer appendTail(@NonNull StringBuffer);
+    method public int end();
+    method public int end(int);
+    method public int end(@NonNull String);
+    method public boolean find();
+    method public boolean find(int);
+    method @NonNull public String group();
+    method @Nullable public String group(int);
+    method @Nullable public String group(@NonNull String);
+    method public int groupCount();
+    method public boolean hasAnchoringBounds();
+    method public boolean hasTransparentBounds();
+    method public boolean hitEnd();
+    method public boolean lookingAt();
+    method public boolean matches();
+    method @NonNull public java.util.regex.Pattern pattern();
+    method @NonNull public static String quoteReplacement(@NonNull String);
+    method @NonNull public java.util.regex.Matcher region(int, int);
+    method public int regionEnd();
+    method public int regionStart();
+    method @NonNull public String replaceAll(@NonNull String);
+    method @NonNull public String replaceFirst(@NonNull String);
+    method public boolean requireEnd();
+    method @NonNull public java.util.regex.Matcher reset();
+    method @NonNull public java.util.regex.Matcher reset(@NonNull CharSequence);
+    method public int start();
+    method public int start(int);
+    method public int start(@NonNull String);
+    method @NonNull public java.util.regex.MatchResult toMatchResult();
+    method @NonNull public java.util.regex.Matcher useAnchoringBounds(boolean);
+    method @NonNull public java.util.regex.Matcher usePattern(@NonNull java.util.regex.Pattern);
+    method @NonNull public java.util.regex.Matcher useTransparentBounds(boolean);
+  }
+
+  public final class Pattern implements java.io.Serializable {
+    method @NonNull public java.util.function.Predicate<java.lang.String> asPredicate();
+    method @NonNull public static java.util.regex.Pattern compile(@NonNull String);
+    method @NonNull public static java.util.regex.Pattern compile(@NonNull String, int);
+    method public int flags();
+    method @NonNull public java.util.regex.Matcher matcher(@NonNull CharSequence);
+    method public static boolean matches(@NonNull String, @NonNull CharSequence);
+    method @NonNull public String pattern();
+    method @NonNull public static String quote(@NonNull String);
+    method @NonNull public String[] split(@NonNull CharSequence, int);
+    method @NonNull public String[] split(@NonNull CharSequence);
+    method @NonNull public java.util.stream.Stream<java.lang.String> splitAsStream(@NonNull CharSequence);
+    field public static final int CANON_EQ = 128; // 0x80
+    field public static final int CASE_INSENSITIVE = 2; // 0x2
+    field public static final int COMMENTS = 4; // 0x4
+    field public static final int DOTALL = 32; // 0x20
+    field public static final int LITERAL = 16; // 0x10
+    field public static final int MULTILINE = 8; // 0x8
+    field public static final int UNICODE_CASE = 64; // 0x40
+    field public static final int UNICODE_CHARACTER_CLASS = 256; // 0x100
+    field public static final int UNIX_LINES = 1; // 0x1
+  }
+
+  public class PatternSyntaxException extends java.lang.IllegalArgumentException {
+    ctor public PatternSyntaxException(String, String, int);
+    method public String getDescription();
+    method public int getIndex();
+    method public String getPattern();
+  }
+
+}
+
+package java.util.stream {
+
+  public interface BaseStream<T, S extends java.util.stream.BaseStream<T, S>> extends java.lang.AutoCloseable {
+    method public void close();
+    method public boolean isParallel();
+    method public java.util.Iterator<T> iterator();
+    method public S onClose(Runnable);
+    method public S parallel();
+    method public S sequential();
+    method public java.util.Spliterator<T> spliterator();
+    method public S unordered();
+  }
+
+  public interface Collector<T, A, R> {
+    method public java.util.function.BiConsumer<A,T> accumulator();
+    method public java.util.Set<java.util.stream.Collector.Characteristics> characteristics();
+    method public java.util.function.BinaryOperator<A> combiner();
+    method public java.util.function.Function<A,R> finisher();
+    method public static <T, R> java.util.stream.Collector<T,R,R> of(java.util.function.Supplier<R>, java.util.function.BiConsumer<R,T>, java.util.function.BinaryOperator<R>, java.util.stream.Collector.Characteristics...);
+    method public static <T, A, R> java.util.stream.Collector<T,A,R> of(java.util.function.Supplier<A>, java.util.function.BiConsumer<A,T>, java.util.function.BinaryOperator<A>, java.util.function.Function<A,R>, java.util.stream.Collector.Characteristics...);
+    method public java.util.function.Supplier<A> supplier();
+  }
+
+  public enum Collector.Characteristics {
+    enum_constant public static final java.util.stream.Collector.Characteristics CONCURRENT;
+    enum_constant public static final java.util.stream.Collector.Characteristics IDENTITY_FINISH;
+    enum_constant public static final java.util.stream.Collector.Characteristics UNORDERED;
+  }
+
+  public final class Collectors {
+    method public static <T> java.util.stream.Collector<T,?,java.lang.Double> averagingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.lang.Double> averagingInt(java.util.function.ToIntFunction<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.lang.Double> averagingLong(java.util.function.ToLongFunction<? super T>);
+    method public static <T, A, R, RR> java.util.stream.Collector<T,A,RR> collectingAndThen(java.util.stream.Collector<T,A,R>, java.util.function.Function<R,RR>);
+    method public static <T> java.util.stream.Collector<T,?,java.lang.Long> counting();
+    method public static <T, K> java.util.stream.Collector<T,?,java.util.Map<K,java.util.List<T>>> groupingBy(java.util.function.Function<? super T,? extends K>);
+    method public static <T, K, A, D> java.util.stream.Collector<T,?,java.util.Map<K,D>> groupingBy(java.util.function.Function<? super T,? extends K>, java.util.stream.Collector<? super T,A,D>);
+    method public static <T, K, D, A, M extends java.util.Map<K, D>> java.util.stream.Collector<T,?,M> groupingBy(java.util.function.Function<? super T,? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T,A,D>);
+    method public static <T, K> java.util.stream.Collector<T,?,java.util.concurrent.ConcurrentMap<K,java.util.List<T>>> groupingByConcurrent(java.util.function.Function<? super T,? extends K>);
+    method public static <T, K, A, D> java.util.stream.Collector<T,?,java.util.concurrent.ConcurrentMap<K,D>> groupingByConcurrent(java.util.function.Function<? super T,? extends K>, java.util.stream.Collector<? super T,A,D>);
+    method public static <T, K, A, D, M extends java.util.concurrent.ConcurrentMap<K, D>> java.util.stream.Collector<T,?,M> groupingByConcurrent(java.util.function.Function<? super T,? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T,A,D>);
+    method public static java.util.stream.Collector<java.lang.CharSequence,?,java.lang.String> joining();
+    method public static java.util.stream.Collector<java.lang.CharSequence,?,java.lang.String> joining(CharSequence);
+    method public static java.util.stream.Collector<java.lang.CharSequence,?,java.lang.String> joining(CharSequence, CharSequence, CharSequence);
+    method public static <T, U, A, R> java.util.stream.Collector<T,?,R> mapping(java.util.function.Function<? super T,? extends U>, java.util.stream.Collector<? super U,A,R>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.Optional<T>> maxBy(java.util.Comparator<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.Optional<T>> minBy(java.util.Comparator<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.Map<java.lang.Boolean,java.util.List<T>>> partitioningBy(java.util.function.Predicate<? super T>);
+    method public static <T, D, A> java.util.stream.Collector<T,?,java.util.Map<java.lang.Boolean,D>> partitioningBy(java.util.function.Predicate<? super T>, java.util.stream.Collector<? super T,A,D>);
+    method public static <T> java.util.stream.Collector<T,?,T> reducing(T, java.util.function.BinaryOperator<T>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.Optional<T>> reducing(java.util.function.BinaryOperator<T>);
+    method public static <T, U> java.util.stream.Collector<T,?,U> reducing(U, java.util.function.Function<? super T,? extends U>, java.util.function.BinaryOperator<U>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.DoubleSummaryStatistics> summarizingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.IntSummaryStatistics> summarizingInt(java.util.function.ToIntFunction<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.LongSummaryStatistics> summarizingLong(java.util.function.ToLongFunction<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.lang.Double> summingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.lang.Integer> summingInt(java.util.function.ToIntFunction<? super T>);
+    method public static <T> java.util.stream.Collector<T,?,java.lang.Long> summingLong(java.util.function.ToLongFunction<? super T>);
+    method public static <T, C extends java.util.Collection<T>> java.util.stream.Collector<T,?,C> toCollection(java.util.function.Supplier<C>);
+    method public static <T, K, U> java.util.stream.Collector<T,?,java.util.concurrent.ConcurrentMap<K,U>> toConcurrentMap(java.util.function.Function<? super T,? extends K>, java.util.function.Function<? super T,? extends U>);
+    method public static <T, K, U> java.util.stream.Collector<T,?,java.util.concurrent.ConcurrentMap<K,U>> toConcurrentMap(java.util.function.Function<? super T,? extends K>, java.util.function.Function<? super T,? extends U>, java.util.function.BinaryOperator<U>);
+    method public static <T, K, U, M extends java.util.concurrent.ConcurrentMap<K, U>> java.util.stream.Collector<T,?,M> toConcurrentMap(java.util.function.Function<? super T,? extends K>, java.util.function.Function<? super T,? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.List<T>> toList();
+    method public static <T, K, U> java.util.stream.Collector<T,?,java.util.Map<K,U>> toMap(java.util.function.Function<? super T,? extends K>, java.util.function.Function<? super T,? extends U>);
+    method public static <T, K, U> java.util.stream.Collector<T,?,java.util.Map<K,U>> toMap(java.util.function.Function<? super T,? extends K>, java.util.function.Function<? super T,? extends U>, java.util.function.BinaryOperator<U>);
+    method public static <T, K, U, M extends java.util.Map<K, U>> java.util.stream.Collector<T,?,M> toMap(java.util.function.Function<? super T,? extends K>, java.util.function.Function<? super T,? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>);
+    method public static <T> java.util.stream.Collector<T,?,java.util.Set<T>> toSet();
+  }
+
+  public interface DoubleStream extends java.util.stream.BaseStream<java.lang.Double,java.util.stream.DoubleStream> {
+    method public boolean allMatch(java.util.function.DoublePredicate);
+    method public boolean anyMatch(java.util.function.DoublePredicate);
+    method public java.util.OptionalDouble average();
+    method public java.util.stream.Stream<java.lang.Double> boxed();
+    method public static java.util.stream.DoubleStream.Builder builder();
+    method public <R> R collect(java.util.function.Supplier<R>, java.util.function.ObjDoubleConsumer<R>, java.util.function.BiConsumer<R,R>);
+    method public static java.util.stream.DoubleStream concat(java.util.stream.DoubleStream, java.util.stream.DoubleStream);
+    method public long count();
+    method public java.util.stream.DoubleStream distinct();
+    method public static java.util.stream.DoubleStream empty();
+    method public java.util.stream.DoubleStream filter(java.util.function.DoublePredicate);
+    method public java.util.OptionalDouble findAny();
+    method public java.util.OptionalDouble findFirst();
+    method public java.util.stream.DoubleStream flatMap(java.util.function.DoubleFunction<? extends java.util.stream.DoubleStream>);
+    method public void forEach(java.util.function.DoubleConsumer);
+    method public void forEachOrdered(java.util.function.DoubleConsumer);
+    method public static java.util.stream.DoubleStream generate(java.util.function.DoubleSupplier);
+    method public static java.util.stream.DoubleStream iterate(double, java.util.function.DoubleUnaryOperator);
+    method public java.util.PrimitiveIterator.OfDouble iterator();
+    method public java.util.stream.DoubleStream limit(long);
+    method public java.util.stream.DoubleStream map(java.util.function.DoubleUnaryOperator);
+    method public java.util.stream.IntStream mapToInt(java.util.function.DoubleToIntFunction);
+    method public java.util.stream.LongStream mapToLong(java.util.function.DoubleToLongFunction);
+    method public <U> java.util.stream.Stream<U> mapToObj(java.util.function.DoubleFunction<? extends U>);
+    method public java.util.OptionalDouble max();
+    method public java.util.OptionalDouble min();
+    method public boolean noneMatch(java.util.function.DoublePredicate);
+    method public static java.util.stream.DoubleStream of(double);
+    method public static java.util.stream.DoubleStream of(double...);
+    method public java.util.stream.DoubleStream parallel();
+    method public java.util.stream.DoubleStream peek(java.util.function.DoubleConsumer);
+    method public double reduce(double, java.util.function.DoubleBinaryOperator);
+    method public java.util.OptionalDouble reduce(java.util.function.DoubleBinaryOperator);
+    method public java.util.stream.DoubleStream sequential();
+    method public java.util.stream.DoubleStream skip(long);
+    method public java.util.stream.DoubleStream sorted();
+    method public java.util.Spliterator.OfDouble spliterator();
+    method public double sum();
+    method public java.util.DoubleSummaryStatistics summaryStatistics();
+    method public double[] toArray();
+  }
+
+  public static interface DoubleStream.Builder extends java.util.function.DoubleConsumer {
+    method public default java.util.stream.DoubleStream.Builder add(double);
+    method public java.util.stream.DoubleStream build();
+  }
+
+  public interface IntStream extends java.util.stream.BaseStream<java.lang.Integer,java.util.stream.IntStream> {
+    method public boolean allMatch(java.util.function.IntPredicate);
+    method public boolean anyMatch(java.util.function.IntPredicate);
+    method public java.util.stream.DoubleStream asDoubleStream();
+    method public java.util.stream.LongStream asLongStream();
+    method public java.util.OptionalDouble average();
+    method public java.util.stream.Stream<java.lang.Integer> boxed();
+    method public static java.util.stream.IntStream.Builder builder();
+    method public <R> R collect(java.util.function.Supplier<R>, java.util.function.ObjIntConsumer<R>, java.util.function.BiConsumer<R,R>);
+    method public static java.util.stream.IntStream concat(java.util.stream.IntStream, java.util.stream.IntStream);
+    method public long count();
+    method public java.util.stream.IntStream distinct();
+    method public static java.util.stream.IntStream empty();
+    method public java.util.stream.IntStream filter(java.util.function.IntPredicate);
+    method public java.util.OptionalInt findAny();
+    method public java.util.OptionalInt findFirst();
+    method public java.util.stream.IntStream flatMap(java.util.function.IntFunction<? extends java.util.stream.IntStream>);
+    method public void forEach(java.util.function.IntConsumer);
+    method public void forEachOrdered(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream generate(java.util.function.IntSupplier);
+    method public static java.util.stream.IntStream iterate(int, java.util.function.IntUnaryOperator);
+    method public java.util.PrimitiveIterator.OfInt iterator();
+    method public java.util.stream.IntStream limit(long);
+    method public java.util.stream.IntStream map(java.util.function.IntUnaryOperator);
+    method public java.util.stream.DoubleStream mapToDouble(java.util.function.IntToDoubleFunction);
+    method public java.util.stream.LongStream mapToLong(java.util.function.IntToLongFunction);
+    method public <U> java.util.stream.Stream<U> mapToObj(java.util.function.IntFunction<? extends U>);
+    method public java.util.OptionalInt max();
+    method public java.util.OptionalInt min();
+    method public boolean noneMatch(java.util.function.IntPredicate);
+    method public static java.util.stream.IntStream of(int);
+    method public static java.util.stream.IntStream of(int...);
+    method public java.util.stream.IntStream parallel();
+    method public java.util.stream.IntStream peek(java.util.function.IntConsumer);
+    method public static java.util.stream.IntStream range(int, int);
+    method public static java.util.stream.IntStream rangeClosed(int, int);
+    method public int reduce(int, java.util.function.IntBinaryOperator);
+    method public java.util.OptionalInt reduce(java.util.function.IntBinaryOperator);
+    method public java.util.stream.IntStream sequential();
+    method public java.util.stream.IntStream skip(long);
+    method public java.util.stream.IntStream sorted();
+    method public java.util.Spliterator.OfInt spliterator();
+    method public int sum();
+    method public java.util.IntSummaryStatistics summaryStatistics();
+    method public int[] toArray();
+  }
+
+  public static interface IntStream.Builder extends java.util.function.IntConsumer {
+    method public default java.util.stream.IntStream.Builder add(int);
+    method public java.util.stream.IntStream build();
+  }
+
+  public interface LongStream extends java.util.stream.BaseStream<java.lang.Long,java.util.stream.LongStream> {
+    method public boolean allMatch(java.util.function.LongPredicate);
+    method public boolean anyMatch(java.util.function.LongPredicate);
+    method public java.util.stream.DoubleStream asDoubleStream();
+    method public java.util.OptionalDouble average();
+    method public java.util.stream.Stream<java.lang.Long> boxed();
+    method public static java.util.stream.LongStream.Builder builder();
+    method public <R> R collect(java.util.function.Supplier<R>, java.util.function.ObjLongConsumer<R>, java.util.function.BiConsumer<R,R>);
+    method public static java.util.stream.LongStream concat(java.util.stream.LongStream, java.util.stream.LongStream);
+    method public long count();
+    method public java.util.stream.LongStream distinct();
+    method public static java.util.stream.LongStream empty();
+    method public java.util.stream.LongStream filter(java.util.function.LongPredicate);
+    method public java.util.OptionalLong findAny();
+    method public java.util.OptionalLong findFirst();
+    method public java.util.stream.LongStream flatMap(java.util.function.LongFunction<? extends java.util.stream.LongStream>);
+    method public void forEach(java.util.function.LongConsumer);
+    method public void forEachOrdered(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream generate(java.util.function.LongSupplier);
+    method public static java.util.stream.LongStream iterate(long, java.util.function.LongUnaryOperator);
+    method public java.util.PrimitiveIterator.OfLong iterator();
+    method public java.util.stream.LongStream limit(long);
+    method public java.util.stream.LongStream map(java.util.function.LongUnaryOperator);
+    method public java.util.stream.DoubleStream mapToDouble(java.util.function.LongToDoubleFunction);
+    method public java.util.stream.IntStream mapToInt(java.util.function.LongToIntFunction);
+    method public <U> java.util.stream.Stream<U> mapToObj(java.util.function.LongFunction<? extends U>);
+    method public java.util.OptionalLong max();
+    method public java.util.OptionalLong min();
+    method public boolean noneMatch(java.util.function.LongPredicate);
+    method public static java.util.stream.LongStream of(long);
+    method public static java.util.stream.LongStream of(long...);
+    method public java.util.stream.LongStream parallel();
+    method public java.util.stream.LongStream peek(java.util.function.LongConsumer);
+    method public static java.util.stream.LongStream range(long, long);
+    method public static java.util.stream.LongStream rangeClosed(long, long);
+    method public long reduce(long, java.util.function.LongBinaryOperator);
+    method public java.util.OptionalLong reduce(java.util.function.LongBinaryOperator);
+    method public java.util.stream.LongStream sequential();
+    method public java.util.stream.LongStream skip(long);
+    method public java.util.stream.LongStream sorted();
+    method public java.util.Spliterator.OfLong spliterator();
+    method public long sum();
+    method public java.util.LongSummaryStatistics summaryStatistics();
+    method public long[] toArray();
+  }
+
+  public static interface LongStream.Builder extends java.util.function.LongConsumer {
+    method public default java.util.stream.LongStream.Builder add(long);
+    method public java.util.stream.LongStream build();
+  }
+
+  public interface Stream<T> extends java.util.stream.BaseStream<T,java.util.stream.Stream<T>> {
+    method public boolean allMatch(java.util.function.Predicate<? super T>);
+    method public boolean anyMatch(java.util.function.Predicate<? super T>);
+    method public static <T> java.util.stream.Stream.Builder<T> builder();
+    method public <R> R collect(java.util.function.Supplier<R>, java.util.function.BiConsumer<R,? super T>, java.util.function.BiConsumer<R,R>);
+    method public <R, A> R collect(java.util.stream.Collector<? super T,A,R>);
+    method public static <T> java.util.stream.Stream<T> concat(java.util.stream.Stream<? extends T>, java.util.stream.Stream<? extends T>);
+    method public long count();
+    method public java.util.stream.Stream<T> distinct();
+    method public static <T> java.util.stream.Stream<T> empty();
+    method public java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T>);
+    method public java.util.Optional<T> findAny();
+    method public java.util.Optional<T> findFirst();
+    method public <R> java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T,? extends java.util.stream.Stream<? extends R>>);
+    method public java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T,? extends java.util.stream.DoubleStream>);
+    method public java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T,? extends java.util.stream.IntStream>);
+    method public java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T,? extends java.util.stream.LongStream>);
+    method public void forEach(java.util.function.Consumer<? super T>);
+    method public void forEachOrdered(java.util.function.Consumer<? super T>);
+    method public static <T> java.util.stream.Stream<T> generate(java.util.function.Supplier<T>);
+    method public static <T> java.util.stream.Stream<T> iterate(T, java.util.function.UnaryOperator<T>);
+    method public java.util.stream.Stream<T> limit(long);
+    method public <R> java.util.stream.Stream<R> map(java.util.function.Function<? super T,? extends R>);
+    method public java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T>);
+    method public java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T>);
+    method public java.util.Optional<T> max(java.util.Comparator<? super T>);
+    method public java.util.Optional<T> min(java.util.Comparator<? super T>);
+    method public boolean noneMatch(java.util.function.Predicate<? super T>);
+    method public static <T> java.util.stream.Stream<T> of(T);
+    method @java.lang.SafeVarargs public static <T> java.util.stream.Stream<T> of(T...);
+    method public java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T>);
+    method public T reduce(T, java.util.function.BinaryOperator<T>);
+    method public java.util.Optional<T> reduce(java.util.function.BinaryOperator<T>);
+    method public <U> U reduce(U, java.util.function.BiFunction<U,? super T,U>, java.util.function.BinaryOperator<U>);
+    method public java.util.stream.Stream<T> skip(long);
+    method public java.util.stream.Stream<T> sorted();
+    method public java.util.stream.Stream<T> sorted(java.util.Comparator<? super T>);
+    method public Object[] toArray();
+    method public <A> A[] toArray(java.util.function.IntFunction<A[]>);
+  }
+
+  public static interface Stream.Builder<T> extends java.util.function.Consumer<T> {
+    method public default java.util.stream.Stream.Builder<T> add(T);
+    method public java.util.stream.Stream<T> build();
+  }
+
+  public final class StreamSupport {
+    method public static java.util.stream.DoubleStream doubleStream(java.util.Spliterator.OfDouble, boolean);
+    method public static java.util.stream.DoubleStream doubleStream(java.util.function.Supplier<? extends java.util.Spliterator.OfDouble>, int, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.Spliterator.OfInt, boolean);
+    method public static java.util.stream.IntStream intStream(java.util.function.Supplier<? extends java.util.Spliterator.OfInt>, int, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.Spliterator.OfLong, boolean);
+    method public static java.util.stream.LongStream longStream(java.util.function.Supplier<? extends java.util.Spliterator.OfLong>, int, boolean);
+    method public static <T> java.util.stream.Stream<T> stream(java.util.Spliterator<T>, boolean);
+    method public static <T> java.util.stream.Stream<T> stream(java.util.function.Supplier<? extends java.util.Spliterator<T>>, int, boolean);
+  }
+
+}
+
+package java.util.zip {
+
+  public class Adler32 implements java.util.zip.Checksum {
+    ctor public Adler32();
+    method public long getValue();
+    method public void reset();
+    method public void update(int);
+    method public void update(byte[], int, int);
+    method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
+  }
+
+  public class CRC32 implements java.util.zip.Checksum {
+    ctor public CRC32();
+    method public long getValue();
+    method public void reset();
+    method public void update(int);
+    method public void update(byte[], int, int);
+    method public void update(byte[]);
+    method public void update(java.nio.ByteBuffer);
+  }
+
+  public class CheckedInputStream extends java.io.FilterInputStream {
+    ctor public CheckedInputStream(java.io.InputStream, java.util.zip.Checksum);
+    method public java.util.zip.Checksum getChecksum();
+  }
+
+  public class CheckedOutputStream extends java.io.FilterOutputStream {
+    ctor public CheckedOutputStream(java.io.OutputStream, java.util.zip.Checksum);
+    method public java.util.zip.Checksum getChecksum();
+  }
+
+  public interface Checksum {
+    method public long getValue();
+    method public void reset();
+    method public void update(int);
+    method public void update(byte[], int, int);
+  }
+
+  public class DataFormatException extends java.lang.Exception {
+    ctor public DataFormatException();
+    ctor public DataFormatException(String);
+  }
+
+  public class Deflater {
+    ctor public Deflater(int, boolean);
+    ctor public Deflater(int);
+    ctor public Deflater();
+    method public int deflate(byte[], int, int);
+    method public int deflate(byte[]);
+    method public int deflate(byte[], int, int, int);
+    method public void end();
+    method protected void finalize();
+    method public void finish();
+    method public boolean finished();
+    method public int getAdler();
+    method public long getBytesRead();
+    method public long getBytesWritten();
+    method public int getTotalIn();
+    method public int getTotalOut();
+    method public boolean needsInput();
+    method public void reset();
+    method public void setDictionary(byte[], int, int);
+    method public void setDictionary(byte[]);
+    method public void setInput(byte[], int, int);
+    method public void setInput(byte[]);
+    method public void setLevel(int);
+    method public void setStrategy(int);
+    field public static final int BEST_COMPRESSION = 9; // 0x9
+    field public static final int BEST_SPEED = 1; // 0x1
+    field public static final int DEFAULT_COMPRESSION = -1; // 0xffffffff
+    field public static final int DEFAULT_STRATEGY = 0; // 0x0
+    field public static final int DEFLATED = 8; // 0x8
+    field public static final int FILTERED = 1; // 0x1
+    field public static final int FULL_FLUSH = 3; // 0x3
+    field public static final int HUFFMAN_ONLY = 2; // 0x2
+    field public static final int NO_COMPRESSION = 0; // 0x0
+    field public static final int NO_FLUSH = 0; // 0x0
+    field public static final int SYNC_FLUSH = 2; // 0x2
+  }
+
+  public class DeflaterInputStream extends java.io.FilterInputStream {
+    ctor public DeflaterInputStream(java.io.InputStream);
+    ctor public DeflaterInputStream(java.io.InputStream, java.util.zip.Deflater);
+    ctor public DeflaterInputStream(java.io.InputStream, java.util.zip.Deflater, int);
+    field protected final byte[] buf;
+    field protected final java.util.zip.Deflater def;
+  }
+
+  public class DeflaterOutputStream extends java.io.FilterOutputStream {
+    ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int, boolean);
+    ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, int);
+    ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater, boolean);
+    ctor public DeflaterOutputStream(java.io.OutputStream, java.util.zip.Deflater);
+    ctor public DeflaterOutputStream(java.io.OutputStream, boolean);
+    ctor public DeflaterOutputStream(java.io.OutputStream);
+    method protected void deflate() throws java.io.IOException;
+    method public void finish() throws java.io.IOException;
+    field protected byte[] buf;
+    field protected java.util.zip.Deflater def;
+  }
+
+  public class GZIPInputStream extends java.util.zip.InflaterInputStream {
+    ctor public GZIPInputStream(java.io.InputStream, int) throws java.io.IOException;
+    ctor public GZIPInputStream(java.io.InputStream) throws java.io.IOException;
+    field public static final int GZIP_MAGIC = 35615; // 0x8b1f
+    field protected java.util.zip.CRC32 crc;
+    field protected boolean eos;
+  }
+
+  public class GZIPOutputStream extends java.util.zip.DeflaterOutputStream {
+    ctor public GZIPOutputStream(java.io.OutputStream, int) throws java.io.IOException;
+    ctor public GZIPOutputStream(java.io.OutputStream, int, boolean) throws java.io.IOException;
+    ctor public GZIPOutputStream(java.io.OutputStream) throws java.io.IOException;
+    ctor public GZIPOutputStream(java.io.OutputStream, boolean) throws java.io.IOException;
+    field protected java.util.zip.CRC32 crc;
+  }
+
+  public class Inflater {
+    ctor public Inflater(boolean);
+    ctor public Inflater();
+    method public void end();
+    method protected void finalize();
+    method public boolean finished();
+    method public int getAdler();
+    method public long getBytesRead();
+    method public long getBytesWritten();
+    method public int getRemaining();
+    method public int getTotalIn();
+    method public int getTotalOut();
+    method public int inflate(byte[], int, int) throws java.util.zip.DataFormatException;
+    method public int inflate(byte[]) throws java.util.zip.DataFormatException;
+    method public boolean needsDictionary();
+    method public boolean needsInput();
+    method public void reset();
+    method public void setDictionary(byte[], int, int);
+    method public void setDictionary(byte[]);
+    method public void setInput(byte[], int, int);
+    method public void setInput(byte[]);
+  }
+
+  public class InflaterInputStream extends java.io.FilterInputStream {
+    ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater, int);
+    ctor public InflaterInputStream(java.io.InputStream, java.util.zip.Inflater);
+    ctor public InflaterInputStream(java.io.InputStream);
+    method protected void fill() throws java.io.IOException;
+    field protected byte[] buf;
+    field @Deprecated protected boolean closed;
+    field protected java.util.zip.Inflater inf;
+    field protected int len;
+  }
+
+  public class InflaterOutputStream extends java.io.FilterOutputStream {
+    ctor public InflaterOutputStream(java.io.OutputStream);
+    ctor public InflaterOutputStream(java.io.OutputStream, java.util.zip.Inflater);
+    ctor public InflaterOutputStream(java.io.OutputStream, java.util.zip.Inflater, int);
+    method public void finish() throws java.io.IOException;
+    field protected final byte[] buf;
+    field protected final java.util.zip.Inflater inf;
+  }
+
+  public class ZipEntry implements java.lang.Cloneable {
+    ctor public ZipEntry(String);
+    ctor public ZipEntry(java.util.zip.ZipEntry);
+    method public Object clone();
+    method public String getComment();
+    method public long getCompressedSize();
+    method public long getCrc();
+    method public java.nio.file.attribute.FileTime getCreationTime();
+    method public byte[] getExtra();
+    method public java.nio.file.attribute.FileTime getLastAccessTime();
+    method public java.nio.file.attribute.FileTime getLastModifiedTime();
+    method public int getMethod();
+    method public String getName();
+    method public long getSize();
+    method public long getTime();
+    method public boolean isDirectory();
+    method public void setComment(String);
+    method public void setCompressedSize(long);
+    method public void setCrc(long);
+    method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime);
+    method public void setExtra(byte[]);
+    method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime);
+    method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime);
+    method public void setMethod(int);
+    method public void setSize(long);
+    method public void setTime(long);
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int DEFLATED = 8; // 0x8
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+    field public static final int STORED = 0; // 0x0
+  }
+
+  public class ZipError extends java.lang.InternalError {
+    ctor public ZipError(String);
+  }
+
+  public class ZipException extends java.io.IOException {
+    ctor public ZipException();
+    ctor public ZipException(String);
+  }
+
+  public class ZipFile implements java.io.Closeable {
+    ctor public ZipFile(String) throws java.io.IOException;
+    ctor public ZipFile(java.io.File, int) throws java.io.IOException;
+    ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
+    ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException;
+    ctor public ZipFile(String, java.nio.charset.Charset) throws java.io.IOException;
+    ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException;
+    method public void close() throws java.io.IOException;
+    method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries();
+    method protected void finalize() throws java.io.IOException;
+    method public String getComment();
+    method public java.util.zip.ZipEntry getEntry(String);
+    method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException;
+    method public String getName();
+    method public int size();
+    method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream();
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+    field public static final int OPEN_DELETE = 4; // 0x4
+    field public static final int OPEN_READ = 1; // 0x1
+  }
+
+  public class ZipInputStream extends java.util.zip.InflaterInputStream {
+    ctor public ZipInputStream(java.io.InputStream);
+    ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset);
+    method public void closeEntry() throws java.io.IOException;
+    method protected java.util.zip.ZipEntry createZipEntry(String);
+    method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException;
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+  }
+
+  public class ZipOutputStream extends java.util.zip.DeflaterOutputStream {
+    ctor public ZipOutputStream(java.io.OutputStream);
+    ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset);
+    method public void closeEntry() throws java.io.IOException;
+    method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException;
+    method public void setComment(String);
+    method public void setLevel(int);
+    method public void setMethod(int);
+    field public static final int CENATT = 36; // 0x24
+    field public static final int CENATX = 38; // 0x26
+    field public static final int CENCOM = 32; // 0x20
+    field public static final int CENCRC = 16; // 0x10
+    field public static final int CENDSK = 34; // 0x22
+    field public static final int CENEXT = 30; // 0x1e
+    field public static final int CENFLG = 8; // 0x8
+    field public static final int CENHDR = 46; // 0x2e
+    field public static final int CENHOW = 10; // 0xa
+    field public static final int CENLEN = 24; // 0x18
+    field public static final int CENNAM = 28; // 0x1c
+    field public static final int CENOFF = 42; // 0x2a
+    field public static final long CENSIG = 33639248L; // 0x2014b50L
+    field public static final int CENSIZ = 20; // 0x14
+    field public static final int CENTIM = 12; // 0xc
+    field public static final int CENVEM = 4; // 0x4
+    field public static final int CENVER = 6; // 0x6
+    field public static final int DEFLATED = 8; // 0x8
+    field public static final int ENDCOM = 20; // 0x14
+    field public static final int ENDHDR = 22; // 0x16
+    field public static final int ENDOFF = 16; // 0x10
+    field public static final long ENDSIG = 101010256L; // 0x6054b50L
+    field public static final int ENDSIZ = 12; // 0xc
+    field public static final int ENDSUB = 8; // 0x8
+    field public static final int ENDTOT = 10; // 0xa
+    field public static final int EXTCRC = 4; // 0x4
+    field public static final int EXTHDR = 16; // 0x10
+    field public static final int EXTLEN = 12; // 0xc
+    field public static final long EXTSIG = 134695760L; // 0x8074b50L
+    field public static final int EXTSIZ = 8; // 0x8
+    field public static final int LOCCRC = 14; // 0xe
+    field public static final int LOCEXT = 28; // 0x1c
+    field public static final int LOCFLG = 6; // 0x6
+    field public static final int LOCHDR = 30; // 0x1e
+    field public static final int LOCHOW = 8; // 0x8
+    field public static final int LOCLEN = 22; // 0x16
+    field public static final int LOCNAM = 26; // 0x1a
+    field public static final long LOCSIG = 67324752L; // 0x4034b50L
+    field public static final int LOCSIZ = 18; // 0x12
+    field public static final int LOCTIM = 10; // 0xa
+    field public static final int LOCVER = 4; // 0x4
+    field public static final int STORED = 0; // 0x0
+  }
+
+}
+
+package javax.crypto {
+
+  public class AEADBadTagException extends javax.crypto.BadPaddingException {
+    ctor public AEADBadTagException();
+    ctor public AEADBadTagException(String);
+  }
+
+  public class BadPaddingException extends java.security.GeneralSecurityException {
+    ctor public BadPaddingException();
+    ctor public BadPaddingException(String);
+  }
+
+  public class Cipher {
+    ctor protected Cipher(javax.crypto.CipherSpi, java.security.Provider, String);
+    method public final byte[] doFinal() throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException;
+    method public final int doFinal(byte[], int) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException, javax.crypto.ShortBufferException;
+    method public final byte[] doFinal(byte[]) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException;
+    method public final byte[] doFinal(byte[], int, int) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException;
+    method public final int doFinal(byte[], int, int, byte[]) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException, javax.crypto.ShortBufferException;
+    method public final int doFinal(byte[], int, int, byte[], int) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException, javax.crypto.ShortBufferException;
+    method public final int doFinal(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException, javax.crypto.ShortBufferException;
+    method public final String getAlgorithm();
+    method public final int getBlockSize();
+    method public final javax.crypto.ExemptionMechanism getExemptionMechanism();
+    method public final byte[] getIV();
+    method public static final javax.crypto.Cipher getInstance(String) throws java.security.NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException;
+    method public static final javax.crypto.Cipher getInstance(String, String) throws java.security.NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException, java.security.NoSuchProviderException;
+    method public static final javax.crypto.Cipher getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException;
+    method public static final int getMaxAllowedKeyLength(String) throws java.security.NoSuchAlgorithmException;
+    method public static final java.security.spec.AlgorithmParameterSpec getMaxAllowedParameterSpec(String) throws java.security.NoSuchAlgorithmException;
+    method public final int getOutputSize(int);
+    method public final java.security.AlgorithmParameters getParameters();
+    method public final java.security.Provider getProvider();
+    method public final void init(int, java.security.Key) throws java.security.InvalidKeyException;
+    method public final void init(int, java.security.Key, java.security.SecureRandom) throws java.security.InvalidKeyException;
+    method public final void init(int, java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final void init(int, java.security.Key, java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final void init(int, java.security.Key, java.security.AlgorithmParameters) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final void init(int, java.security.Key, java.security.AlgorithmParameters, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final void init(int, java.security.cert.Certificate) throws java.security.InvalidKeyException;
+    method public final void init(int, java.security.cert.Certificate, java.security.SecureRandom) throws java.security.InvalidKeyException;
+    method public final java.security.Key unwrap(byte[], String, int) throws java.security.InvalidKeyException, java.security.NoSuchAlgorithmException;
+    method public final byte[] update(byte[]);
+    method public final byte[] update(byte[], int, int);
+    method public final int update(byte[], int, int, byte[]) throws javax.crypto.ShortBufferException;
+    method public final int update(byte[], int, int, byte[], int) throws javax.crypto.ShortBufferException;
+    method public final int update(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.crypto.ShortBufferException;
+    method public final void updateAAD(byte[]);
+    method public final void updateAAD(byte[], int, int);
+    method public final void updateAAD(java.nio.ByteBuffer);
+    method public final byte[] wrap(java.security.Key) throws javax.crypto.IllegalBlockSizeException, java.security.InvalidKeyException;
+    field public static final int DECRYPT_MODE = 2; // 0x2
+    field public static final int ENCRYPT_MODE = 1; // 0x1
+    field public static final int PRIVATE_KEY = 2; // 0x2
+    field public static final int PUBLIC_KEY = 1; // 0x1
+    field public static final int SECRET_KEY = 3; // 0x3
+    field public static final int UNWRAP_MODE = 4; // 0x4
+    field public static final int WRAP_MODE = 3; // 0x3
+  }
+
+  public class CipherInputStream extends java.io.FilterInputStream {
+    ctor public CipherInputStream(java.io.InputStream, javax.crypto.Cipher);
+    ctor protected CipherInputStream(java.io.InputStream);
+  }
+
+  public class CipherOutputStream extends java.io.FilterOutputStream {
+    ctor public CipherOutputStream(java.io.OutputStream, javax.crypto.Cipher);
+    ctor protected CipherOutputStream(java.io.OutputStream);
+  }
+
+  public abstract class CipherSpi {
+    ctor public CipherSpi();
+    method protected abstract byte[] engineDoFinal(byte[], int, int) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException;
+    method protected abstract int engineDoFinal(byte[], int, int, byte[], int) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException, javax.crypto.ShortBufferException;
+    method protected int engineDoFinal(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.crypto.BadPaddingException, javax.crypto.IllegalBlockSizeException, javax.crypto.ShortBufferException;
+    method protected abstract int engineGetBlockSize();
+    method protected abstract byte[] engineGetIV();
+    method protected int engineGetKeySize(java.security.Key) throws java.security.InvalidKeyException;
+    method protected abstract int engineGetOutputSize(int);
+    method protected abstract java.security.AlgorithmParameters engineGetParameters();
+    method protected abstract void engineInit(int, java.security.Key, java.security.SecureRandom) throws java.security.InvalidKeyException;
+    method protected abstract void engineInit(int, java.security.Key, java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method protected abstract void engineInit(int, java.security.Key, java.security.AlgorithmParameters, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method protected abstract void engineSetMode(String) throws java.security.NoSuchAlgorithmException;
+    method protected abstract void engineSetPadding(String) throws javax.crypto.NoSuchPaddingException;
+    method protected java.security.Key engineUnwrap(byte[], String, int) throws java.security.InvalidKeyException, java.security.NoSuchAlgorithmException;
+    method protected abstract byte[] engineUpdate(byte[], int, int);
+    method protected abstract int engineUpdate(byte[], int, int, byte[], int) throws javax.crypto.ShortBufferException;
+    method protected int engineUpdate(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.crypto.ShortBufferException;
+    method protected void engineUpdateAAD(byte[], int, int);
+    method protected void engineUpdateAAD(java.nio.ByteBuffer);
+    method protected byte[] engineWrap(java.security.Key) throws javax.crypto.IllegalBlockSizeException, java.security.InvalidKeyException;
+  }
+
+  public class EncryptedPrivateKeyInfo {
+    ctor public EncryptedPrivateKeyInfo(byte[]) throws java.io.IOException;
+    ctor public EncryptedPrivateKeyInfo(String, byte[]) throws java.security.NoSuchAlgorithmException;
+    ctor public EncryptedPrivateKeyInfo(java.security.AlgorithmParameters, byte[]) throws java.security.NoSuchAlgorithmException;
+    method public String getAlgName();
+    method public java.security.AlgorithmParameters getAlgParameters();
+    method public byte[] getEncoded() throws java.io.IOException;
+    method public byte[] getEncryptedData();
+    method public java.security.spec.PKCS8EncodedKeySpec getKeySpec(javax.crypto.Cipher) throws java.security.spec.InvalidKeySpecException;
+    method public java.security.spec.PKCS8EncodedKeySpec getKeySpec(java.security.Key) throws java.security.InvalidKeyException, java.security.NoSuchAlgorithmException;
+    method public java.security.spec.PKCS8EncodedKeySpec getKeySpec(java.security.Key, String) throws java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public java.security.spec.PKCS8EncodedKeySpec getKeySpec(java.security.Key, java.security.Provider) throws java.security.InvalidKeyException, java.security.NoSuchAlgorithmException;
+  }
+
+  public class ExemptionMechanism {
+    ctor protected ExemptionMechanism(javax.crypto.ExemptionMechanismSpi, java.security.Provider, String);
+    method public final byte[] genExemptionBlob() throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException;
+    method public final int genExemptionBlob(byte[]) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
+    method public final int genExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, java.lang.IllegalStateException, javax.crypto.ShortBufferException;
+    method public static final javax.crypto.ExemptionMechanism getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static final javax.crypto.ExemptionMechanism getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static final javax.crypto.ExemptionMechanism getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final String getName();
+    method public final int getOutputSize(int) throws java.lang.IllegalStateException;
+    method public final java.security.Provider getProvider();
+    method public final void init(java.security.Key) throws javax.crypto.ExemptionMechanismException, java.security.InvalidKeyException;
+    method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final void init(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final boolean isCryptoAllowed(java.security.Key) throws javax.crypto.ExemptionMechanismException;
+  }
+
+  public class ExemptionMechanismException extends java.security.GeneralSecurityException {
+    ctor public ExemptionMechanismException();
+    ctor public ExemptionMechanismException(String);
+  }
+
+  public abstract class ExemptionMechanismSpi {
+    ctor public ExemptionMechanismSpi();
+    method protected abstract byte[] engineGenExemptionBlob() throws javax.crypto.ExemptionMechanismException;
+    method protected abstract int engineGenExemptionBlob(byte[], int) throws javax.crypto.ExemptionMechanismException, javax.crypto.ShortBufferException;
+    method protected abstract int engineGetOutputSize(int);
+    method protected abstract void engineInit(java.security.Key) throws javax.crypto.ExemptionMechanismException, java.security.InvalidKeyException;
+    method protected abstract void engineInit(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method protected abstract void engineInit(java.security.Key, java.security.AlgorithmParameters) throws javax.crypto.ExemptionMechanismException, java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+  }
+
+  public class IllegalBlockSizeException extends java.security.GeneralSecurityException {
+    ctor public IllegalBlockSizeException();
+    ctor public IllegalBlockSizeException(String);
+  }
+
+  public class KeyAgreement {
+    ctor protected KeyAgreement(javax.crypto.KeyAgreementSpi, java.security.Provider, String);
+    method public final java.security.Key doPhase(java.security.Key, boolean) throws java.lang.IllegalStateException, java.security.InvalidKeyException;
+    method public final byte[] generateSecret() throws java.lang.IllegalStateException;
+    method public final int generateSecret(byte[], int) throws java.lang.IllegalStateException, javax.crypto.ShortBufferException;
+    method public final javax.crypto.SecretKey generateSecret(String) throws java.lang.IllegalStateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException;
+    method public final String getAlgorithm();
+    method public static final javax.crypto.KeyAgreement getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static final javax.crypto.KeyAgreement getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static final javax.crypto.KeyAgreement getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public final void init(java.security.Key) throws java.security.InvalidKeyException;
+    method public final void init(java.security.Key, java.security.SecureRandom) throws java.security.InvalidKeyException;
+    method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+  }
+
+  public abstract class KeyAgreementSpi {
+    ctor public KeyAgreementSpi();
+    method protected abstract java.security.Key engineDoPhase(java.security.Key, boolean) throws java.lang.IllegalStateException, java.security.InvalidKeyException;
+    method protected abstract byte[] engineGenerateSecret() throws java.lang.IllegalStateException;
+    method protected abstract int engineGenerateSecret(byte[], int) throws java.lang.IllegalStateException, javax.crypto.ShortBufferException;
+    method protected abstract javax.crypto.SecretKey engineGenerateSecret(String) throws java.lang.IllegalStateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException;
+    method protected abstract void engineInit(java.security.Key, java.security.SecureRandom) throws java.security.InvalidKeyException;
+    method protected abstract void engineInit(java.security.Key, java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+  }
+
+  public class KeyGenerator {
+    ctor protected KeyGenerator(javax.crypto.KeyGeneratorSpi, java.security.Provider, String);
+    method public final javax.crypto.SecretKey generateKey();
+    method public final String getAlgorithm();
+    method public static final javax.crypto.KeyGenerator getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static final javax.crypto.KeyGenerator getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static final javax.crypto.KeyGenerator getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public final void init(java.security.SecureRandom);
+    method public final void init(java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException;
+    method public final void init(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
+    method public final void init(int);
+    method public final void init(int, java.security.SecureRandom);
+  }
+
+  public abstract class KeyGeneratorSpi {
+    ctor public KeyGeneratorSpi();
+    method protected abstract javax.crypto.SecretKey engineGenerateKey();
+    method protected abstract void engineInit(java.security.SecureRandom);
+    method protected abstract void engineInit(java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom) throws java.security.InvalidAlgorithmParameterException;
+    method protected abstract void engineInit(int, java.security.SecureRandom);
+  }
+
+  public class Mac implements java.lang.Cloneable {
+    ctor protected Mac(javax.crypto.MacSpi, java.security.Provider, String);
+    method public final Object clone() throws java.lang.CloneNotSupportedException;
+    method public final byte[] doFinal() throws java.lang.IllegalStateException;
+    method public final void doFinal(byte[], int) throws java.lang.IllegalStateException, javax.crypto.ShortBufferException;
+    method public final byte[] doFinal(byte[]) throws java.lang.IllegalStateException;
+    method public final String getAlgorithm();
+    method public static final javax.crypto.Mac getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static final javax.crypto.Mac getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static final javax.crypto.Mac getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final int getMacLength();
+    method public final java.security.Provider getProvider();
+    method public final void init(java.security.Key) throws java.security.InvalidKeyException;
+    method public final void init(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method public final void reset();
+    method public final void update(byte) throws java.lang.IllegalStateException;
+    method public final void update(byte[]) throws java.lang.IllegalStateException;
+    method public final void update(byte[], int, int) throws java.lang.IllegalStateException;
+    method public final void update(java.nio.ByteBuffer);
+  }
+
+  public abstract class MacSpi {
+    ctor public MacSpi();
+    method public Object clone() throws java.lang.CloneNotSupportedException;
+    method protected abstract byte[] engineDoFinal();
+    method protected abstract int engineGetMacLength();
+    method protected abstract void engineInit(java.security.Key, java.security.spec.AlgorithmParameterSpec) throws java.security.InvalidAlgorithmParameterException, java.security.InvalidKeyException;
+    method protected abstract void engineReset();
+    method protected abstract void engineUpdate(byte);
+    method protected abstract void engineUpdate(byte[], int, int);
+    method protected void engineUpdate(java.nio.ByteBuffer);
+  }
+
+  public class NoSuchPaddingException extends java.security.GeneralSecurityException {
+    ctor public NoSuchPaddingException();
+    ctor public NoSuchPaddingException(String);
+  }
+
+  public class NullCipher extends javax.crypto.Cipher {
+    ctor public NullCipher();
+  }
+
+  public class SealedObject implements java.io.Serializable {
+    ctor public SealedObject(java.io.Serializable, javax.crypto.Cipher) throws java.io.IOException, javax.crypto.IllegalBlockSizeException;
+    ctor protected SealedObject(javax.crypto.SealedObject);
+    method public final String getAlgorithm();
+    method public final Object getObject(java.security.Key) throws java.lang.ClassNotFoundException, java.io.IOException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException;
+    method public final Object getObject(javax.crypto.Cipher) throws javax.crypto.BadPaddingException, java.lang.ClassNotFoundException, java.io.IOException, javax.crypto.IllegalBlockSizeException;
+    method public final Object getObject(java.security.Key, String) throws java.lang.ClassNotFoundException, java.io.IOException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    field protected byte[] encodedParams;
+  }
+
+  public interface SecretKey extends java.security.Key javax.security.auth.Destroyable {
+    field public static final long serialVersionUID = -4795878709595146952L; // 0xbd719db928b8f538L
+  }
+
+  public class SecretKeyFactory {
+    ctor protected SecretKeyFactory(javax.crypto.SecretKeyFactorySpi, java.security.Provider, String);
+    method public final javax.crypto.SecretKey generateSecret(java.security.spec.KeySpec) throws java.security.spec.InvalidKeySpecException;
+    method public final String getAlgorithm();
+    method public static final javax.crypto.SecretKeyFactory getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static final javax.crypto.SecretKeyFactory getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static final javax.crypto.SecretKeyFactory getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.spec.KeySpec getKeySpec(javax.crypto.SecretKey, Class<?>) throws java.security.spec.InvalidKeySpecException;
+    method public final java.security.Provider getProvider();
+    method public final javax.crypto.SecretKey translateKey(javax.crypto.SecretKey) throws java.security.InvalidKeyException;
+  }
+
+  public abstract class SecretKeyFactorySpi {
+    ctor public SecretKeyFactorySpi();
+    method protected abstract javax.crypto.SecretKey engineGenerateSecret(java.security.spec.KeySpec) throws java.security.spec.InvalidKeySpecException;
+    method protected abstract java.security.spec.KeySpec engineGetKeySpec(javax.crypto.SecretKey, Class<?>) throws java.security.spec.InvalidKeySpecException;
+    method protected abstract javax.crypto.SecretKey engineTranslateKey(javax.crypto.SecretKey) throws java.security.InvalidKeyException;
+  }
+
+  public class ShortBufferException extends java.security.GeneralSecurityException {
+    ctor public ShortBufferException();
+    ctor public ShortBufferException(String);
+  }
+
+}
+
+package javax.crypto.interfaces {
+
+  public interface DHKey {
+    method public javax.crypto.spec.DHParameterSpec getParams();
+  }
+
+  public interface DHPrivateKey extends javax.crypto.interfaces.DHKey java.security.PrivateKey {
+    method public java.math.BigInteger getX();
+    field public static final long serialVersionUID = 2211791113380396553L; // 0x1eb1dc4c8e677e09L
+  }
+
+  public interface DHPublicKey extends javax.crypto.interfaces.DHKey java.security.PublicKey {
+    method public java.math.BigInteger getY();
+    field public static final long serialVersionUID = -6628103563352519193L; // 0xa4043eed23df4de7L
+  }
+
+  public interface PBEKey extends javax.crypto.SecretKey {
+    method public int getIterationCount();
+    method public char[] getPassword();
+    method public byte[] getSalt();
+    field public static final long serialVersionUID = -1430015993304333921L; // 0xec279007d7f7c19fL
+  }
+
+}
+
+package javax.crypto.spec {
+
+  public class DESKeySpec implements java.security.spec.KeySpec {
+    ctor public DESKeySpec(byte[]) throws java.security.InvalidKeyException;
+    ctor public DESKeySpec(byte[], int) throws java.security.InvalidKeyException;
+    method public byte[] getKey();
+    method public static boolean isParityAdjusted(byte[], int) throws java.security.InvalidKeyException;
+    method public static boolean isWeak(byte[], int) throws java.security.InvalidKeyException;
+    field public static final int DES_KEY_LEN = 8; // 0x8
+  }
+
+  public class DESedeKeySpec implements java.security.spec.KeySpec {
+    ctor public DESedeKeySpec(byte[]) throws java.security.InvalidKeyException;
+    ctor public DESedeKeySpec(byte[], int) throws java.security.InvalidKeyException;
+    method public byte[] getKey();
+    method public static boolean isParityAdjusted(byte[], int) throws java.security.InvalidKeyException;
+    field public static final int DES_EDE_KEY_LEN = 24; // 0x18
+  }
+
+  public class DHGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public DHGenParameterSpec(int, int);
+    method public int getExponentSize();
+    method public int getPrimeSize();
+  }
+
+  public class DHParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public DHParameterSpec(java.math.BigInteger, java.math.BigInteger);
+    ctor public DHParameterSpec(java.math.BigInteger, java.math.BigInteger, int);
+    method public java.math.BigInteger getG();
+    method public int getL();
+    method public java.math.BigInteger getP();
+  }
+
+  public class DHPrivateKeySpec implements java.security.spec.KeySpec {
+    ctor public DHPrivateKeySpec(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getG();
+    method public java.math.BigInteger getP();
+    method public java.math.BigInteger getX();
+  }
+
+  public class DHPublicKeySpec implements java.security.spec.KeySpec {
+    ctor public DHPublicKeySpec(java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public java.math.BigInteger getG();
+    method public java.math.BigInteger getP();
+    method public java.math.BigInteger getY();
+  }
+
+  public class GCMParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public GCMParameterSpec(int, byte[]);
+    ctor public GCMParameterSpec(int, byte[], int, int);
+    method public byte[] getIV();
+    method public int getTLen();
+  }
+
+  public class IvParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public IvParameterSpec(byte[]);
+    ctor public IvParameterSpec(byte[], int, int);
+    method public byte[] getIV();
+  }
+
+  public class OAEPParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public OAEPParameterSpec(String, String, java.security.spec.AlgorithmParameterSpec, javax.crypto.spec.PSource);
+    method public String getDigestAlgorithm();
+    method public String getMGFAlgorithm();
+    method public java.security.spec.AlgorithmParameterSpec getMGFParameters();
+    method public javax.crypto.spec.PSource getPSource();
+    field public static final javax.crypto.spec.OAEPParameterSpec DEFAULT;
+  }
+
+  public class PBEKeySpec implements java.security.spec.KeySpec {
+    ctor public PBEKeySpec(char[]);
+    ctor public PBEKeySpec(char[], byte[], int, int);
+    ctor public PBEKeySpec(char[], byte[], int);
+    method public final void clearPassword();
+    method public final int getIterationCount();
+    method public final int getKeyLength();
+    method public final char[] getPassword();
+    method public final byte[] getSalt();
+  }
+
+  public class PBEParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public PBEParameterSpec(byte[], int);
+    ctor public PBEParameterSpec(byte[], int, java.security.spec.AlgorithmParameterSpec);
+    method public int getIterationCount();
+    method public java.security.spec.AlgorithmParameterSpec getParameterSpec();
+    method public byte[] getSalt();
+  }
+
+  public class PSource {
+    ctor protected PSource(String);
+    method public String getAlgorithm();
+  }
+
+  public static final class PSource.PSpecified extends javax.crypto.spec.PSource {
+    ctor public PSource.PSpecified(byte[]);
+    method public byte[] getValue();
+    field public static final javax.crypto.spec.PSource.PSpecified DEFAULT;
+  }
+
+  public class RC2ParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public RC2ParameterSpec(int);
+    ctor public RC2ParameterSpec(int, byte[]);
+    ctor public RC2ParameterSpec(int, byte[], int);
+    method public int getEffectiveKeyBits();
+    method public byte[] getIV();
+  }
+
+  public class RC5ParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    ctor public RC5ParameterSpec(int, int, int);
+    ctor public RC5ParameterSpec(int, int, int, byte[]);
+    ctor public RC5ParameterSpec(int, int, int, byte[], int);
+    method public byte[] getIV();
+    method public int getRounds();
+    method public int getVersion();
+    method public int getWordSize();
+  }
+
+  public class SecretKeySpec implements java.security.spec.KeySpec javax.crypto.SecretKey {
+    ctor public SecretKeySpec(byte[], String);
+    ctor public SecretKeySpec(byte[], int, int, String);
+    method public String getAlgorithm();
+    method public byte[] getEncoded();
+    method public String getFormat();
+  }
+
+}
+
+package javax.net {
+
+  public abstract class ServerSocketFactory {
+    ctor protected ServerSocketFactory();
+    method public java.net.ServerSocket createServerSocket() throws java.io.IOException;
+    method public abstract java.net.ServerSocket createServerSocket(int) throws java.io.IOException;
+    method public abstract java.net.ServerSocket createServerSocket(int, int) throws java.io.IOException;
+    method public abstract java.net.ServerSocket createServerSocket(int, int, java.net.InetAddress) throws java.io.IOException;
+    method public static javax.net.ServerSocketFactory getDefault();
+  }
+
+  public abstract class SocketFactory {
+    ctor protected SocketFactory();
+    method public java.net.Socket createSocket() throws java.io.IOException;
+    method public abstract java.net.Socket createSocket(String, int) throws java.io.IOException, java.net.UnknownHostException;
+    method public abstract java.net.Socket createSocket(String, int, java.net.InetAddress, int) throws java.io.IOException, java.net.UnknownHostException;
+    method public abstract java.net.Socket createSocket(java.net.InetAddress, int) throws java.io.IOException;
+    method public abstract java.net.Socket createSocket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
+    method public static javax.net.SocketFactory getDefault();
+  }
+
+}
+
+package javax.net.ssl {
+
+  public class CertPathTrustManagerParameters implements javax.net.ssl.ManagerFactoryParameters {
+    ctor public CertPathTrustManagerParameters(java.security.cert.CertPathParameters);
+    method public java.security.cert.CertPathParameters getParameters();
+  }
+
+  public abstract class ExtendedSSLSession implements javax.net.ssl.SSLSession {
+    ctor public ExtendedSSLSession();
+    method public abstract String[] getLocalSupportedSignatureAlgorithms();
+    method public abstract String[] getPeerSupportedSignatureAlgorithms();
+    method public java.util.List<javax.net.ssl.SNIServerName> getRequestedServerNames();
+  }
+
+  public class HandshakeCompletedEvent extends java.util.EventObject {
+    ctor public HandshakeCompletedEvent(javax.net.ssl.SSLSocket, javax.net.ssl.SSLSession);
+    method public String getCipherSuite();
+    method public java.security.cert.Certificate[] getLocalCertificates();
+    method public java.security.Principal getLocalPrincipal();
+    method public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public java.security.cert.Certificate[] getPeerCertificates() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public java.security.Principal getPeerPrincipal() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public javax.net.ssl.SSLSession getSession();
+    method public javax.net.ssl.SSLSocket getSocket();
+  }
+
+  public interface HandshakeCompletedListener extends java.util.EventListener {
+    method public void handshakeCompleted(javax.net.ssl.HandshakeCompletedEvent);
+  }
+
+  public interface HostnameVerifier {
+    method public boolean verify(String, javax.net.ssl.SSLSession);
+  }
+
+  public abstract class HttpsURLConnection extends java.net.HttpURLConnection {
+    ctor protected HttpsURLConnection(java.net.URL);
+    method public abstract String getCipherSuite();
+    method public static javax.net.ssl.HostnameVerifier getDefaultHostnameVerifier();
+    method public static javax.net.ssl.SSLSocketFactory getDefaultSSLSocketFactory();
+    method public javax.net.ssl.HostnameVerifier getHostnameVerifier();
+    method public abstract java.security.cert.Certificate[] getLocalCertificates();
+    method public java.security.Principal getLocalPrincipal();
+    method public java.security.Principal getPeerPrincipal() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public javax.net.ssl.SSLSocketFactory getSSLSocketFactory();
+    method public abstract java.security.cert.Certificate[] getServerCertificates() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public static void setDefaultHostnameVerifier(javax.net.ssl.HostnameVerifier);
+    method public static void setDefaultSSLSocketFactory(javax.net.ssl.SSLSocketFactory);
+    method public void setHostnameVerifier(javax.net.ssl.HostnameVerifier);
+    method public void setSSLSocketFactory(javax.net.ssl.SSLSocketFactory);
+    field protected javax.net.ssl.HostnameVerifier hostnameVerifier;
+  }
+
+  public interface KeyManager {
+  }
+
+  public class KeyManagerFactory {
+    ctor protected KeyManagerFactory(javax.net.ssl.KeyManagerFactorySpi, java.security.Provider, String);
+    method public final String getAlgorithm();
+    method public static final String getDefaultAlgorithm();
+    method public static final javax.net.ssl.KeyManagerFactory getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static final javax.net.ssl.KeyManagerFactory getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static final javax.net.ssl.KeyManagerFactory getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final javax.net.ssl.KeyManager[] getKeyManagers();
+    method public final java.security.Provider getProvider();
+    method public final void init(java.security.KeyStore, char[]) throws java.security.KeyStoreException, java.security.NoSuchAlgorithmException, java.security.UnrecoverableKeyException;
+    method public final void init(javax.net.ssl.ManagerFactoryParameters) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public abstract class KeyManagerFactorySpi {
+    ctor public KeyManagerFactorySpi();
+    method protected abstract javax.net.ssl.KeyManager[] engineGetKeyManagers();
+    method protected abstract void engineInit(java.security.KeyStore, char[]) throws java.security.KeyStoreException, java.security.NoSuchAlgorithmException, java.security.UnrecoverableKeyException;
+    method protected abstract void engineInit(javax.net.ssl.ManagerFactoryParameters) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public class KeyStoreBuilderParameters implements javax.net.ssl.ManagerFactoryParameters {
+    ctor public KeyStoreBuilderParameters(java.security.KeyStore.Builder);
+    ctor public KeyStoreBuilderParameters(java.util.List<java.security.KeyStore.Builder>);
+    method public java.util.List<java.security.KeyStore.Builder> getParameters();
+  }
+
+  public interface ManagerFactoryParameters {
+  }
+
+  public final class SNIHostName extends javax.net.ssl.SNIServerName {
+    ctor public SNIHostName(String);
+    ctor public SNIHostName(byte[]);
+    method public static javax.net.ssl.SNIMatcher createSNIMatcher(String);
+    method public String getAsciiName();
+  }
+
+  public abstract class SNIMatcher {
+    ctor protected SNIMatcher(int);
+    method public final int getType();
+    method public abstract boolean matches(javax.net.ssl.SNIServerName);
+  }
+
+  public abstract class SNIServerName {
+    ctor protected SNIServerName(int, byte[]);
+    method public final byte[] getEncoded();
+    method public final int getType();
+  }
+
+  public class SSLContext {
+    ctor protected SSLContext(javax.net.ssl.SSLContextSpi, java.security.Provider, String);
+    method public final javax.net.ssl.SSLEngine createSSLEngine();
+    method public final javax.net.ssl.SSLEngine createSSLEngine(String, int);
+    method public final javax.net.ssl.SSLSessionContext getClientSessionContext();
+    method public static javax.net.ssl.SSLContext getDefault() throws java.security.NoSuchAlgorithmException;
+    method public final javax.net.ssl.SSLParameters getDefaultSSLParameters();
+    method public static javax.net.ssl.SSLContext getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static javax.net.ssl.SSLContext getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static javax.net.ssl.SSLContext getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final String getProtocol();
+    method public final java.security.Provider getProvider();
+    method public final javax.net.ssl.SSLSessionContext getServerSessionContext();
+    method public final javax.net.ssl.SSLServerSocketFactory getServerSocketFactory();
+    method public final javax.net.ssl.SSLSocketFactory getSocketFactory();
+    method public final javax.net.ssl.SSLParameters getSupportedSSLParameters();
+    method public final void init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) throws java.security.KeyManagementException;
+    method public static void setDefault(javax.net.ssl.SSLContext);
+  }
+
+  public abstract class SSLContextSpi {
+    ctor public SSLContextSpi();
+    method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine();
+    method protected abstract javax.net.ssl.SSLEngine engineCreateSSLEngine(String, int);
+    method protected abstract javax.net.ssl.SSLSessionContext engineGetClientSessionContext();
+    method protected javax.net.ssl.SSLParameters engineGetDefaultSSLParameters();
+    method protected abstract javax.net.ssl.SSLSessionContext engineGetServerSessionContext();
+    method protected abstract javax.net.ssl.SSLServerSocketFactory engineGetServerSocketFactory();
+    method protected abstract javax.net.ssl.SSLSocketFactory engineGetSocketFactory();
+    method protected javax.net.ssl.SSLParameters engineGetSupportedSSLParameters();
+    method protected abstract void engineInit(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) throws java.security.KeyManagementException;
+  }
+
+  public abstract class SSLEngine {
+    ctor protected SSLEngine();
+    ctor protected SSLEngine(String, int);
+    method public abstract void beginHandshake() throws javax.net.ssl.SSLException;
+    method public abstract void closeInbound() throws javax.net.ssl.SSLException;
+    method public abstract void closeOutbound();
+    method public String getApplicationProtocol();
+    method public abstract Runnable getDelegatedTask();
+    method public abstract boolean getEnableSessionCreation();
+    method public abstract String[] getEnabledCipherSuites();
+    method public abstract String[] getEnabledProtocols();
+    method public String getHandshakeApplicationProtocol();
+    method public java.util.function.BiFunction<javax.net.ssl.SSLEngine,java.util.List<java.lang.String>,java.lang.String> getHandshakeApplicationProtocolSelector();
+    method public javax.net.ssl.SSLSession getHandshakeSession();
+    method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus();
+    method public abstract boolean getNeedClientAuth();
+    method public String getPeerHost();
+    method public int getPeerPort();
+    method public javax.net.ssl.SSLParameters getSSLParameters();
+    method public abstract javax.net.ssl.SSLSession getSession();
+    method public abstract String[] getSupportedCipherSuites();
+    method public abstract String[] getSupportedProtocols();
+    method public abstract boolean getUseClientMode();
+    method public abstract boolean getWantClientAuth();
+    method public abstract boolean isInboundDone();
+    method public abstract boolean isOutboundDone();
+    method public abstract void setEnableSessionCreation(boolean);
+    method public abstract void setEnabledCipherSuites(String[]);
+    method public abstract void setEnabledProtocols(String[]);
+    method public void setHandshakeApplicationProtocolSelector(java.util.function.BiFunction<javax.net.ssl.SSLEngine,java.util.List<java.lang.String>,java.lang.String>);
+    method public abstract void setNeedClientAuth(boolean);
+    method public void setSSLParameters(javax.net.ssl.SSLParameters);
+    method public abstract void setUseClientMode(boolean);
+    method public abstract void setWantClientAuth(boolean);
+    method public javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+    method public javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[]) throws javax.net.ssl.SSLException;
+    method public abstract javax.net.ssl.SSLEngineResult unwrap(java.nio.ByteBuffer, java.nio.ByteBuffer[], int, int) throws javax.net.ssl.SSLException;
+    method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+    method public javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+    method public abstract javax.net.ssl.SSLEngineResult wrap(java.nio.ByteBuffer[], int, int, java.nio.ByteBuffer) throws javax.net.ssl.SSLException;
+  }
+
+  public class SSLEngineResult {
+    ctor public SSLEngineResult(javax.net.ssl.SSLEngineResult.Status, javax.net.ssl.SSLEngineResult.HandshakeStatus, int, int);
+    method public final int bytesConsumed();
+    method public final int bytesProduced();
+    method public final javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus();
+    method public final javax.net.ssl.SSLEngineResult.Status getStatus();
+  }
+
+  public enum SSLEngineResult.HandshakeStatus {
+    enum_constant public static final javax.net.ssl.SSLEngineResult.HandshakeStatus FINISHED;
+    enum_constant public static final javax.net.ssl.SSLEngineResult.HandshakeStatus NEED_TASK;
+    enum_constant public static final javax.net.ssl.SSLEngineResult.HandshakeStatus NEED_UNWRAP;
+    enum_constant public static final javax.net.ssl.SSLEngineResult.HandshakeStatus NEED_WRAP;
+    enum_constant public static final javax.net.ssl.SSLEngineResult.HandshakeStatus NOT_HANDSHAKING;
+  }
+
+  public enum SSLEngineResult.Status {
+    enum_constant public static final javax.net.ssl.SSLEngineResult.Status BUFFER_OVERFLOW;
+    enum_constant public static final javax.net.ssl.SSLEngineResult.Status BUFFER_UNDERFLOW;
+    enum_constant public static final javax.net.ssl.SSLEngineResult.Status CLOSED;
+    enum_constant public static final javax.net.ssl.SSLEngineResult.Status OK;
+  }
+
+  public class SSLException extends java.io.IOException {
+    ctor public SSLException(String);
+    ctor public SSLException(String, Throwable);
+    ctor public SSLException(Throwable);
+  }
+
+  public class SSLHandshakeException extends javax.net.ssl.SSLException {
+    ctor public SSLHandshakeException(String);
+  }
+
+  public class SSLKeyException extends javax.net.ssl.SSLException {
+    ctor public SSLKeyException(String);
+  }
+
+  public class SSLParameters {
+    ctor public SSLParameters();
+    ctor public SSLParameters(String[]);
+    ctor public SSLParameters(String[], String[]);
+    method public java.security.AlgorithmConstraints getAlgorithmConstraints();
+    method public String[] getApplicationProtocols();
+    method public String[] getCipherSuites();
+    method public String getEndpointIdentificationAlgorithm();
+    method public boolean getNeedClientAuth();
+    method public String[] getProtocols();
+    method public final java.util.Collection<javax.net.ssl.SNIMatcher> getSNIMatchers();
+    method public final java.util.List<javax.net.ssl.SNIServerName> getServerNames();
+    method public final boolean getUseCipherSuitesOrder();
+    method public boolean getWantClientAuth();
+    method public void setAlgorithmConstraints(java.security.AlgorithmConstraints);
+    method public void setApplicationProtocols(String[]);
+    method public void setCipherSuites(String[]);
+    method public void setEndpointIdentificationAlgorithm(String);
+    method public void setNeedClientAuth(boolean);
+    method public void setProtocols(String[]);
+    method public final void setSNIMatchers(java.util.Collection<javax.net.ssl.SNIMatcher>);
+    method public final void setServerNames(java.util.List<javax.net.ssl.SNIServerName>);
+    method public final void setUseCipherSuitesOrder(boolean);
+    method public void setWantClientAuth(boolean);
+  }
+
+  public class SSLPeerUnverifiedException extends javax.net.ssl.SSLException {
+    ctor public SSLPeerUnverifiedException(String);
+  }
+
+  public final class SSLPermission extends java.security.BasicPermission {
+    ctor public SSLPermission(String);
+    ctor public SSLPermission(String, String);
+  }
+
+  public class SSLProtocolException extends javax.net.ssl.SSLException {
+    ctor public SSLProtocolException(String);
+  }
+
+  public abstract class SSLServerSocket extends java.net.ServerSocket {
+    ctor protected SSLServerSocket() throws java.io.IOException;
+    ctor protected SSLServerSocket(int) throws java.io.IOException;
+    ctor protected SSLServerSocket(int, int) throws java.io.IOException;
+    ctor protected SSLServerSocket(int, int, java.net.InetAddress) throws java.io.IOException;
+    method public abstract boolean getEnableSessionCreation();
+    method public abstract String[] getEnabledCipherSuites();
+    method public abstract String[] getEnabledProtocols();
+    method public abstract boolean getNeedClientAuth();
+    method public javax.net.ssl.SSLParameters getSSLParameters();
+    method public abstract String[] getSupportedCipherSuites();
+    method public abstract String[] getSupportedProtocols();
+    method public abstract boolean getUseClientMode();
+    method public abstract boolean getWantClientAuth();
+    method public abstract void setEnableSessionCreation(boolean);
+    method public abstract void setEnabledCipherSuites(String[]);
+    method public abstract void setEnabledProtocols(String[]);
+    method public abstract void setNeedClientAuth(boolean);
+    method public void setSSLParameters(javax.net.ssl.SSLParameters);
+    method public abstract void setUseClientMode(boolean);
+    method public abstract void setWantClientAuth(boolean);
+  }
+
+  public abstract class SSLServerSocketFactory extends javax.net.ServerSocketFactory {
+    ctor protected SSLServerSocketFactory();
+    method public static javax.net.ServerSocketFactory getDefault();
+    method public abstract String[] getDefaultCipherSuites();
+    method public abstract String[] getSupportedCipherSuites();
+  }
+
+  public interface SSLSession {
+    method public int getApplicationBufferSize();
+    method public String getCipherSuite();
+    method public long getCreationTime();
+    method public byte[] getId();
+    method public long getLastAccessedTime();
+    method public java.security.cert.Certificate[] getLocalCertificates();
+    method public java.security.Principal getLocalPrincipal();
+    method public int getPacketBufferSize();
+    method public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public java.security.cert.Certificate[] getPeerCertificates() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public String getPeerHost();
+    method public int getPeerPort();
+    method public java.security.Principal getPeerPrincipal() throws javax.net.ssl.SSLPeerUnverifiedException;
+    method public String getProtocol();
+    method public javax.net.ssl.SSLSessionContext getSessionContext();
+    method public Object getValue(String);
+    method public String[] getValueNames();
+    method public void invalidate();
+    method public boolean isValid();
+    method public void putValue(String, Object);
+    method public void removeValue(String);
+  }
+
+  public class SSLSessionBindingEvent extends java.util.EventObject {
+    ctor public SSLSessionBindingEvent(javax.net.ssl.SSLSession, String);
+    method public String getName();
+    method public javax.net.ssl.SSLSession getSession();
+  }
+
+  public interface SSLSessionBindingListener extends java.util.EventListener {
+    method public void valueBound(javax.net.ssl.SSLSessionBindingEvent);
+    method public void valueUnbound(javax.net.ssl.SSLSessionBindingEvent);
+  }
+
+  public interface SSLSessionContext {
+    method public java.util.Enumeration<byte[]> getIds();
+    method public javax.net.ssl.SSLSession getSession(byte[]);
+    method public int getSessionCacheSize();
+    method public int getSessionTimeout();
+    method public void setSessionCacheSize(int) throws java.lang.IllegalArgumentException;
+    method public void setSessionTimeout(int) throws java.lang.IllegalArgumentException;
+  }
+
+  public abstract class SSLSocket extends java.net.Socket {
+    ctor protected SSLSocket();
+    ctor protected SSLSocket(String, int) throws java.io.IOException, java.net.UnknownHostException;
+    ctor protected SSLSocket(java.net.InetAddress, int) throws java.io.IOException;
+    ctor protected SSLSocket(String, int, java.net.InetAddress, int) throws java.io.IOException, java.net.UnknownHostException;
+    ctor protected SSLSocket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
+    method public abstract void addHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener);
+    method public String getApplicationProtocol();
+    method public abstract boolean getEnableSessionCreation();
+    method public abstract String[] getEnabledCipherSuites();
+    method public abstract String[] getEnabledProtocols();
+    method public String getHandshakeApplicationProtocol();
+    method public java.util.function.BiFunction<javax.net.ssl.SSLSocket,java.util.List<java.lang.String>,java.lang.String> getHandshakeApplicationProtocolSelector();
+    method public javax.net.ssl.SSLSession getHandshakeSession();
+    method public abstract boolean getNeedClientAuth();
+    method public javax.net.ssl.SSLParameters getSSLParameters();
+    method public abstract javax.net.ssl.SSLSession getSession();
+    method public abstract String[] getSupportedCipherSuites();
+    method public abstract String[] getSupportedProtocols();
+    method public abstract boolean getUseClientMode();
+    method public abstract boolean getWantClientAuth();
+    method public abstract void removeHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener);
+    method public abstract void setEnableSessionCreation(boolean);
+    method public abstract void setEnabledCipherSuites(String[]);
+    method public abstract void setEnabledProtocols(String[]);
+    method public void setHandshakeApplicationProtocolSelector(java.util.function.BiFunction<javax.net.ssl.SSLSocket,java.util.List<java.lang.String>,java.lang.String>);
+    method public abstract void setNeedClientAuth(boolean);
+    method public void setSSLParameters(javax.net.ssl.SSLParameters);
+    method public abstract void setUseClientMode(boolean);
+    method public abstract void setWantClientAuth(boolean);
+    method public abstract void startHandshake() throws java.io.IOException;
+  }
+
+  public abstract class SSLSocketFactory extends javax.net.SocketFactory {
+    ctor public SSLSocketFactory();
+    method public abstract java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException;
+    method public static javax.net.SocketFactory getDefault();
+    method public abstract String[] getDefaultCipherSuites();
+    method public abstract String[] getSupportedCipherSuites();
+  }
+
+  public final class StandardConstants {
+    field public static final int SNI_HOST_NAME = 0; // 0x0
+  }
+
+  public interface TrustManager {
+  }
+
+  public class TrustManagerFactory {
+    ctor protected TrustManagerFactory(javax.net.ssl.TrustManagerFactorySpi, java.security.Provider, String);
+    method public final String getAlgorithm();
+    method public static final String getDefaultAlgorithm();
+    method public static final javax.net.ssl.TrustManagerFactory getInstance(String) throws java.security.NoSuchAlgorithmException;
+    method public static final javax.net.ssl.TrustManagerFactory getInstance(String, String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
+    method public static final javax.net.ssl.TrustManagerFactory getInstance(String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
+    method public final java.security.Provider getProvider();
+    method public final javax.net.ssl.TrustManager[] getTrustManagers();
+    method public final void init(java.security.KeyStore) throws java.security.KeyStoreException;
+    method public final void init(javax.net.ssl.ManagerFactoryParameters) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public abstract class TrustManagerFactorySpi {
+    ctor public TrustManagerFactorySpi();
+    method protected abstract javax.net.ssl.TrustManager[] engineGetTrustManagers();
+    method protected abstract void engineInit(java.security.KeyStore) throws java.security.KeyStoreException;
+    method protected abstract void engineInit(javax.net.ssl.ManagerFactoryParameters) throws java.security.InvalidAlgorithmParameterException;
+  }
+
+  public abstract class X509ExtendedKeyManager implements javax.net.ssl.X509KeyManager {
+    ctor protected X509ExtendedKeyManager();
+    method public String chooseEngineClientAlias(String[], java.security.Principal[], javax.net.ssl.SSLEngine);
+    method public String chooseEngineServerAlias(String, java.security.Principal[], javax.net.ssl.SSLEngine);
+  }
+
+  public abstract class X509ExtendedTrustManager implements javax.net.ssl.X509TrustManager {
+    ctor public X509ExtendedTrustManager();
+    method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], String, java.net.Socket) throws java.security.cert.CertificateException;
+    method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
+    method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], String, java.net.Socket) throws java.security.cert.CertificateException;
+    method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException;
+  }
+
+  public interface X509KeyManager extends javax.net.ssl.KeyManager {
+    method public String chooseClientAlias(String[], java.security.Principal[], java.net.Socket);
+    method public String chooseServerAlias(String, java.security.Principal[], java.net.Socket);
+    method public java.security.cert.X509Certificate[] getCertificateChain(String);
+    method public String[] getClientAliases(String, java.security.Principal[]);
+    method public java.security.PrivateKey getPrivateKey(String);
+    method public String[] getServerAliases(String, java.security.Principal[]);
+  }
+
+  public interface X509TrustManager extends javax.net.ssl.TrustManager {
+    method public void checkClientTrusted(java.security.cert.X509Certificate[], String) throws java.security.cert.CertificateException;
+    method public void checkServerTrusted(java.security.cert.X509Certificate[], String) throws java.security.cert.CertificateException;
+    method public java.security.cert.X509Certificate[] getAcceptedIssuers();
+  }
+
+}
+
+package javax.security.auth {
+
+  public final class AuthPermission extends java.security.BasicPermission {
+    ctor public AuthPermission(String);
+    ctor public AuthPermission(String, String);
+  }
+
+  public class DestroyFailedException extends java.lang.Exception {
+    ctor public DestroyFailedException();
+    ctor public DestroyFailedException(String);
+  }
+
+  public interface Destroyable {
+    method public default void destroy() throws javax.security.auth.DestroyFailedException;
+    method public default boolean isDestroyed();
+  }
+
+  public final class PrivateCredentialPermission extends java.security.Permission {
+    ctor public PrivateCredentialPermission(String, String);
+    method public String getActions();
+    method public String getCredentialClass();
+    method public String[][] getPrincipals();
+    method public boolean implies(java.security.Permission);
+  }
+
+  public final class Subject implements java.io.Serializable {
+    ctor public Subject();
+    ctor public Subject(boolean, java.util.Set<? extends java.security.Principal>, java.util.Set<?>, java.util.Set<?>);
+    method public static <T> T doAs(javax.security.auth.Subject, java.security.PrivilegedAction<T>);
+    method public static <T> T doAs(javax.security.auth.Subject, java.security.PrivilegedExceptionAction<T>) throws java.security.PrivilegedActionException;
+    method public static <T> T doAsPrivileged(javax.security.auth.Subject, java.security.PrivilegedAction<T>, java.security.AccessControlContext);
+    method public static <T> T doAsPrivileged(javax.security.auth.Subject, java.security.PrivilegedExceptionAction<T>, java.security.AccessControlContext) throws java.security.PrivilegedActionException;
+    method public java.util.Set<java.security.Principal> getPrincipals();
+    method public <T extends java.security.Principal> java.util.Set<T> getPrincipals(Class<T>);
+    method public java.util.Set<java.lang.Object> getPrivateCredentials();
+    method public <T> java.util.Set<T> getPrivateCredentials(Class<T>);
+    method public java.util.Set<java.lang.Object> getPublicCredentials();
+    method public <T> java.util.Set<T> getPublicCredentials(Class<T>);
+    method public static javax.security.auth.Subject getSubject(java.security.AccessControlContext);
+    method public boolean isReadOnly();
+    method public void setReadOnly();
+  }
+
+  public class SubjectDomainCombiner implements java.security.DomainCombiner {
+    ctor public SubjectDomainCombiner(javax.security.auth.Subject);
+    method public java.security.ProtectionDomain[] combine(java.security.ProtectionDomain[], java.security.ProtectionDomain[]);
+    method public javax.security.auth.Subject getSubject();
+  }
+
+}
+
+package javax.security.auth.callback {
+
+  public interface Callback {
+  }
+
+  public interface CallbackHandler {
+    method public void handle(javax.security.auth.callback.Callback[]) throws java.io.IOException, javax.security.auth.callback.UnsupportedCallbackException;
+  }
+
+  public class PasswordCallback implements javax.security.auth.callback.Callback java.io.Serializable {
+    ctor public PasswordCallback(String, boolean);
+    method public void clearPassword();
+    method public char[] getPassword();
+    method public String getPrompt();
+    method public boolean isEchoOn();
+    method public void setPassword(char[]);
+  }
+
+  public class UnsupportedCallbackException extends java.lang.Exception {
+    ctor public UnsupportedCallbackException(javax.security.auth.callback.Callback);
+    ctor public UnsupportedCallbackException(javax.security.auth.callback.Callback, String);
+    method public javax.security.auth.callback.Callback getCallback();
+  }
+
+}
+
+package javax.security.auth.login {
+
+  public class LoginException extends java.security.GeneralSecurityException {
+    ctor public LoginException();
+    ctor public LoginException(String);
+  }
+
+}
+
+package javax.security.auth.x500 {
+
+  public final class X500Principal implements java.security.Principal java.io.Serializable {
+    ctor public X500Principal(String);
+    ctor public X500Principal(String, java.util.Map<java.lang.String,java.lang.String>);
+    ctor public X500Principal(byte[]);
+    ctor public X500Principal(java.io.InputStream);
+    method public byte[] getEncoded();
+    method public String getName();
+    method public String getName(String);
+    method public String getName(String, java.util.Map<java.lang.String,java.lang.String>);
+    field public static final String CANONICAL = "CANONICAL";
+    field public static final String RFC1779 = "RFC1779";
+    field public static final String RFC2253 = "RFC2253";
+  }
+
+}
+
+package javax.security.cert {
+
+  public abstract class Certificate {
+    ctor public Certificate();
+    method public abstract byte[] getEncoded() throws javax.security.cert.CertificateEncodingException;
+    method public abstract java.security.PublicKey getPublicKey();
+    method public abstract String toString();
+    method public abstract void verify(java.security.PublicKey) throws javax.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+    method public abstract void verify(java.security.PublicKey, String) throws javax.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
+  }
+
+  public class CertificateEncodingException extends javax.security.cert.CertificateException {
+    ctor public CertificateEncodingException();
+    ctor public CertificateEncodingException(String);
+  }
+
+  public class CertificateException extends java.lang.Exception {
+    ctor public CertificateException();
+    ctor public CertificateException(String);
+  }
+
+  public class CertificateExpiredException extends javax.security.cert.CertificateException {
+    ctor public CertificateExpiredException();
+    ctor public CertificateExpiredException(String);
+  }
+
+  public class CertificateNotYetValidException extends javax.security.cert.CertificateException {
+    ctor public CertificateNotYetValidException();
+    ctor public CertificateNotYetValidException(String);
+  }
+
+  public class CertificateParsingException extends javax.security.cert.CertificateException {
+    ctor public CertificateParsingException();
+    ctor public CertificateParsingException(String);
+  }
+
+  public abstract class X509Certificate extends javax.security.cert.Certificate {
+    ctor public X509Certificate();
+    method public abstract void checkValidity() throws javax.security.cert.CertificateExpiredException, javax.security.cert.CertificateNotYetValidException;
+    method public abstract void checkValidity(java.util.Date) throws javax.security.cert.CertificateExpiredException, javax.security.cert.CertificateNotYetValidException;
+    method public static final javax.security.cert.X509Certificate getInstance(java.io.InputStream) throws javax.security.cert.CertificateException;
+    method public static final javax.security.cert.X509Certificate getInstance(byte[]) throws javax.security.cert.CertificateException;
+    method public abstract java.security.Principal getIssuerDN();
+    method public abstract java.util.Date getNotAfter();
+    method public abstract java.util.Date getNotBefore();
+    method public abstract java.math.BigInteger getSerialNumber();
+    method public abstract String getSigAlgName();
+    method public abstract String getSigAlgOID();
+    method public abstract byte[] getSigAlgParams();
+    method public abstract java.security.Principal getSubjectDN();
+    method public abstract int getVersion();
+  }
+
+}
+
+package javax.sql {
+
+  public interface CommonDataSource {
+    method public java.io.PrintWriter getLogWriter() throws java.sql.SQLException;
+    method public int getLoginTimeout() throws java.sql.SQLException;
+    method public java.util.logging.Logger getParentLogger() throws java.sql.SQLFeatureNotSupportedException;
+    method public void setLogWriter(java.io.PrintWriter) throws java.sql.SQLException;
+    method public void setLoginTimeout(int) throws java.sql.SQLException;
+  }
+
+  public class ConnectionEvent extends java.util.EventObject {
+    ctor public ConnectionEvent(javax.sql.PooledConnection);
+    ctor public ConnectionEvent(javax.sql.PooledConnection, java.sql.SQLException);
+    method public java.sql.SQLException getSQLException();
+  }
+
+  public interface ConnectionEventListener extends java.util.EventListener {
+    method public void connectionClosed(javax.sql.ConnectionEvent);
+    method public void connectionErrorOccurred(javax.sql.ConnectionEvent);
+  }
+
+  public interface ConnectionPoolDataSource extends javax.sql.CommonDataSource {
+    method public javax.sql.PooledConnection getPooledConnection() throws java.sql.SQLException;
+    method public javax.sql.PooledConnection getPooledConnection(String, String) throws java.sql.SQLException;
+  }
+
+  public interface DataSource extends javax.sql.CommonDataSource java.sql.Wrapper {
+    method public java.sql.Connection getConnection() throws java.sql.SQLException;
+    method public java.sql.Connection getConnection(String, String) throws java.sql.SQLException;
+  }
+
+  public interface PooledConnection {
+    method public void addConnectionEventListener(javax.sql.ConnectionEventListener);
+    method public void addStatementEventListener(javax.sql.StatementEventListener);
+    method public void close() throws java.sql.SQLException;
+    method public java.sql.Connection getConnection() throws java.sql.SQLException;
+    method public void removeConnectionEventListener(javax.sql.ConnectionEventListener);
+    method public void removeStatementEventListener(javax.sql.StatementEventListener);
+  }
+
+  public interface RowSet extends java.sql.ResultSet {
+    method public void addRowSetListener(javax.sql.RowSetListener);
+    method public void clearParameters() throws java.sql.SQLException;
+    method public void execute() throws java.sql.SQLException;
+    method public String getCommand();
+    method public String getDataSourceName();
+    method public boolean getEscapeProcessing() throws java.sql.SQLException;
+    method public int getMaxFieldSize() throws java.sql.SQLException;
+    method public int getMaxRows() throws java.sql.SQLException;
+    method public String getPassword();
+    method public int getQueryTimeout() throws java.sql.SQLException;
+    method public int getTransactionIsolation();
+    method public java.util.Map<java.lang.String,java.lang.Class<?>> getTypeMap() throws java.sql.SQLException;
+    method public String getUrl() throws java.sql.SQLException;
+    method public String getUsername();
+    method public boolean isReadOnly();
+    method public void removeRowSetListener(javax.sql.RowSetListener);
+    method public void setArray(int, java.sql.Array) throws java.sql.SQLException;
+    method public void setAsciiStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setAsciiStream(String, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setAsciiStream(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void setAsciiStream(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBigDecimal(int, java.math.BigDecimal) throws java.sql.SQLException;
+    method public void setBigDecimal(String, java.math.BigDecimal) throws java.sql.SQLException;
+    method public void setBinaryStream(int, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setBinaryStream(String, java.io.InputStream, int) throws java.sql.SQLException;
+    method public void setBinaryStream(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBinaryStream(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBlob(int, java.sql.Blob) throws java.sql.SQLException;
+    method public void setBlob(int, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setBlob(int, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBlob(String, java.io.InputStream, long) throws java.sql.SQLException;
+    method public void setBlob(String, java.sql.Blob) throws java.sql.SQLException;
+    method public void setBlob(String, java.io.InputStream) throws java.sql.SQLException;
+    method public void setBoolean(int, boolean) throws java.sql.SQLException;
+    method public void setBoolean(String, boolean) throws java.sql.SQLException;
+    method public void setByte(int, byte) throws java.sql.SQLException;
+    method public void setByte(String, byte) throws java.sql.SQLException;
+    method public void setBytes(int, byte[]) throws java.sql.SQLException;
+    method public void setBytes(String, byte[]) throws java.sql.SQLException;
+    method public void setCharacterStream(int, java.io.Reader, int) throws java.sql.SQLException;
+    method public void setCharacterStream(String, java.io.Reader, int) throws java.sql.SQLException;
+    method public void setCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setCharacterStream(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setClob(int, java.sql.Clob) throws java.sql.SQLException;
+    method public void setClob(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setClob(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setClob(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setClob(String, java.sql.Clob) throws java.sql.SQLException;
+    method public void setClob(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setCommand(String) throws java.sql.SQLException;
+    method public void setConcurrency(int) throws java.sql.SQLException;
+    method public void setDataSourceName(String) throws java.sql.SQLException;
+    method public void setDate(int, java.sql.Date) throws java.sql.SQLException;
+    method public void setDate(int, java.sql.Date, java.util.Calendar) throws java.sql.SQLException;
+    method public void setDate(String, java.sql.Date) throws java.sql.SQLException;
+    method public void setDate(String, java.sql.Date, java.util.Calendar) throws java.sql.SQLException;
+    method public void setDouble(int, double) throws java.sql.SQLException;
+    method public void setDouble(String, double) throws java.sql.SQLException;
+    method public void setEscapeProcessing(boolean) throws java.sql.SQLException;
+    method public void setFloat(int, float) throws java.sql.SQLException;
+    method public void setFloat(String, float) throws java.sql.SQLException;
+    method public void setInt(int, int) throws java.sql.SQLException;
+    method public void setInt(String, int) throws java.sql.SQLException;
+    method public void setLong(int, long) throws java.sql.SQLException;
+    method public void setLong(String, long) throws java.sql.SQLException;
+    method public void setMaxFieldSize(int) throws java.sql.SQLException;
+    method public void setMaxRows(int) throws java.sql.SQLException;
+    method public void setNCharacterStream(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setNCharacterStream(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNCharacterStream(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNCharacterStream(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setNClob(String, java.sql.NClob) throws java.sql.SQLException;
+    method public void setNClob(String, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNClob(String, java.io.Reader) throws java.sql.SQLException;
+    method public void setNClob(int, java.io.Reader, long) throws java.sql.SQLException;
+    method public void setNClob(int, java.sql.NClob) throws java.sql.SQLException;
+    method public void setNClob(int, java.io.Reader) throws java.sql.SQLException;
+    method public void setNString(int, String) throws java.sql.SQLException;
+    method public void setNString(String, String) throws java.sql.SQLException;
+    method public void setNull(int, int) throws java.sql.SQLException;
+    method public void setNull(String, int) throws java.sql.SQLException;
+    method public void setNull(int, int, String) throws java.sql.SQLException;
+    method public void setNull(String, int, String) throws java.sql.SQLException;
+    method public void setObject(int, Object, int, int) throws java.sql.SQLException;
+    method public void setObject(String, Object, int, int) throws java.sql.SQLException;
+    method public void setObject(int, Object, int) throws java.sql.SQLException;
+    method public void setObject(String, Object, int) throws java.sql.SQLException;
+    method public void setObject(String, Object) throws java.sql.SQLException;
+    method public void setObject(int, Object) throws java.sql.SQLException;
+    method public void setPassword(String) throws java.sql.SQLException;
+    method public void setQueryTimeout(int) throws java.sql.SQLException;
+    method public void setReadOnly(boolean) throws java.sql.SQLException;
+    method public void setRef(int, java.sql.Ref) throws java.sql.SQLException;
+    method public void setRowId(int, java.sql.RowId) throws java.sql.SQLException;
+    method public void setRowId(String, java.sql.RowId) throws java.sql.SQLException;
+    method public void setSQLXML(int, java.sql.SQLXML) throws java.sql.SQLException;
+    method public void setSQLXML(String, java.sql.SQLXML) throws java.sql.SQLException;
+    method public void setShort(int, short) throws java.sql.SQLException;
+    method public void setShort(String, short) throws java.sql.SQLException;
+    method public void setString(int, String) throws java.sql.SQLException;
+    method public void setString(String, String) throws java.sql.SQLException;
+    method public void setTime(int, java.sql.Time) throws java.sql.SQLException;
+    method public void setTime(int, java.sql.Time, java.util.Calendar) throws java.sql.SQLException;
+    method public void setTime(String, java.sql.Time) throws java.sql.SQLException;
+    method public void setTime(String, java.sql.Time, java.util.Calendar) throws java.sql.SQLException;
+    method public void setTimestamp(int, java.sql.Timestamp) throws java.sql.SQLException;
+    method public void setTimestamp(String, java.sql.Timestamp) throws java.sql.SQLException;
+    method public void setTimestamp(int, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
+    method public void setTimestamp(String, java.sql.Timestamp, java.util.Calendar) throws java.sql.SQLException;
+    method public void setTransactionIsolation(int) throws java.sql.SQLException;
+    method public void setType(int) throws java.sql.SQLException;
+    method public void setTypeMap(java.util.Map<java.lang.String,java.lang.Class<?>>) throws java.sql.SQLException;
+    method public void setURL(int, java.net.URL) throws java.sql.SQLException;
+    method public void setUrl(String) throws java.sql.SQLException;
+    method public void setUsername(String) throws java.sql.SQLException;
+  }
+
+  public class RowSetEvent extends java.util.EventObject {
+    ctor public RowSetEvent(javax.sql.RowSet);
+  }
+
+  public interface RowSetInternal {
+    method public java.sql.Connection getConnection() throws java.sql.SQLException;
+    method public java.sql.ResultSet getOriginal() throws java.sql.SQLException;
+    method public java.sql.ResultSet getOriginalRow() throws java.sql.SQLException;
+    method public Object[] getParams() throws java.sql.SQLException;
+    method public void setMetaData(javax.sql.RowSetMetaData) throws java.sql.SQLException;
+  }
+
+  public interface RowSetListener extends java.util.EventListener {
+    method public void cursorMoved(javax.sql.RowSetEvent);
+    method public void rowChanged(javax.sql.RowSetEvent);
+    method public void rowSetChanged(javax.sql.RowSetEvent);
+  }
+
+  public interface RowSetMetaData extends java.sql.ResultSetMetaData {
+    method public void setAutoIncrement(int, boolean) throws java.sql.SQLException;
+    method public void setCaseSensitive(int, boolean) throws java.sql.SQLException;
+    method public void setCatalogName(int, String) throws java.sql.SQLException;
+    method public void setColumnCount(int) throws java.sql.SQLException;
+    method public void setColumnDisplaySize(int, int) throws java.sql.SQLException;
+    method public void setColumnLabel(int, String) throws java.sql.SQLException;
+    method public void setColumnName(int, String) throws java.sql.SQLException;
+    method public void setColumnType(int, int) throws java.sql.SQLException;
+    method public void setColumnTypeName(int, String) throws java.sql.SQLException;
+    method public void setCurrency(int, boolean) throws java.sql.SQLException;
+    method public void setNullable(int, int) throws java.sql.SQLException;
+    method public void setPrecision(int, int) throws java.sql.SQLException;
+    method public void setScale(int, int) throws java.sql.SQLException;
+    method public void setSchemaName(int, String) throws java.sql.SQLException;
+    method public void setSearchable(int, boolean) throws java.sql.SQLException;
+    method public void setSigned(int, boolean) throws java.sql.SQLException;
+    method public void setTableName(int, String) throws java.sql.SQLException;
+  }
+
+  public interface RowSetReader {
+    method public void readData(javax.sql.RowSetInternal) throws java.sql.SQLException;
+  }
+
+  public interface RowSetWriter {
+    method public boolean writeData(javax.sql.RowSetInternal) throws java.sql.SQLException;
+  }
+
+  public class StatementEvent extends java.util.EventObject {
+    ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement);
+    ctor public StatementEvent(javax.sql.PooledConnection, java.sql.PreparedStatement, java.sql.SQLException);
+    method public java.sql.SQLException getSQLException();
+    method public java.sql.PreparedStatement getStatement();
+  }
+
+  public interface StatementEventListener extends java.util.EventListener {
+    method public void statementClosed(javax.sql.StatementEvent);
+    method public void statementErrorOccurred(javax.sql.StatementEvent);
+  }
+
+}
+
+package javax.xml {
+
+  public final class XMLConstants {
+    field public static final String DEFAULT_NS_PREFIX = "";
+    field public static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
+    field public static final String NULL_NS_URI = "";
+    field public static final String RELAXNG_NS_URI = "http://relaxng.org/ns/structure/1.0";
+    field public static final String W3C_XML_SCHEMA_INSTANCE_NS_URI = "http://www.w3.org/2001/XMLSchema-instance";
+    field public static final String W3C_XML_SCHEMA_NS_URI = "http://www.w3.org/2001/XMLSchema";
+    field public static final String W3C_XPATH_DATATYPE_NS_URI = "http://www.w3.org/2003/11/xpath-datatypes";
+    field public static final String XMLNS_ATTRIBUTE = "xmlns";
+    field public static final String XMLNS_ATTRIBUTE_NS_URI = "http://www.w3.org/2000/xmlns/";
+    field public static final String XML_DTD_NS_URI = "http://www.w3.org/TR/REC-xml";
+    field public static final String XML_NS_PREFIX = "xml";
+    field public static final String XML_NS_URI = "http://www.w3.org/XML/1998/namespace";
+  }
+
+}
+
+package javax.xml.datatype {
+
+  public class DatatypeConfigurationException extends java.lang.Exception {
+    ctor public DatatypeConfigurationException();
+    ctor public DatatypeConfigurationException(String);
+    ctor public DatatypeConfigurationException(String, Throwable);
+    ctor public DatatypeConfigurationException(Throwable);
+  }
+
+  public final class DatatypeConstants {
+    field public static final int APRIL = 4; // 0x4
+    field public static final int AUGUST = 8; // 0x8
+    field public static final javax.xml.namespace.QName DATE;
+    field public static final javax.xml.namespace.QName DATETIME;
+    field public static final javax.xml.datatype.DatatypeConstants.Field DAYS;
+    field public static final int DECEMBER = 12; // 0xc
+    field public static final javax.xml.namespace.QName DURATION;
+    field public static final javax.xml.namespace.QName DURATION_DAYTIME;
+    field public static final javax.xml.namespace.QName DURATION_YEARMONTH;
+    field public static final int EQUAL = 0; // 0x0
+    field public static final int FEBRUARY = 2; // 0x2
+    field public static final int FIELD_UNDEFINED = -2147483648; // 0x80000000
+    field public static final javax.xml.namespace.QName GDAY;
+    field public static final javax.xml.namespace.QName GMONTH;
+    field public static final javax.xml.namespace.QName GMONTHDAY;
+    field public static final int GREATER = 1; // 0x1
+    field public static final javax.xml.namespace.QName GYEAR;
+    field public static final javax.xml.namespace.QName GYEARMONTH;
+    field public static final javax.xml.datatype.DatatypeConstants.Field HOURS;
+    field public static final int INDETERMINATE = 2; // 0x2
+    field public static final int JANUARY = 1; // 0x1
+    field public static final int JULY = 7; // 0x7
+    field public static final int JUNE = 6; // 0x6
+    field public static final int LESSER = -1; // 0xffffffff
+    field public static final int MARCH = 3; // 0x3
+    field public static final int MAX_TIMEZONE_OFFSET = -840; // 0xfffffcb8
+    field public static final int MAY = 5; // 0x5
+    field public static final javax.xml.datatype.DatatypeConstants.Field MINUTES;
+    field public static final int MIN_TIMEZONE_OFFSET = 840; // 0x348
+    field public static final javax.xml.datatype.DatatypeConstants.Field MONTHS;
+    field public static final int NOVEMBER = 11; // 0xb
+    field public static final int OCTOBER = 10; // 0xa
+    field public static final javax.xml.datatype.DatatypeConstants.Field SECONDS;
+    field public static final int SEPTEMBER = 9; // 0x9
+    field public static final javax.xml.namespace.QName TIME;
+    field public static final javax.xml.datatype.DatatypeConstants.Field YEARS;
+  }
+
+  public static final class DatatypeConstants.Field {
+    method public int getId();
+  }
+
+  public abstract class DatatypeFactory {
+    ctor protected DatatypeFactory();
+    method public abstract javax.xml.datatype.Duration newDuration(String);
+    method public abstract javax.xml.datatype.Duration newDuration(long);
+    method public abstract javax.xml.datatype.Duration newDuration(boolean, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigDecimal);
+    method public javax.xml.datatype.Duration newDuration(boolean, int, int, int, int, int, int);
+    method public javax.xml.datatype.Duration newDurationDayTime(String);
+    method public javax.xml.datatype.Duration newDurationDayTime(long);
+    method public javax.xml.datatype.Duration newDurationDayTime(boolean, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger, java.math.BigInteger);
+    method public javax.xml.datatype.Duration newDurationDayTime(boolean, int, int, int, int);
+    method public javax.xml.datatype.Duration newDurationYearMonth(String);
+    method public javax.xml.datatype.Duration newDurationYearMonth(long);
+    method public javax.xml.datatype.Duration newDurationYearMonth(boolean, java.math.BigInteger, java.math.BigInteger);
+    method public javax.xml.datatype.Duration newDurationYearMonth(boolean, int, int);
+    method public static javax.xml.datatype.DatatypeFactory newInstance() throws javax.xml.datatype.DatatypeConfigurationException;
+    method public static javax.xml.datatype.DatatypeFactory newInstance(String, ClassLoader) throws javax.xml.datatype.DatatypeConfigurationException;
+    method public abstract javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendar();
+    method public abstract javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendar(String);
+    method public abstract javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendar(java.util.GregorianCalendar);
+    method public abstract javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendar(java.math.BigInteger, int, int, int, int, int, java.math.BigDecimal, int);
+    method public javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendar(int, int, int, int, int, int, int, int);
+    method public javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendarDate(int, int, int, int);
+    method public javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendarTime(int, int, int, int);
+    method public javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendarTime(int, int, int, java.math.BigDecimal, int);
+    method public javax.xml.datatype.XMLGregorianCalendar newXMLGregorianCalendarTime(int, int, int, int, int);
+    field public static final String DATATYPEFACTORY_IMPLEMENTATION_CLASS;
+    field public static final String DATATYPEFACTORY_PROPERTY = "javax.xml.datatype.DatatypeFactory";
+  }
+
+  public abstract class Duration {
+    ctor public Duration();
+    method public abstract javax.xml.datatype.Duration add(javax.xml.datatype.Duration);
+    method public abstract void addTo(java.util.Calendar);
+    method public void addTo(java.util.Date);
+    method public abstract int compare(javax.xml.datatype.Duration);
+    method public int getDays();
+    method public abstract Number getField(javax.xml.datatype.DatatypeConstants.Field);
+    method public int getHours();
+    method public int getMinutes();
+    method public int getMonths();
+    method public int getSeconds();
+    method public abstract int getSign();
+    method public long getTimeInMillis(java.util.Calendar);
+    method public long getTimeInMillis(java.util.Date);
+    method public javax.xml.namespace.QName getXMLSchemaType();
+    method public int getYears();
+    method public abstract int hashCode();
+    method public boolean isLongerThan(javax.xml.datatype.Duration);
+    method public abstract boolean isSet(javax.xml.datatype.DatatypeConstants.Field);
+    method public boolean isShorterThan(javax.xml.datatype.Duration);
+    method public javax.xml.datatype.Duration multiply(int);
+    method public abstract javax.xml.datatype.Duration multiply(java.math.BigDecimal);
+    method public abstract javax.xml.datatype.Duration negate();
+    method public abstract javax.xml.datatype.Duration normalizeWith(java.util.Calendar);
+    method public javax.xml.datatype.Duration subtract(javax.xml.datatype.Duration);
+  }
+
+  public abstract class XMLGregorianCalendar implements java.lang.Cloneable {
+    ctor public XMLGregorianCalendar();
+    method public abstract void add(javax.xml.datatype.Duration);
+    method public abstract void clear();
+    method public abstract Object clone();
+    method public abstract int compare(javax.xml.datatype.XMLGregorianCalendar);
+    method public abstract int getDay();
+    method public abstract java.math.BigInteger getEon();
+    method public abstract java.math.BigInteger getEonAndYear();
+    method public abstract java.math.BigDecimal getFractionalSecond();
+    method public abstract int getHour();
+    method public int getMillisecond();
+    method public abstract int getMinute();
+    method public abstract int getMonth();
+    method public abstract int getSecond();
+    method public abstract java.util.TimeZone getTimeZone(int);
+    method public abstract int getTimezone();
+    method public abstract javax.xml.namespace.QName getXMLSchemaType();
+    method public abstract int getYear();
+    method public abstract boolean isValid();
+    method public abstract javax.xml.datatype.XMLGregorianCalendar normalize();
+    method public abstract void reset();
+    method public abstract void setDay(int);
+    method public abstract void setFractionalSecond(java.math.BigDecimal);
+    method public abstract void setHour(int);
+    method public abstract void setMillisecond(int);
+    method public abstract void setMinute(int);
+    method public abstract void setMonth(int);
+    method public abstract void setSecond(int);
+    method public void setTime(int, int, int);
+    method public void setTime(int, int, int, java.math.BigDecimal);
+    method public void setTime(int, int, int, int);
+    method public abstract void setTimezone(int);
+    method public abstract void setYear(java.math.BigInteger);
+    method public abstract void setYear(int);
+    method public abstract java.util.GregorianCalendar toGregorianCalendar();
+    method public abstract java.util.GregorianCalendar toGregorianCalendar(java.util.TimeZone, java.util.Locale, javax.xml.datatype.XMLGregorianCalendar);
+    method public abstract String toXMLFormat();
+  }
+
+}
+
+package javax.xml.namespace {
+
+  public interface NamespaceContext {
+    method public String getNamespaceURI(String);
+    method public String getPrefix(String);
+    method public java.util.Iterator getPrefixes(String);
+  }
+
+  public class QName implements java.io.Serializable {
+    ctor public QName(String, String);
+    ctor public QName(String, String, String);
+    ctor public QName(String);
+    method public final boolean equals(Object);
+    method public String getLocalPart();
+    method public String getNamespaceURI();
+    method public String getPrefix();
+    method public final int hashCode();
+    method public static javax.xml.namespace.QName valueOf(String);
+  }
+
+}
+
+package javax.xml.parsers {
+
+  public abstract class DocumentBuilder {
+    ctor protected DocumentBuilder();
+    method public abstract org.w3c.dom.DOMImplementation getDOMImplementation();
+    method public javax.xml.validation.Schema getSchema();
+    method public abstract boolean isNamespaceAware();
+    method public abstract boolean isValidating();
+    method public boolean isXIncludeAware();
+    method public abstract org.w3c.dom.Document newDocument();
+    method public org.w3c.dom.Document parse(java.io.InputStream) throws java.io.IOException, org.xml.sax.SAXException;
+    method public org.w3c.dom.Document parse(java.io.InputStream, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public org.w3c.dom.Document parse(String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public org.w3c.dom.Document parse(java.io.File) throws java.io.IOException, org.xml.sax.SAXException;
+    method public abstract org.w3c.dom.Document parse(org.xml.sax.InputSource) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void reset();
+    method public abstract void setEntityResolver(org.xml.sax.EntityResolver);
+    method public abstract void setErrorHandler(org.xml.sax.ErrorHandler);
+  }
+
+  public abstract class DocumentBuilderFactory {
+    ctor protected DocumentBuilderFactory();
+    method public abstract Object getAttribute(String) throws java.lang.IllegalArgumentException;
+    method public abstract boolean getFeature(String) throws javax.xml.parsers.ParserConfigurationException;
+    method public javax.xml.validation.Schema getSchema();
+    method public boolean isCoalescing();
+    method public boolean isExpandEntityReferences();
+    method public boolean isIgnoringComments();
+    method public boolean isIgnoringElementContentWhitespace();
+    method public boolean isNamespaceAware();
+    method public boolean isValidating();
+    method public boolean isXIncludeAware();
+    method public abstract javax.xml.parsers.DocumentBuilder newDocumentBuilder() throws javax.xml.parsers.ParserConfigurationException;
+    method public static javax.xml.parsers.DocumentBuilderFactory newInstance();
+    method public static javax.xml.parsers.DocumentBuilderFactory newInstance(String, ClassLoader);
+    method public abstract void setAttribute(String, Object) throws java.lang.IllegalArgumentException;
+    method public void setCoalescing(boolean);
+    method public void setExpandEntityReferences(boolean);
+    method public abstract void setFeature(String, boolean) throws javax.xml.parsers.ParserConfigurationException;
+    method public void setIgnoringComments(boolean);
+    method public void setIgnoringElementContentWhitespace(boolean);
+    method public void setNamespaceAware(boolean);
+    method public void setSchema(javax.xml.validation.Schema);
+    method public void setValidating(boolean);
+    method public void setXIncludeAware(boolean);
+  }
+
+  public class FactoryConfigurationError extends java.lang.Error {
+    ctor public FactoryConfigurationError();
+    ctor public FactoryConfigurationError(String);
+    ctor public FactoryConfigurationError(Exception);
+    ctor public FactoryConfigurationError(Exception, String);
+    method public Exception getException();
+  }
+
+  public class ParserConfigurationException extends java.lang.Exception {
+    ctor public ParserConfigurationException();
+    ctor public ParserConfigurationException(String);
+  }
+
+  public abstract class SAXParser {
+    ctor protected SAXParser();
+    method public abstract org.xml.sax.Parser getParser() throws org.xml.sax.SAXException;
+    method public abstract Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public javax.xml.validation.Schema getSchema();
+    method public abstract org.xml.sax.XMLReader getXMLReader() throws org.xml.sax.SAXException;
+    method public abstract boolean isNamespaceAware();
+    method public abstract boolean isValidating();
+    method public boolean isXIncludeAware();
+    method public void parse(java.io.InputStream, org.xml.sax.HandlerBase) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(java.io.InputStream, org.xml.sax.HandlerBase, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(java.io.InputStream, org.xml.sax.helpers.DefaultHandler) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(java.io.InputStream, org.xml.sax.helpers.DefaultHandler, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(String, org.xml.sax.HandlerBase) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(String, org.xml.sax.helpers.DefaultHandler) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(java.io.File, org.xml.sax.HandlerBase) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(java.io.File, org.xml.sax.helpers.DefaultHandler) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(org.xml.sax.InputSource, org.xml.sax.HandlerBase) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(org.xml.sax.InputSource, org.xml.sax.helpers.DefaultHandler) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void reset();
+    method public abstract void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+  }
+
+  public abstract class SAXParserFactory {
+    ctor protected SAXParserFactory();
+    method public abstract boolean getFeature(String) throws javax.xml.parsers.ParserConfigurationException, org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public javax.xml.validation.Schema getSchema();
+    method public boolean isNamespaceAware();
+    method public boolean isValidating();
+    method public boolean isXIncludeAware();
+    method public static javax.xml.parsers.SAXParserFactory newInstance();
+    method public static javax.xml.parsers.SAXParserFactory newInstance(String, ClassLoader);
+    method public abstract javax.xml.parsers.SAXParser newSAXParser() throws javax.xml.parsers.ParserConfigurationException, org.xml.sax.SAXException;
+    method public abstract void setFeature(String, boolean) throws javax.xml.parsers.ParserConfigurationException, org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setNamespaceAware(boolean);
+    method public void setSchema(javax.xml.validation.Schema);
+    method public void setValidating(boolean);
+    method public void setXIncludeAware(boolean);
+  }
+
+}
+
+package javax.xml.transform {
+
+  public interface ErrorListener {
+    method public void error(javax.xml.transform.TransformerException) throws javax.xml.transform.TransformerException;
+    method public void fatalError(javax.xml.transform.TransformerException) throws javax.xml.transform.TransformerException;
+    method public void warning(javax.xml.transform.TransformerException) throws javax.xml.transform.TransformerException;
+  }
+
+  public class OutputKeys {
+    field public static final String CDATA_SECTION_ELEMENTS = "cdata-section-elements";
+    field public static final String DOCTYPE_PUBLIC = "doctype-public";
+    field public static final String DOCTYPE_SYSTEM = "doctype-system";
+    field public static final String ENCODING = "encoding";
+    field public static final String INDENT = "indent";
+    field public static final String MEDIA_TYPE = "media-type";
+    field public static final String METHOD = "method";
+    field public static final String OMIT_XML_DECLARATION = "omit-xml-declaration";
+    field public static final String STANDALONE = "standalone";
+    field public static final String VERSION = "version";
+  }
+
+  public interface Result {
+    method public String getSystemId();
+    method public void setSystemId(String);
+    field public static final String PI_DISABLE_OUTPUT_ESCAPING = "javax.xml.transform.disable-output-escaping";
+    field public static final String PI_ENABLE_OUTPUT_ESCAPING = "javax.xml.transform.enable-output-escaping";
+  }
+
+  public interface Source {
+    method public String getSystemId();
+    method public void setSystemId(String);
+  }
+
+  public interface SourceLocator {
+    method public int getColumnNumber();
+    method public int getLineNumber();
+    method public String getPublicId();
+    method public String getSystemId();
+  }
+
+  public interface Templates {
+    method public java.util.Properties getOutputProperties();
+    method public javax.xml.transform.Transformer newTransformer() throws javax.xml.transform.TransformerConfigurationException;
+  }
+
+  public abstract class Transformer {
+    ctor protected Transformer();
+    method public abstract void clearParameters();
+    method public abstract javax.xml.transform.ErrorListener getErrorListener();
+    method public abstract java.util.Properties getOutputProperties();
+    method public abstract String getOutputProperty(String) throws java.lang.IllegalArgumentException;
+    method public abstract Object getParameter(String);
+    method public abstract javax.xml.transform.URIResolver getURIResolver();
+    method public void reset();
+    method public abstract void setErrorListener(javax.xml.transform.ErrorListener) throws java.lang.IllegalArgumentException;
+    method public abstract void setOutputProperties(java.util.Properties);
+    method public abstract void setOutputProperty(String, String) throws java.lang.IllegalArgumentException;
+    method public abstract void setParameter(String, Object);
+    method public abstract void setURIResolver(javax.xml.transform.URIResolver);
+    method public abstract void transform(javax.xml.transform.Source, javax.xml.transform.Result) throws javax.xml.transform.TransformerException;
+  }
+
+  public class TransformerConfigurationException extends javax.xml.transform.TransformerException {
+    ctor public TransformerConfigurationException();
+    ctor public TransformerConfigurationException(String);
+    ctor public TransformerConfigurationException(Throwable);
+    ctor public TransformerConfigurationException(String, Throwable);
+    ctor public TransformerConfigurationException(String, javax.xml.transform.SourceLocator);
+    ctor public TransformerConfigurationException(String, javax.xml.transform.SourceLocator, Throwable);
+  }
+
+  public class TransformerException extends java.lang.Exception {
+    ctor public TransformerException(String);
+    ctor public TransformerException(Throwable);
+    ctor public TransformerException(String, Throwable);
+    ctor public TransformerException(String, javax.xml.transform.SourceLocator);
+    ctor public TransformerException(String, javax.xml.transform.SourceLocator, Throwable);
+    method public Throwable getException();
+    method public String getLocationAsString();
+    method public javax.xml.transform.SourceLocator getLocator();
+    method public String getMessageAndLocation();
+    method public void setLocator(javax.xml.transform.SourceLocator);
+  }
+
+  public abstract class TransformerFactory {
+    ctor protected TransformerFactory();
+    method public abstract javax.xml.transform.Source getAssociatedStylesheet(javax.xml.transform.Source, String, String, String) throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract Object getAttribute(String);
+    method public abstract javax.xml.transform.ErrorListener getErrorListener();
+    method public abstract boolean getFeature(String);
+    method public abstract javax.xml.transform.URIResolver getURIResolver();
+    method public static javax.xml.transform.TransformerFactory newInstance() throws javax.xml.transform.TransformerFactoryConfigurationError;
+    method public static javax.xml.transform.TransformerFactory newInstance(String, ClassLoader) throws javax.xml.transform.TransformerFactoryConfigurationError;
+    method public abstract javax.xml.transform.Templates newTemplates(javax.xml.transform.Source) throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract javax.xml.transform.Transformer newTransformer(javax.xml.transform.Source) throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract javax.xml.transform.Transformer newTransformer() throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract void setAttribute(String, Object);
+    method public abstract void setErrorListener(javax.xml.transform.ErrorListener);
+    method public abstract void setFeature(String, boolean) throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract void setURIResolver(javax.xml.transform.URIResolver);
+  }
+
+  public class TransformerFactoryConfigurationError extends java.lang.Error {
+    ctor public TransformerFactoryConfigurationError();
+    ctor public TransformerFactoryConfigurationError(String);
+    ctor public TransformerFactoryConfigurationError(Exception);
+    ctor public TransformerFactoryConfigurationError(Exception, String);
+    method public Exception getException();
+  }
+
+  public interface URIResolver {
+    method public javax.xml.transform.Source resolve(String, String) throws javax.xml.transform.TransformerException;
+  }
+
+}
+
+package javax.xml.transform.dom {
+
+  public interface DOMLocator extends javax.xml.transform.SourceLocator {
+    method public org.w3c.dom.Node getOriginatingNode();
+  }
+
+  public class DOMResult implements javax.xml.transform.Result {
+    ctor public DOMResult();
+    ctor public DOMResult(org.w3c.dom.Node);
+    ctor public DOMResult(org.w3c.dom.Node, String);
+    ctor public DOMResult(org.w3c.dom.Node, org.w3c.dom.Node);
+    ctor public DOMResult(org.w3c.dom.Node, org.w3c.dom.Node, String);
+    method public org.w3c.dom.Node getNextSibling();
+    method public org.w3c.dom.Node getNode();
+    method public String getSystemId();
+    method public void setNextSibling(org.w3c.dom.Node);
+    method public void setNode(org.w3c.dom.Node);
+    method public void setSystemId(String);
+    field public static final String FEATURE = "http://javax.xml.transform.dom.DOMResult/feature";
+  }
+
+  public class DOMSource implements javax.xml.transform.Source {
+    ctor public DOMSource();
+    ctor public DOMSource(org.w3c.dom.Node);
+    ctor public DOMSource(org.w3c.dom.Node, String);
+    method public org.w3c.dom.Node getNode();
+    method public String getSystemId();
+    method public void setNode(org.w3c.dom.Node);
+    method public void setSystemId(String);
+    field public static final String FEATURE = "http://javax.xml.transform.dom.DOMSource/feature";
+  }
+
+}
+
+package javax.xml.transform.sax {
+
+  public class SAXResult implements javax.xml.transform.Result {
+    ctor public SAXResult();
+    ctor public SAXResult(org.xml.sax.ContentHandler);
+    method public org.xml.sax.ContentHandler getHandler();
+    method public org.xml.sax.ext.LexicalHandler getLexicalHandler();
+    method public String getSystemId();
+    method public void setHandler(org.xml.sax.ContentHandler);
+    method public void setLexicalHandler(org.xml.sax.ext.LexicalHandler);
+    method public void setSystemId(String);
+    field public static final String FEATURE = "http://javax.xml.transform.sax.SAXResult/feature";
+  }
+
+  public class SAXSource implements javax.xml.transform.Source {
+    ctor public SAXSource();
+    ctor public SAXSource(org.xml.sax.XMLReader, org.xml.sax.InputSource);
+    ctor public SAXSource(org.xml.sax.InputSource);
+    method public org.xml.sax.InputSource getInputSource();
+    method public String getSystemId();
+    method public org.xml.sax.XMLReader getXMLReader();
+    method public void setInputSource(org.xml.sax.InputSource);
+    method public void setSystemId(String);
+    method public void setXMLReader(org.xml.sax.XMLReader);
+    method public static org.xml.sax.InputSource sourceToInputSource(javax.xml.transform.Source);
+    field public static final String FEATURE = "http://javax.xml.transform.sax.SAXSource/feature";
+  }
+
+  public abstract class SAXTransformerFactory extends javax.xml.transform.TransformerFactory {
+    ctor protected SAXTransformerFactory();
+    method public abstract javax.xml.transform.sax.TemplatesHandler newTemplatesHandler() throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract javax.xml.transform.sax.TransformerHandler newTransformerHandler(javax.xml.transform.Source) throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract javax.xml.transform.sax.TransformerHandler newTransformerHandler(javax.xml.transform.Templates) throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract javax.xml.transform.sax.TransformerHandler newTransformerHandler() throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract org.xml.sax.XMLFilter newXMLFilter(javax.xml.transform.Source) throws javax.xml.transform.TransformerConfigurationException;
+    method public abstract org.xml.sax.XMLFilter newXMLFilter(javax.xml.transform.Templates) throws javax.xml.transform.TransformerConfigurationException;
+    field public static final String FEATURE = "http://javax.xml.transform.sax.SAXTransformerFactory/feature";
+    field public static final String FEATURE_XMLFILTER = "http://javax.xml.transform.sax.SAXTransformerFactory/feature/xmlfilter";
+  }
+
+  public interface TemplatesHandler extends org.xml.sax.ContentHandler {
+    method public String getSystemId();
+    method public javax.xml.transform.Templates getTemplates();
+    method public void setSystemId(String);
+  }
+
+  public interface TransformerHandler extends org.xml.sax.ContentHandler org.xml.sax.DTDHandler org.xml.sax.ext.LexicalHandler {
+    method public String getSystemId();
+    method public javax.xml.transform.Transformer getTransformer();
+    method public void setResult(javax.xml.transform.Result) throws java.lang.IllegalArgumentException;
+    method public void setSystemId(String);
+  }
+
+}
+
+package javax.xml.transform.stream {
+
+  public class StreamResult implements javax.xml.transform.Result {
+    ctor public StreamResult();
+    ctor public StreamResult(java.io.OutputStream);
+    ctor public StreamResult(java.io.Writer);
+    ctor public StreamResult(String);
+    ctor public StreamResult(java.io.File);
+    method public java.io.OutputStream getOutputStream();
+    method public String getSystemId();
+    method public java.io.Writer getWriter();
+    method public void setOutputStream(java.io.OutputStream);
+    method public void setSystemId(String);
+    method public void setSystemId(java.io.File);
+    method public void setWriter(java.io.Writer);
+    field public static final String FEATURE = "http://javax.xml.transform.stream.StreamResult/feature";
+  }
+
+  public class StreamSource implements javax.xml.transform.Source {
+    ctor public StreamSource();
+    ctor public StreamSource(java.io.InputStream);
+    ctor public StreamSource(java.io.InputStream, String);
+    ctor public StreamSource(java.io.Reader);
+    ctor public StreamSource(java.io.Reader, String);
+    ctor public StreamSource(String);
+    ctor public StreamSource(java.io.File);
+    method public java.io.InputStream getInputStream();
+    method public String getPublicId();
+    method public java.io.Reader getReader();
+    method public String getSystemId();
+    method public void setInputStream(java.io.InputStream);
+    method public void setPublicId(String);
+    method public void setReader(java.io.Reader);
+    method public void setSystemId(String);
+    method public void setSystemId(java.io.File);
+    field public static final String FEATURE = "http://javax.xml.transform.stream.StreamSource/feature";
+  }
+
+}
+
+package javax.xml.validation {
+
+  public abstract class Schema {
+    ctor protected Schema();
+    method public abstract javax.xml.validation.Validator newValidator();
+    method public abstract javax.xml.validation.ValidatorHandler newValidatorHandler();
+  }
+
+  public abstract class SchemaFactory {
+    ctor protected SchemaFactory();
+    method public abstract org.xml.sax.ErrorHandler getErrorHandler();
+    method public boolean getFeature(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public abstract org.w3c.dom.ls.LSResourceResolver getResourceResolver();
+    method public abstract boolean isSchemaLanguageSupported(String);
+    method public static javax.xml.validation.SchemaFactory newInstance(String);
+    method public static javax.xml.validation.SchemaFactory newInstance(String, String, ClassLoader);
+    method public javax.xml.validation.Schema newSchema(javax.xml.transform.Source) throws org.xml.sax.SAXException;
+    method public javax.xml.validation.Schema newSchema(java.io.File) throws org.xml.sax.SAXException;
+    method public javax.xml.validation.Schema newSchema(java.net.URL) throws org.xml.sax.SAXException;
+    method public abstract javax.xml.validation.Schema newSchema(javax.xml.transform.Source[]) throws org.xml.sax.SAXException;
+    method public abstract javax.xml.validation.Schema newSchema() throws org.xml.sax.SAXException;
+    method public abstract void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setFeature(String, boolean) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public abstract void setResourceResolver(org.w3c.dom.ls.LSResourceResolver);
+  }
+
+  public abstract class SchemaFactoryLoader {
+    ctor protected SchemaFactoryLoader();
+    method public abstract javax.xml.validation.SchemaFactory newFactory(String);
+  }
+
+  public abstract class TypeInfoProvider {
+    ctor protected TypeInfoProvider();
+    method public abstract org.w3c.dom.TypeInfo getAttributeTypeInfo(int);
+    method public abstract org.w3c.dom.TypeInfo getElementTypeInfo();
+    method public abstract boolean isIdAttribute(int);
+    method public abstract boolean isSpecified(int);
+  }
+
+  public abstract class Validator {
+    ctor protected Validator();
+    method public abstract org.xml.sax.ErrorHandler getErrorHandler();
+    method public boolean getFeature(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public abstract org.w3c.dom.ls.LSResourceResolver getResourceResolver();
+    method public abstract void reset();
+    method public abstract void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setFeature(String, boolean) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public abstract void setResourceResolver(org.w3c.dom.ls.LSResourceResolver);
+    method public void validate(javax.xml.transform.Source) throws java.io.IOException, org.xml.sax.SAXException;
+    method public abstract void validate(javax.xml.transform.Source, javax.xml.transform.Result) throws java.io.IOException, org.xml.sax.SAXException;
+  }
+
+  public abstract class ValidatorHandler implements org.xml.sax.ContentHandler {
+    ctor protected ValidatorHandler();
+    method public abstract org.xml.sax.ContentHandler getContentHandler();
+    method public abstract org.xml.sax.ErrorHandler getErrorHandler();
+    method public boolean getFeature(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public abstract org.w3c.dom.ls.LSResourceResolver getResourceResolver();
+    method public abstract javax.xml.validation.TypeInfoProvider getTypeInfoProvider();
+    method public abstract void setContentHandler(org.xml.sax.ContentHandler);
+    method public abstract void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setFeature(String, boolean) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public abstract void setResourceResolver(org.w3c.dom.ls.LSResourceResolver);
+  }
+
+}
+
+package javax.xml.xpath {
+
+  public interface XPath {
+    method public javax.xml.xpath.XPathExpression compile(String) throws javax.xml.xpath.XPathExpressionException;
+    method public Object evaluate(String, Object, javax.xml.namespace.QName) throws javax.xml.xpath.XPathExpressionException;
+    method public String evaluate(String, Object) throws javax.xml.xpath.XPathExpressionException;
+    method public Object evaluate(String, org.xml.sax.InputSource, javax.xml.namespace.QName) throws javax.xml.xpath.XPathExpressionException;
+    method public String evaluate(String, org.xml.sax.InputSource) throws javax.xml.xpath.XPathExpressionException;
+    method public javax.xml.namespace.NamespaceContext getNamespaceContext();
+    method public javax.xml.xpath.XPathFunctionResolver getXPathFunctionResolver();
+    method public javax.xml.xpath.XPathVariableResolver getXPathVariableResolver();
+    method public void reset();
+    method public void setNamespaceContext(javax.xml.namespace.NamespaceContext);
+    method public void setXPathFunctionResolver(javax.xml.xpath.XPathFunctionResolver);
+    method public void setXPathVariableResolver(javax.xml.xpath.XPathVariableResolver);
+  }
+
+  public class XPathConstants {
+    field public static final javax.xml.namespace.QName BOOLEAN;
+    field public static final String DOM_OBJECT_MODEL = "http://java.sun.com/jaxp/xpath/dom";
+    field public static final javax.xml.namespace.QName NODE;
+    field public static final javax.xml.namespace.QName NODESET;
+    field public static final javax.xml.namespace.QName NUMBER;
+    field public static final javax.xml.namespace.QName STRING;
+  }
+
+  public class XPathException extends java.lang.Exception {
+    ctor public XPathException(String);
+    ctor public XPathException(Throwable);
+  }
+
+  public interface XPathExpression {
+    method public Object evaluate(Object, javax.xml.namespace.QName) throws javax.xml.xpath.XPathExpressionException;
+    method public String evaluate(Object) throws javax.xml.xpath.XPathExpressionException;
+    method public Object evaluate(org.xml.sax.InputSource, javax.xml.namespace.QName) throws javax.xml.xpath.XPathExpressionException;
+    method public String evaluate(org.xml.sax.InputSource) throws javax.xml.xpath.XPathExpressionException;
+  }
+
+  public class XPathExpressionException extends javax.xml.xpath.XPathException {
+    ctor public XPathExpressionException(String);
+    ctor public XPathExpressionException(Throwable);
+  }
+
+  public abstract class XPathFactory {
+    ctor protected XPathFactory();
+    method public abstract boolean getFeature(String) throws javax.xml.xpath.XPathFactoryConfigurationException;
+    method public abstract boolean isObjectModelSupported(String);
+    method public static final javax.xml.xpath.XPathFactory newInstance();
+    method public static final javax.xml.xpath.XPathFactory newInstance(String) throws javax.xml.xpath.XPathFactoryConfigurationException;
+    method public static javax.xml.xpath.XPathFactory newInstance(String, String, ClassLoader) throws javax.xml.xpath.XPathFactoryConfigurationException;
+    method public abstract javax.xml.xpath.XPath newXPath();
+    method public abstract void setFeature(String, boolean) throws javax.xml.xpath.XPathFactoryConfigurationException;
+    method public abstract void setXPathFunctionResolver(javax.xml.xpath.XPathFunctionResolver);
+    method public abstract void setXPathVariableResolver(javax.xml.xpath.XPathVariableResolver);
+    field public static final String DEFAULT_OBJECT_MODEL_URI = "http://java.sun.com/jaxp/xpath/dom";
+    field public static final String DEFAULT_PROPERTY_NAME = "javax.xml.xpath.XPathFactory";
+  }
+
+  public class XPathFactoryConfigurationException extends javax.xml.xpath.XPathException {
+    ctor public XPathFactoryConfigurationException(String);
+    ctor public XPathFactoryConfigurationException(Throwable);
+  }
+
+  public interface XPathFunction {
+    method public Object evaluate(java.util.List) throws javax.xml.xpath.XPathFunctionException;
+  }
+
+  public class XPathFunctionException extends javax.xml.xpath.XPathExpressionException {
+    ctor public XPathFunctionException(String);
+    ctor public XPathFunctionException(Throwable);
+  }
+
+  public interface XPathFunctionResolver {
+    method public javax.xml.xpath.XPathFunction resolveFunction(javax.xml.namespace.QName, int);
+  }
+
+  public interface XPathVariableResolver {
+    method public Object resolveVariable(javax.xml.namespace.QName);
+  }
+
+}
+
+package org.json {
+
+  public class JSONArray {
+    ctor public JSONArray();
+    ctor public JSONArray(java.util.Collection);
+    ctor public JSONArray(org.json.JSONTokener) throws org.json.JSONException;
+    ctor public JSONArray(String) throws org.json.JSONException;
+    ctor public JSONArray(Object) throws org.json.JSONException;
+    method public Object get(int) throws org.json.JSONException;
+    method public boolean getBoolean(int) throws org.json.JSONException;
+    method public double getDouble(int) throws org.json.JSONException;
+    method public int getInt(int) throws org.json.JSONException;
+    method public org.json.JSONArray getJSONArray(int) throws org.json.JSONException;
+    method public org.json.JSONObject getJSONObject(int) throws org.json.JSONException;
+    method public long getLong(int) throws org.json.JSONException;
+    method public String getString(int) throws org.json.JSONException;
+    method public boolean isNull(int);
+    method public String join(String) throws org.json.JSONException;
+    method public int length();
+    method public Object opt(int);
+    method public boolean optBoolean(int);
+    method public boolean optBoolean(int, boolean);
+    method public double optDouble(int);
+    method public double optDouble(int, double);
+    method public int optInt(int);
+    method public int optInt(int, int);
+    method public org.json.JSONArray optJSONArray(int);
+    method public org.json.JSONObject optJSONObject(int);
+    method public long optLong(int);
+    method public long optLong(int, long);
+    method public String optString(int);
+    method public String optString(int, String);
+    method public org.json.JSONArray put(boolean);
+    method public org.json.JSONArray put(double) throws org.json.JSONException;
+    method public org.json.JSONArray put(int);
+    method public org.json.JSONArray put(long);
+    method public org.json.JSONArray put(Object);
+    method public org.json.JSONArray put(int, boolean) throws org.json.JSONException;
+    method public org.json.JSONArray put(int, double) throws org.json.JSONException;
+    method public org.json.JSONArray put(int, int) throws org.json.JSONException;
+    method public org.json.JSONArray put(int, long) throws org.json.JSONException;
+    method public org.json.JSONArray put(int, Object) throws org.json.JSONException;
+    method public Object remove(int);
+    method public org.json.JSONObject toJSONObject(org.json.JSONArray) throws org.json.JSONException;
+    method public String toString(int) throws org.json.JSONException;
+  }
+
+  public class JSONException extends java.lang.Exception {
+    ctor public JSONException(String);
+    ctor public JSONException(String, Throwable);
+    ctor public JSONException(Throwable);
+  }
+
+  public class JSONObject {
+    ctor public JSONObject();
+    ctor public JSONObject(@NonNull java.util.Map);
+    ctor public JSONObject(@NonNull org.json.JSONTokener) throws org.json.JSONException;
+    ctor public JSONObject(@NonNull String) throws org.json.JSONException;
+    ctor public JSONObject(@NonNull org.json.JSONObject, @NonNull String[]) throws org.json.JSONException;
+    method @NonNull public org.json.JSONObject accumulate(@NonNull String, @Nullable Object) throws org.json.JSONException;
+    method @NonNull public Object get(@NonNull String) throws org.json.JSONException;
+    method public boolean getBoolean(@NonNull String) throws org.json.JSONException;
+    method public double getDouble(@NonNull String) throws org.json.JSONException;
+    method public int getInt(@NonNull String) throws org.json.JSONException;
+    method @NonNull public org.json.JSONArray getJSONArray(@NonNull String) throws org.json.JSONException;
+    method @NonNull public org.json.JSONObject getJSONObject(@NonNull String) throws org.json.JSONException;
+    method public long getLong(@NonNull String) throws org.json.JSONException;
+    method @NonNull public String getString(@NonNull String) throws org.json.JSONException;
+    method public boolean has(@Nullable String);
+    method public boolean isNull(@Nullable String);
+    method @NonNull public java.util.Iterator<java.lang.String> keys();
+    method public int length();
+    method @Nullable public org.json.JSONArray names();
+    method @NonNull public static String numberToString(@NonNull Number) throws org.json.JSONException;
+    method @Nullable public Object opt(@Nullable String);
+    method public boolean optBoolean(@Nullable String);
+    method public boolean optBoolean(@Nullable String, boolean);
+    method public double optDouble(@Nullable String);
+    method public double optDouble(@Nullable String, double);
+    method public int optInt(@Nullable String);
+    method public int optInt(@Nullable String, int);
+    method @Nullable public org.json.JSONArray optJSONArray(@Nullable String);
+    method @Nullable public org.json.JSONObject optJSONObject(@Nullable String);
+    method public long optLong(@Nullable String);
+    method public long optLong(@Nullable String, long);
+    method @NonNull public String optString(@Nullable String);
+    method @NonNull public String optString(@Nullable String, @NonNull String);
+    method @NonNull public org.json.JSONObject put(@NonNull String, boolean) throws org.json.JSONException;
+    method @NonNull public org.json.JSONObject put(@NonNull String, double) throws org.json.JSONException;
+    method @NonNull public org.json.JSONObject put(@NonNull String, int) throws org.json.JSONException;
+    method @NonNull public org.json.JSONObject put(@NonNull String, long) throws org.json.JSONException;
+    method @NonNull public org.json.JSONObject put(@NonNull String, @Nullable Object) throws org.json.JSONException;
+    method @NonNull public org.json.JSONObject putOpt(@Nullable String, @Nullable Object) throws org.json.JSONException;
+    method @NonNull public static String quote(@Nullable String);
+    method @Nullable public Object remove(@Nullable String);
+    method @Nullable public org.json.JSONArray toJSONArray(@Nullable org.json.JSONArray) throws org.json.JSONException;
+    method @NonNull public String toString(int) throws org.json.JSONException;
+    method @Nullable public static Object wrap(@Nullable Object);
+    field @NonNull public static final Object NULL;
+  }
+
+  public class JSONStringer {
+    ctor public JSONStringer();
+    method public org.json.JSONStringer array() throws org.json.JSONException;
+    method public org.json.JSONStringer endArray() throws org.json.JSONException;
+    method public org.json.JSONStringer endObject() throws org.json.JSONException;
+    method public org.json.JSONStringer key(String) throws org.json.JSONException;
+    method public org.json.JSONStringer object() throws org.json.JSONException;
+    method public org.json.JSONStringer value(Object) throws org.json.JSONException;
+    method public org.json.JSONStringer value(boolean) throws org.json.JSONException;
+    method public org.json.JSONStringer value(double) throws org.json.JSONException;
+    method public org.json.JSONStringer value(long) throws org.json.JSONException;
+  }
+
+  public class JSONTokener {
+    ctor public JSONTokener(String);
+    method public void back();
+    method public static int dehexchar(char);
+    method public boolean more();
+    method public char next();
+    method public char next(char) throws org.json.JSONException;
+    method public String next(int) throws org.json.JSONException;
+    method public char nextClean() throws org.json.JSONException;
+    method public String nextString(char) throws org.json.JSONException;
+    method public String nextTo(String);
+    method public String nextTo(char);
+    method public Object nextValue() throws org.json.JSONException;
+    method public void skipPast(String);
+    method public char skipTo(char);
+    method public org.json.JSONException syntaxError(String);
+  }
+
+}
+
+package org.w3c.dom {
+
+  public interface Attr extends org.w3c.dom.Node {
+    method public String getName();
+    method public org.w3c.dom.Element getOwnerElement();
+    method public org.w3c.dom.TypeInfo getSchemaTypeInfo();
+    method public boolean getSpecified();
+    method public String getValue();
+    method public boolean isId();
+    method public void setValue(String) throws org.w3c.dom.DOMException;
+  }
+
+  public interface CDATASection extends org.w3c.dom.Text {
+  }
+
+  public interface CharacterData extends org.w3c.dom.Node {
+    method public void appendData(String) throws org.w3c.dom.DOMException;
+    method public void deleteData(int, int) throws org.w3c.dom.DOMException;
+    method public String getData() throws org.w3c.dom.DOMException;
+    method public int getLength();
+    method public void insertData(int, String) throws org.w3c.dom.DOMException;
+    method public void replaceData(int, int, String) throws org.w3c.dom.DOMException;
+    method public void setData(String) throws org.w3c.dom.DOMException;
+    method public String substringData(int, int) throws org.w3c.dom.DOMException;
+  }
+
+  public interface Comment extends org.w3c.dom.CharacterData {
+  }
+
+  public interface DOMConfiguration {
+    method public boolean canSetParameter(String, Object);
+    method public Object getParameter(String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.DOMStringList getParameterNames();
+    method public void setParameter(String, Object) throws org.w3c.dom.DOMException;
+  }
+
+  public interface DOMError {
+    method public org.w3c.dom.DOMLocator getLocation();
+    method public String getMessage();
+    method public Object getRelatedData();
+    method public Object getRelatedException();
+    method public short getSeverity();
+    method public String getType();
+    field public static final short SEVERITY_ERROR = 2; // 0x2
+    field public static final short SEVERITY_FATAL_ERROR = 3; // 0x3
+    field public static final short SEVERITY_WARNING = 1; // 0x1
+  }
+
+  public interface DOMErrorHandler {
+    method public boolean handleError(org.w3c.dom.DOMError);
+  }
+
+  public class DOMException extends java.lang.RuntimeException {
+    ctor public DOMException(short, String);
+    field public static final short DOMSTRING_SIZE_ERR = 2; // 0x2
+    field public static final short HIERARCHY_REQUEST_ERR = 3; // 0x3
+    field public static final short INDEX_SIZE_ERR = 1; // 0x1
+    field public static final short INUSE_ATTRIBUTE_ERR = 10; // 0xa
+    field public static final short INVALID_ACCESS_ERR = 15; // 0xf
+    field public static final short INVALID_CHARACTER_ERR = 5; // 0x5
+    field public static final short INVALID_MODIFICATION_ERR = 13; // 0xd
+    field public static final short INVALID_STATE_ERR = 11; // 0xb
+    field public static final short NAMESPACE_ERR = 14; // 0xe
+    field public static final short NOT_FOUND_ERR = 8; // 0x8
+    field public static final short NOT_SUPPORTED_ERR = 9; // 0x9
+    field public static final short NO_DATA_ALLOWED_ERR = 6; // 0x6
+    field public static final short NO_MODIFICATION_ALLOWED_ERR = 7; // 0x7
+    field public static final short SYNTAX_ERR = 12; // 0xc
+    field public static final short TYPE_MISMATCH_ERR = 17; // 0x11
+    field public static final short VALIDATION_ERR = 16; // 0x10
+    field public static final short WRONG_DOCUMENT_ERR = 4; // 0x4
+    field public short code;
+  }
+
+  public interface DOMImplementation {
+    method public org.w3c.dom.Document createDocument(String, String, org.w3c.dom.DocumentType) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.DocumentType createDocumentType(String, String, String) throws org.w3c.dom.DOMException;
+    method public Object getFeature(String, String);
+    method public boolean hasFeature(String, String);
+  }
+
+  public interface DOMImplementationList {
+    method public int getLength();
+    method public org.w3c.dom.DOMImplementation item(int);
+  }
+
+  public interface DOMImplementationSource {
+    method public org.w3c.dom.DOMImplementation getDOMImplementation(String);
+    method public org.w3c.dom.DOMImplementationList getDOMImplementationList(String);
+  }
+
+  public interface DOMLocator {
+    method public int getByteOffset();
+    method public int getColumnNumber();
+    method public int getLineNumber();
+    method public org.w3c.dom.Node getRelatedNode();
+    method public String getUri();
+    method public int getUtf16Offset();
+  }
+
+  public interface DOMStringList {
+    method public boolean contains(String);
+    method public int getLength();
+    method public String item(int);
+  }
+
+  public interface Document extends org.w3c.dom.Node {
+    method public org.w3c.dom.Node adoptNode(org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Attr createAttribute(String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Attr createAttributeNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.CDATASection createCDATASection(String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Comment createComment(String);
+    method public org.w3c.dom.DocumentFragment createDocumentFragment();
+    method public org.w3c.dom.Element createElement(String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Element createElementNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.EntityReference createEntityReference(String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.ProcessingInstruction createProcessingInstruction(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Text createTextNode(String);
+    method public org.w3c.dom.DocumentType getDoctype();
+    method public org.w3c.dom.Element getDocumentElement();
+    method public String getDocumentURI();
+    method public org.w3c.dom.DOMConfiguration getDomConfig();
+    method public org.w3c.dom.Element getElementById(String);
+    method public org.w3c.dom.NodeList getElementsByTagName(String);
+    method public org.w3c.dom.NodeList getElementsByTagNameNS(String, String);
+    method public org.w3c.dom.DOMImplementation getImplementation();
+    method public String getInputEncoding();
+    method public boolean getStrictErrorChecking();
+    method public String getXmlEncoding();
+    method public boolean getXmlStandalone();
+    method public String getXmlVersion();
+    method public org.w3c.dom.Node importNode(org.w3c.dom.Node, boolean) throws org.w3c.dom.DOMException;
+    method public void normalizeDocument();
+    method public org.w3c.dom.Node renameNode(org.w3c.dom.Node, String, String) throws org.w3c.dom.DOMException;
+    method public void setDocumentURI(String);
+    method public void setStrictErrorChecking(boolean);
+    method public void setXmlStandalone(boolean) throws org.w3c.dom.DOMException;
+    method public void setXmlVersion(String) throws org.w3c.dom.DOMException;
+  }
+
+  public interface DocumentFragment extends org.w3c.dom.Node {
+  }
+
+  public interface DocumentType extends org.w3c.dom.Node {
+    method public org.w3c.dom.NamedNodeMap getEntities();
+    method public String getInternalSubset();
+    method public String getName();
+    method public org.w3c.dom.NamedNodeMap getNotations();
+    method public String getPublicId();
+    method public String getSystemId();
+  }
+
+  public interface Element extends org.w3c.dom.Node {
+    method public String getAttribute(String);
+    method public String getAttributeNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Attr getAttributeNode(String);
+    method public org.w3c.dom.Attr getAttributeNodeNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.NodeList getElementsByTagName(String);
+    method public org.w3c.dom.NodeList getElementsByTagNameNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.TypeInfo getSchemaTypeInfo();
+    method public String getTagName();
+    method public boolean hasAttribute(String);
+    method public boolean hasAttributeNS(String, String) throws org.w3c.dom.DOMException;
+    method public void removeAttribute(String) throws org.w3c.dom.DOMException;
+    method public void removeAttributeNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Attr removeAttributeNode(org.w3c.dom.Attr) throws org.w3c.dom.DOMException;
+    method public void setAttribute(String, String) throws org.w3c.dom.DOMException;
+    method public void setAttributeNS(String, String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Attr setAttributeNode(org.w3c.dom.Attr) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Attr setAttributeNodeNS(org.w3c.dom.Attr) throws org.w3c.dom.DOMException;
+    method public void setIdAttribute(String, boolean) throws org.w3c.dom.DOMException;
+    method public void setIdAttributeNS(String, String, boolean) throws org.w3c.dom.DOMException;
+    method public void setIdAttributeNode(org.w3c.dom.Attr, boolean) throws org.w3c.dom.DOMException;
+  }
+
+  public interface Entity extends org.w3c.dom.Node {
+    method public String getInputEncoding();
+    method public String getNotationName();
+    method public String getPublicId();
+    method public String getSystemId();
+    method public String getXmlEncoding();
+    method public String getXmlVersion();
+  }
+
+  public interface EntityReference extends org.w3c.dom.Node {
+  }
+
+  public interface NameList {
+    method public boolean contains(String);
+    method public boolean containsNS(String, String);
+    method public int getLength();
+    method public String getName(int);
+    method public String getNamespaceURI(int);
+  }
+
+  public interface NamedNodeMap {
+    method public int getLength();
+    method public org.w3c.dom.Node getNamedItem(String);
+    method public org.w3c.dom.Node getNamedItemNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Node item(int);
+    method public org.w3c.dom.Node removeNamedItem(String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Node removeNamedItemNS(String, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Node setNamedItem(org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Node setNamedItemNS(org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+  }
+
+  public interface Node {
+    method public org.w3c.dom.Node appendChild(org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Node cloneNode(boolean);
+    method public short compareDocumentPosition(org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.NamedNodeMap getAttributes();
+    method public String getBaseURI();
+    method public org.w3c.dom.NodeList getChildNodes();
+    method public Object getFeature(String, String);
+    method public org.w3c.dom.Node getFirstChild();
+    method public org.w3c.dom.Node getLastChild();
+    method public String getLocalName();
+    method public String getNamespaceURI();
+    method public org.w3c.dom.Node getNextSibling();
+    method public String getNodeName();
+    method public short getNodeType();
+    method public String getNodeValue() throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Document getOwnerDocument();
+    method public org.w3c.dom.Node getParentNode();
+    method public String getPrefix();
+    method public org.w3c.dom.Node getPreviousSibling();
+    method public String getTextContent() throws org.w3c.dom.DOMException;
+    method public Object getUserData(String);
+    method public boolean hasAttributes();
+    method public boolean hasChildNodes();
+    method public org.w3c.dom.Node insertBefore(org.w3c.dom.Node, org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+    method public boolean isDefaultNamespace(String);
+    method public boolean isEqualNode(org.w3c.dom.Node);
+    method public boolean isSameNode(org.w3c.dom.Node);
+    method public boolean isSupported(String, String);
+    method public String lookupNamespaceURI(String);
+    method public String lookupPrefix(String);
+    method public void normalize();
+    method public org.w3c.dom.Node removeChild(org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Node replaceChild(org.w3c.dom.Node, org.w3c.dom.Node) throws org.w3c.dom.DOMException;
+    method public void setNodeValue(String) throws org.w3c.dom.DOMException;
+    method public void setPrefix(String) throws org.w3c.dom.DOMException;
+    method public void setTextContent(String) throws org.w3c.dom.DOMException;
+    method public Object setUserData(String, Object, org.w3c.dom.UserDataHandler);
+    field public static final short ATTRIBUTE_NODE = 2; // 0x2
+    field public static final short CDATA_SECTION_NODE = 4; // 0x4
+    field public static final short COMMENT_NODE = 8; // 0x8
+    field public static final short DOCUMENT_FRAGMENT_NODE = 11; // 0xb
+    field public static final short DOCUMENT_NODE = 9; // 0x9
+    field public static final short DOCUMENT_POSITION_CONTAINED_BY = 16; // 0x10
+    field public static final short DOCUMENT_POSITION_CONTAINS = 8; // 0x8
+    field public static final short DOCUMENT_POSITION_DISCONNECTED = 1; // 0x1
+    field public static final short DOCUMENT_POSITION_FOLLOWING = 4; // 0x4
+    field public static final short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 32; // 0x20
+    field public static final short DOCUMENT_POSITION_PRECEDING = 2; // 0x2
+    field public static final short DOCUMENT_TYPE_NODE = 10; // 0xa
+    field public static final short ELEMENT_NODE = 1; // 0x1
+    field public static final short ENTITY_NODE = 6; // 0x6
+    field public static final short ENTITY_REFERENCE_NODE = 5; // 0x5
+    field public static final short NOTATION_NODE = 12; // 0xc
+    field public static final short PROCESSING_INSTRUCTION_NODE = 7; // 0x7
+    field public static final short TEXT_NODE = 3; // 0x3
+  }
+
+  public interface NodeList {
+    method public int getLength();
+    method public org.w3c.dom.Node item(int);
+  }
+
+  public interface Notation extends org.w3c.dom.Node {
+    method public String getPublicId();
+    method public String getSystemId();
+  }
+
+  public interface ProcessingInstruction extends org.w3c.dom.Node {
+    method public String getData();
+    method public String getTarget();
+    method public void setData(String) throws org.w3c.dom.DOMException;
+  }
+
+  public interface Text extends org.w3c.dom.CharacterData {
+    method public String getWholeText();
+    method public boolean isElementContentWhitespace();
+    method public org.w3c.dom.Text replaceWholeText(String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.Text splitText(int) throws org.w3c.dom.DOMException;
+  }
+
+  public interface TypeInfo {
+    method public String getTypeName();
+    method public String getTypeNamespace();
+    method public boolean isDerivedFrom(String, String, int);
+    field public static final int DERIVATION_EXTENSION = 2; // 0x2
+    field public static final int DERIVATION_LIST = 8; // 0x8
+    field public static final int DERIVATION_RESTRICTION = 1; // 0x1
+    field public static final int DERIVATION_UNION = 4; // 0x4
+  }
+
+  public interface UserDataHandler {
+    method public void handle(short, String, Object, org.w3c.dom.Node, org.w3c.dom.Node);
+    field public static final short NODE_ADOPTED = 5; // 0x5
+    field public static final short NODE_CLONED = 1; // 0x1
+    field public static final short NODE_DELETED = 3; // 0x3
+    field public static final short NODE_IMPORTED = 2; // 0x2
+    field public static final short NODE_RENAMED = 4; // 0x4
+  }
+
+}
+
+package org.w3c.dom.ls {
+
+  public interface DOMImplementationLS {
+    method public org.w3c.dom.ls.LSInput createLSInput();
+    method public org.w3c.dom.ls.LSOutput createLSOutput();
+    method public org.w3c.dom.ls.LSParser createLSParser(short, String) throws org.w3c.dom.DOMException;
+    method public org.w3c.dom.ls.LSSerializer createLSSerializer();
+    field public static final short MODE_ASYNCHRONOUS = 2; // 0x2
+    field public static final short MODE_SYNCHRONOUS = 1; // 0x1
+  }
+
+  public class LSException extends java.lang.RuntimeException {
+    ctor public LSException(short, String);
+    field public static final short PARSE_ERR = 81; // 0x51
+    field public static final short SERIALIZE_ERR = 82; // 0x52
+    field public short code;
+  }
+
+  public interface LSInput {
+    method public String getBaseURI();
+    method public java.io.InputStream getByteStream();
+    method public boolean getCertifiedText();
+    method public java.io.Reader getCharacterStream();
+    method public String getEncoding();
+    method public String getPublicId();
+    method public String getStringData();
+    method public String getSystemId();
+    method public void setBaseURI(String);
+    method public void setByteStream(java.io.InputStream);
+    method public void setCertifiedText(boolean);
+    method public void setCharacterStream(java.io.Reader);
+    method public void setEncoding(String);
+    method public void setPublicId(String);
+    method public void setStringData(String);
+    method public void setSystemId(String);
+  }
+
+  public interface LSOutput {
+    method public java.io.OutputStream getByteStream();
+    method public java.io.Writer getCharacterStream();
+    method public String getEncoding();
+    method public String getSystemId();
+    method public void setByteStream(java.io.OutputStream);
+    method public void setCharacterStream(java.io.Writer);
+    method public void setEncoding(String);
+    method public void setSystemId(String);
+  }
+
+  public interface LSParser {
+    method public void abort();
+    method public boolean getAsync();
+    method public boolean getBusy();
+    method public org.w3c.dom.DOMConfiguration getDomConfig();
+    method public org.w3c.dom.ls.LSParserFilter getFilter();
+    method public org.w3c.dom.Document parse(org.w3c.dom.ls.LSInput) throws org.w3c.dom.DOMException, org.w3c.dom.ls.LSException;
+    method public org.w3c.dom.Document parseURI(String) throws org.w3c.dom.DOMException, org.w3c.dom.ls.LSException;
+    method public org.w3c.dom.Node parseWithContext(org.w3c.dom.ls.LSInput, org.w3c.dom.Node, short) throws org.w3c.dom.DOMException, org.w3c.dom.ls.LSException;
+    method public void setFilter(org.w3c.dom.ls.LSParserFilter);
+    field public static final short ACTION_APPEND_AS_CHILDREN = 1; // 0x1
+    field public static final short ACTION_INSERT_AFTER = 4; // 0x4
+    field public static final short ACTION_INSERT_BEFORE = 3; // 0x3
+    field public static final short ACTION_REPLACE = 5; // 0x5
+    field public static final short ACTION_REPLACE_CHILDREN = 2; // 0x2
+  }
+
+  public interface LSParserFilter {
+    method public short acceptNode(org.w3c.dom.Node);
+    method public int getWhatToShow();
+    method public short startElement(org.w3c.dom.Element);
+    field public static final short FILTER_ACCEPT = 1; // 0x1
+    field public static final short FILTER_INTERRUPT = 4; // 0x4
+    field public static final short FILTER_REJECT = 2; // 0x2
+    field public static final short FILTER_SKIP = 3; // 0x3
+  }
+
+  public interface LSResourceResolver {
+    method public org.w3c.dom.ls.LSInput resolveResource(String, String, String, String, String);
+  }
+
+  public interface LSSerializer {
+    method public org.w3c.dom.DOMConfiguration getDomConfig();
+    method public String getNewLine();
+    method public void setNewLine(String);
+    method public boolean write(org.w3c.dom.Node, org.w3c.dom.ls.LSOutput) throws org.w3c.dom.ls.LSException;
+    method public String writeToString(org.w3c.dom.Node) throws org.w3c.dom.DOMException, org.w3c.dom.ls.LSException;
+    method public boolean writeToURI(org.w3c.dom.Node, String) throws org.w3c.dom.ls.LSException;
+  }
+
+}
+
+package org.xml.sax {
+
+  @Deprecated public interface AttributeList {
+    method @Deprecated public int getLength();
+    method @Deprecated public String getName(int);
+    method @Deprecated public String getType(int);
+    method @Deprecated public String getType(String);
+    method @Deprecated public String getValue(int);
+    method @Deprecated public String getValue(String);
+  }
+
+  public interface Attributes {
+    method public int getIndex(String, String);
+    method public int getIndex(String);
+    method public int getLength();
+    method public String getLocalName(int);
+    method public String getQName(int);
+    method public String getType(int);
+    method public String getType(String, String);
+    method public String getType(String);
+    method public String getURI(int);
+    method public String getValue(int);
+    method public String getValue(String, String);
+    method public String getValue(String);
+  }
+
+  public interface ContentHandler {
+    method public void characters(char[], int, int) throws org.xml.sax.SAXException;
+    method public void endDocument() throws org.xml.sax.SAXException;
+    method public void endElement(String, String, String) throws org.xml.sax.SAXException;
+    method public void endPrefixMapping(String) throws org.xml.sax.SAXException;
+    method public void ignorableWhitespace(char[], int, int) throws org.xml.sax.SAXException;
+    method public void processingInstruction(String, String) throws org.xml.sax.SAXException;
+    method public void setDocumentLocator(org.xml.sax.Locator);
+    method public void skippedEntity(String) throws org.xml.sax.SAXException;
+    method public void startDocument() throws org.xml.sax.SAXException;
+    method public void startElement(String, String, String, org.xml.sax.Attributes) throws org.xml.sax.SAXException;
+    method public void startPrefixMapping(String, String) throws org.xml.sax.SAXException;
+  }
+
+  public interface DTDHandler {
+    method public void notationDecl(String, String, String) throws org.xml.sax.SAXException;
+    method public void unparsedEntityDecl(String, String, String, String) throws org.xml.sax.SAXException;
+  }
+
+  @Deprecated public interface DocumentHandler {
+    method @Deprecated public void characters(char[], int, int) throws org.xml.sax.SAXException;
+    method @Deprecated public void endDocument() throws org.xml.sax.SAXException;
+    method @Deprecated public void endElement(String) throws org.xml.sax.SAXException;
+    method @Deprecated public void ignorableWhitespace(char[], int, int) throws org.xml.sax.SAXException;
+    method @Deprecated public void processingInstruction(String, String) throws org.xml.sax.SAXException;
+    method @Deprecated public void setDocumentLocator(org.xml.sax.Locator);
+    method @Deprecated public void startDocument() throws org.xml.sax.SAXException;
+    method @Deprecated public void startElement(String, org.xml.sax.AttributeList) throws org.xml.sax.SAXException;
+  }
+
+  public interface EntityResolver {
+    method public org.xml.sax.InputSource resolveEntity(String, String) throws java.io.IOException, org.xml.sax.SAXException;
+  }
+
+  public interface ErrorHandler {
+    method public void error(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method public void fatalError(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method public void warning(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+  }
+
+  @Deprecated public class HandlerBase implements org.xml.sax.DTDHandler org.xml.sax.DocumentHandler org.xml.sax.EntityResolver org.xml.sax.ErrorHandler {
+    ctor @Deprecated public HandlerBase();
+    method @Deprecated public void characters(char[], int, int) throws org.xml.sax.SAXException;
+    method @Deprecated public void endDocument() throws org.xml.sax.SAXException;
+    method @Deprecated public void endElement(String) throws org.xml.sax.SAXException;
+    method @Deprecated public void error(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method @Deprecated public void fatalError(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method @Deprecated public void ignorableWhitespace(char[], int, int) throws org.xml.sax.SAXException;
+    method @Deprecated public void notationDecl(String, String, String);
+    method @Deprecated public void processingInstruction(String, String) throws org.xml.sax.SAXException;
+    method @Deprecated public org.xml.sax.InputSource resolveEntity(String, String) throws org.xml.sax.SAXException;
+    method @Deprecated public void setDocumentLocator(org.xml.sax.Locator);
+    method @Deprecated public void startDocument() throws org.xml.sax.SAXException;
+    method @Deprecated public void startElement(String, org.xml.sax.AttributeList) throws org.xml.sax.SAXException;
+    method @Deprecated public void unparsedEntityDecl(String, String, String, String);
+    method @Deprecated public void warning(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+  }
+
+  public class InputSource {
+    ctor public InputSource();
+    ctor public InputSource(String);
+    ctor public InputSource(java.io.InputStream);
+    ctor public InputSource(java.io.Reader);
+    method public java.io.InputStream getByteStream();
+    method public java.io.Reader getCharacterStream();
+    method public String getEncoding();
+    method public String getPublicId();
+    method public String getSystemId();
+    method public void setByteStream(java.io.InputStream);
+    method public void setCharacterStream(java.io.Reader);
+    method public void setEncoding(String);
+    method public void setPublicId(String);
+    method public void setSystemId(String);
+  }
+
+  public interface Locator {
+    method public int getColumnNumber();
+    method public int getLineNumber();
+    method public String getPublicId();
+    method public String getSystemId();
+  }
+
+  @Deprecated public interface Parser {
+    method @Deprecated public void parse(org.xml.sax.InputSource) throws java.io.IOException, org.xml.sax.SAXException;
+    method @Deprecated public void parse(String) throws java.io.IOException, org.xml.sax.SAXException;
+    method @Deprecated public void setDTDHandler(org.xml.sax.DTDHandler);
+    method @Deprecated public void setDocumentHandler(org.xml.sax.DocumentHandler);
+    method @Deprecated public void setEntityResolver(org.xml.sax.EntityResolver);
+    method @Deprecated public void setErrorHandler(org.xml.sax.ErrorHandler);
+    method @Deprecated public void setLocale(java.util.Locale) throws org.xml.sax.SAXException;
+  }
+
+  public class SAXException extends java.lang.Exception {
+    ctor public SAXException();
+    ctor public SAXException(String);
+    ctor public SAXException(Exception);
+    ctor public SAXException(String, Exception);
+    method public Exception getException();
+  }
+
+  public class SAXNotRecognizedException extends org.xml.sax.SAXException {
+    ctor public SAXNotRecognizedException();
+    ctor public SAXNotRecognizedException(String);
+  }
+
+  public class SAXNotSupportedException extends org.xml.sax.SAXException {
+    ctor public SAXNotSupportedException();
+    ctor public SAXNotSupportedException(String);
+  }
+
+  public class SAXParseException extends org.xml.sax.SAXException {
+    ctor public SAXParseException(String, org.xml.sax.Locator);
+    ctor public SAXParseException(String, org.xml.sax.Locator, Exception);
+    ctor public SAXParseException(String, String, String, int, int);
+    ctor public SAXParseException(String, String, String, int, int, Exception);
+    method public int getColumnNumber();
+    method public int getLineNumber();
+    method public String getPublicId();
+    method public String getSystemId();
+  }
+
+  public interface XMLFilter extends org.xml.sax.XMLReader {
+    method public org.xml.sax.XMLReader getParent();
+    method public void setParent(org.xml.sax.XMLReader);
+  }
+
+  public interface XMLReader {
+    method public org.xml.sax.ContentHandler getContentHandler();
+    method public org.xml.sax.DTDHandler getDTDHandler();
+    method public org.xml.sax.EntityResolver getEntityResolver();
+    method public org.xml.sax.ErrorHandler getErrorHandler();
+    method public boolean getFeature(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void parse(org.xml.sax.InputSource) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void setContentHandler(org.xml.sax.ContentHandler);
+    method public void setDTDHandler(org.xml.sax.DTDHandler);
+    method public void setEntityResolver(org.xml.sax.EntityResolver);
+    method public void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setFeature(String, boolean) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+  }
+
+}
+
+package org.xml.sax.ext {
+
+  public interface Attributes2 extends org.xml.sax.Attributes {
+    method public boolean isDeclared(int);
+    method public boolean isDeclared(String);
+    method public boolean isDeclared(String, String);
+    method public boolean isSpecified(int);
+    method public boolean isSpecified(String, String);
+    method public boolean isSpecified(String);
+  }
+
+  public class Attributes2Impl extends org.xml.sax.helpers.AttributesImpl implements org.xml.sax.ext.Attributes2 {
+    ctor public Attributes2Impl();
+    ctor public Attributes2Impl(org.xml.sax.Attributes);
+    method public boolean isDeclared(int);
+    method public boolean isDeclared(String, String);
+    method public boolean isDeclared(String);
+    method public boolean isSpecified(int);
+    method public boolean isSpecified(String, String);
+    method public boolean isSpecified(String);
+    method public void setDeclared(int, boolean);
+    method public void setSpecified(int, boolean);
+  }
+
+  public interface DeclHandler {
+    method public void attributeDecl(String, String, String, String, String) throws org.xml.sax.SAXException;
+    method public void elementDecl(String, String) throws org.xml.sax.SAXException;
+    method public void externalEntityDecl(String, String, String) throws org.xml.sax.SAXException;
+    method public void internalEntityDecl(String, String) throws org.xml.sax.SAXException;
+  }
+
+  public class DefaultHandler2 extends org.xml.sax.helpers.DefaultHandler implements org.xml.sax.ext.DeclHandler org.xml.sax.ext.EntityResolver2 org.xml.sax.ext.LexicalHandler {
+    ctor public DefaultHandler2();
+    method public void attributeDecl(String, String, String, String, String) throws org.xml.sax.SAXException;
+    method public void comment(char[], int, int) throws org.xml.sax.SAXException;
+    method public void elementDecl(String, String) throws org.xml.sax.SAXException;
+    method public void endCDATA() throws org.xml.sax.SAXException;
+    method public void endDTD() throws org.xml.sax.SAXException;
+    method public void endEntity(String) throws org.xml.sax.SAXException;
+    method public void externalEntityDecl(String, String, String) throws org.xml.sax.SAXException;
+    method public org.xml.sax.InputSource getExternalSubset(String, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void internalEntityDecl(String, String) throws org.xml.sax.SAXException;
+    method public org.xml.sax.InputSource resolveEntity(String, String, String, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void startCDATA() throws org.xml.sax.SAXException;
+    method public void startDTD(String, String, String) throws org.xml.sax.SAXException;
+    method public void startEntity(String) throws org.xml.sax.SAXException;
+  }
+
+  public interface EntityResolver2 extends org.xml.sax.EntityResolver {
+    method public org.xml.sax.InputSource getExternalSubset(String, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public org.xml.sax.InputSource resolveEntity(String, String, String, String) throws java.io.IOException, org.xml.sax.SAXException;
+  }
+
+  public interface LexicalHandler {
+    method public void comment(char[], int, int) throws org.xml.sax.SAXException;
+    method public void endCDATA() throws org.xml.sax.SAXException;
+    method public void endDTD() throws org.xml.sax.SAXException;
+    method public void endEntity(String) throws org.xml.sax.SAXException;
+    method public void startCDATA() throws org.xml.sax.SAXException;
+    method public void startDTD(String, String, String) throws org.xml.sax.SAXException;
+    method public void startEntity(String) throws org.xml.sax.SAXException;
+  }
+
+  public interface Locator2 extends org.xml.sax.Locator {
+    method public String getEncoding();
+    method public String getXMLVersion();
+  }
+
+  public class Locator2Impl extends org.xml.sax.helpers.LocatorImpl implements org.xml.sax.ext.Locator2 {
+    ctor public Locator2Impl();
+    ctor public Locator2Impl(org.xml.sax.Locator);
+    method public String getEncoding();
+    method public String getXMLVersion();
+    method public void setEncoding(String);
+    method public void setXMLVersion(String);
+  }
+
+}
+
+package org.xml.sax.helpers {
+
+  @Deprecated public class AttributeListImpl implements org.xml.sax.AttributeList {
+    ctor @Deprecated public AttributeListImpl();
+    ctor @Deprecated public AttributeListImpl(org.xml.sax.AttributeList);
+    method @Deprecated public void addAttribute(String, String, String);
+    method @Deprecated public void clear();
+    method @Deprecated public int getLength();
+    method @Deprecated public String getName(int);
+    method @Deprecated public String getType(int);
+    method @Deprecated public String getType(String);
+    method @Deprecated public String getValue(int);
+    method @Deprecated public String getValue(String);
+    method @Deprecated public void removeAttribute(String);
+    method @Deprecated public void setAttributeList(org.xml.sax.AttributeList);
+  }
+
+  public class AttributesImpl implements org.xml.sax.Attributes {
+    ctor public AttributesImpl();
+    ctor public AttributesImpl(org.xml.sax.Attributes);
+    method public void addAttribute(String, String, String, String, String);
+    method public void clear();
+    method public int getIndex(String, String);
+    method public int getIndex(String);
+    method public int getLength();
+    method public String getLocalName(int);
+    method public String getQName(int);
+    method public String getType(int);
+    method public String getType(String, String);
+    method public String getType(String);
+    method public String getURI(int);
+    method public String getValue(int);
+    method public String getValue(String, String);
+    method public String getValue(String);
+    method public void removeAttribute(int);
+    method public void setAttribute(int, String, String, String, String, String);
+    method public void setAttributes(org.xml.sax.Attributes);
+    method public void setLocalName(int, String);
+    method public void setQName(int, String);
+    method public void setType(int, String);
+    method public void setURI(int, String);
+    method public void setValue(int, String);
+  }
+
+  public class DefaultHandler implements org.xml.sax.ContentHandler org.xml.sax.DTDHandler org.xml.sax.EntityResolver org.xml.sax.ErrorHandler {
+    ctor public DefaultHandler();
+    method public void characters(char[], int, int) throws org.xml.sax.SAXException;
+    method public void endDocument() throws org.xml.sax.SAXException;
+    method public void endElement(String, String, String) throws org.xml.sax.SAXException;
+    method public void endPrefixMapping(String) throws org.xml.sax.SAXException;
+    method public void error(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method public void fatalError(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method public void ignorableWhitespace(char[], int, int) throws org.xml.sax.SAXException;
+    method public void notationDecl(String, String, String) throws org.xml.sax.SAXException;
+    method public void processingInstruction(String, String) throws org.xml.sax.SAXException;
+    method public org.xml.sax.InputSource resolveEntity(String, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void setDocumentLocator(org.xml.sax.Locator);
+    method public void skippedEntity(String) throws org.xml.sax.SAXException;
+    method public void startDocument() throws org.xml.sax.SAXException;
+    method public void startElement(String, String, String, org.xml.sax.Attributes) throws org.xml.sax.SAXException;
+    method public void startPrefixMapping(String, String) throws org.xml.sax.SAXException;
+    method public void unparsedEntityDecl(String, String, String, String) throws org.xml.sax.SAXException;
+    method public void warning(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+  }
+
+  public class LocatorImpl implements org.xml.sax.Locator {
+    ctor public LocatorImpl();
+    ctor public LocatorImpl(org.xml.sax.Locator);
+    method public int getColumnNumber();
+    method public int getLineNumber();
+    method public String getPublicId();
+    method public String getSystemId();
+    method public void setColumnNumber(int);
+    method public void setLineNumber(int);
+    method public void setPublicId(String);
+    method public void setSystemId(String);
+  }
+
+  public class NamespaceSupport {
+    ctor public NamespaceSupport();
+    method public boolean declarePrefix(String, String);
+    method public java.util.Enumeration getDeclaredPrefixes();
+    method public String getPrefix(String);
+    method public java.util.Enumeration getPrefixes();
+    method public java.util.Enumeration getPrefixes(String);
+    method public String getURI(String);
+    method public boolean isNamespaceDeclUris();
+    method public void popContext();
+    method public String[] processName(String, String[], boolean);
+    method public void pushContext();
+    method public void reset();
+    method public void setNamespaceDeclUris(boolean);
+    field public static final String NSDECL = "http://www.w3.org/xmlns/2000/";
+    field public static final String XMLNS = "http://www.w3.org/XML/1998/namespace";
+  }
+
+  public class ParserAdapter implements org.xml.sax.DocumentHandler org.xml.sax.XMLReader {
+    ctor public ParserAdapter() throws org.xml.sax.SAXException;
+    ctor public ParserAdapter(org.xml.sax.Parser);
+    method public void characters(char[], int, int) throws org.xml.sax.SAXException;
+    method public void endDocument() throws org.xml.sax.SAXException;
+    method public void endElement(String) throws org.xml.sax.SAXException;
+    method public org.xml.sax.ContentHandler getContentHandler();
+    method public org.xml.sax.DTDHandler getDTDHandler();
+    method public org.xml.sax.EntityResolver getEntityResolver();
+    method public org.xml.sax.ErrorHandler getErrorHandler();
+    method public boolean getFeature(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void ignorableWhitespace(char[], int, int) throws org.xml.sax.SAXException;
+    method public void parse(String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(org.xml.sax.InputSource) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void processingInstruction(String, String) throws org.xml.sax.SAXException;
+    method public void setContentHandler(org.xml.sax.ContentHandler);
+    method public void setDTDHandler(org.xml.sax.DTDHandler);
+    method public void setDocumentLocator(org.xml.sax.Locator);
+    method public void setEntityResolver(org.xml.sax.EntityResolver);
+    method public void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setFeature(String, boolean) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void startDocument() throws org.xml.sax.SAXException;
+    method public void startElement(String, org.xml.sax.AttributeList) throws org.xml.sax.SAXException;
+  }
+
+  @Deprecated public class ParserFactory {
+    method @Deprecated public static org.xml.sax.Parser makeParser() throws java.lang.ClassCastException, java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException, java.lang.NullPointerException;
+    method @Deprecated public static org.xml.sax.Parser makeParser(String) throws java.lang.ClassCastException, java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+  }
+
+  public class XMLFilterImpl implements org.xml.sax.ContentHandler org.xml.sax.DTDHandler org.xml.sax.EntityResolver org.xml.sax.ErrorHandler org.xml.sax.XMLFilter {
+    ctor public XMLFilterImpl();
+    ctor public XMLFilterImpl(org.xml.sax.XMLReader);
+    method public void characters(char[], int, int) throws org.xml.sax.SAXException;
+    method public void endDocument() throws org.xml.sax.SAXException;
+    method public void endElement(String, String, String) throws org.xml.sax.SAXException;
+    method public void endPrefixMapping(String) throws org.xml.sax.SAXException;
+    method public void error(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method public void fatalError(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+    method public org.xml.sax.ContentHandler getContentHandler();
+    method public org.xml.sax.DTDHandler getDTDHandler();
+    method public org.xml.sax.EntityResolver getEntityResolver();
+    method public org.xml.sax.ErrorHandler getErrorHandler();
+    method public boolean getFeature(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public org.xml.sax.XMLReader getParent();
+    method public Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void ignorableWhitespace(char[], int, int) throws org.xml.sax.SAXException;
+    method public void notationDecl(String, String, String) throws org.xml.sax.SAXException;
+    method public void parse(org.xml.sax.InputSource) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void processingInstruction(String, String) throws org.xml.sax.SAXException;
+    method public org.xml.sax.InputSource resolveEntity(String, String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void setContentHandler(org.xml.sax.ContentHandler);
+    method public void setDTDHandler(org.xml.sax.DTDHandler);
+    method public void setDocumentLocator(org.xml.sax.Locator);
+    method public void setEntityResolver(org.xml.sax.EntityResolver);
+    method public void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setFeature(String, boolean) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setParent(org.xml.sax.XMLReader);
+    method public void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void skippedEntity(String) throws org.xml.sax.SAXException;
+    method public void startDocument() throws org.xml.sax.SAXException;
+    method public void startElement(String, String, String, org.xml.sax.Attributes) throws org.xml.sax.SAXException;
+    method public void startPrefixMapping(String, String) throws org.xml.sax.SAXException;
+    method public void unparsedEntityDecl(String, String, String, String) throws org.xml.sax.SAXException;
+    method public void warning(org.xml.sax.SAXParseException) throws org.xml.sax.SAXException;
+  }
+
+  public class XMLReaderAdapter implements org.xml.sax.ContentHandler org.xml.sax.Parser {
+    ctor public XMLReaderAdapter() throws org.xml.sax.SAXException;
+    ctor public XMLReaderAdapter(org.xml.sax.XMLReader);
+    method public void characters(char[], int, int) throws org.xml.sax.SAXException;
+    method public void endDocument() throws org.xml.sax.SAXException;
+    method public void endElement(String, String, String) throws org.xml.sax.SAXException;
+    method public void endPrefixMapping(String);
+    method public void ignorableWhitespace(char[], int, int) throws org.xml.sax.SAXException;
+    method public void parse(String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(org.xml.sax.InputSource) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void processingInstruction(String, String) throws org.xml.sax.SAXException;
+    method public void setDTDHandler(org.xml.sax.DTDHandler);
+    method public void setDocumentHandler(org.xml.sax.DocumentHandler);
+    method public void setDocumentLocator(org.xml.sax.Locator);
+    method public void setEntityResolver(org.xml.sax.EntityResolver);
+    method public void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setLocale(java.util.Locale) throws org.xml.sax.SAXException;
+    method public void skippedEntity(String) throws org.xml.sax.SAXException;
+    method public void startDocument() throws org.xml.sax.SAXException;
+    method public void startElement(String, String, String, org.xml.sax.Attributes) throws org.xml.sax.SAXException;
+    method public void startPrefixMapping(String, String);
+  }
+
+  public final class XMLReaderFactory {
+    method public static org.xml.sax.XMLReader createXMLReader() throws org.xml.sax.SAXException;
+    method public static org.xml.sax.XMLReader createXMLReader(String) throws org.xml.sax.SAXException;
+  }
+
+}
+
+package org.xmlpull.v1 {
+
+  public interface XmlPullParser {
+    method public void defineEntityReplacementText(String, String) throws org.xmlpull.v1.XmlPullParserException;
+    method public int getAttributeCount();
+    method public String getAttributeName(int);
+    method public String getAttributeNamespace(int);
+    method public String getAttributePrefix(int);
+    method public String getAttributeType(int);
+    method public String getAttributeValue(int);
+    method public String getAttributeValue(String, String);
+    method public int getColumnNumber();
+    method public int getDepth();
+    method public int getEventType() throws org.xmlpull.v1.XmlPullParserException;
+    method public boolean getFeature(String);
+    method public String getInputEncoding();
+    method public int getLineNumber();
+    method public String getName();
+    method public String getNamespace(String);
+    method public String getNamespace();
+    method public int getNamespaceCount(int) throws org.xmlpull.v1.XmlPullParserException;
+    method public String getNamespacePrefix(int) throws org.xmlpull.v1.XmlPullParserException;
+    method public String getNamespaceUri(int) throws org.xmlpull.v1.XmlPullParserException;
+    method public String getPositionDescription();
+    method public String getPrefix();
+    method public Object getProperty(String);
+    method public String getText();
+    method public char[] getTextCharacters(int[]);
+    method public boolean isAttributeDefault(int);
+    method public boolean isEmptyElementTag() throws org.xmlpull.v1.XmlPullParserException;
+    method public boolean isWhitespace() throws org.xmlpull.v1.XmlPullParserException;
+    method public int next() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public int nextTag() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public String nextText() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public int nextToken() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void require(int, String, String) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void setFeature(String, boolean) throws org.xmlpull.v1.XmlPullParserException;
+    method public void setInput(java.io.Reader) throws org.xmlpull.v1.XmlPullParserException;
+    method public void setInput(java.io.InputStream, String) throws org.xmlpull.v1.XmlPullParserException;
+    method public void setProperty(String, Object) throws org.xmlpull.v1.XmlPullParserException;
+    field public static final int CDSECT = 5; // 0x5
+    field public static final int COMMENT = 9; // 0x9
+    field public static final int DOCDECL = 10; // 0xa
+    field public static final int END_DOCUMENT = 1; // 0x1
+    field public static final int END_TAG = 3; // 0x3
+    field public static final int ENTITY_REF = 6; // 0x6
+    field public static final String FEATURE_PROCESS_DOCDECL = "http://xmlpull.org/v1/doc/features.html#process-docdecl";
+    field public static final String FEATURE_PROCESS_NAMESPACES = "http://xmlpull.org/v1/doc/features.html#process-namespaces";
+    field public static final String FEATURE_REPORT_NAMESPACE_ATTRIBUTES = "http://xmlpull.org/v1/doc/features.html#report-namespace-prefixes";
+    field public static final String FEATURE_VALIDATION = "http://xmlpull.org/v1/doc/features.html#validation";
+    field public static final int IGNORABLE_WHITESPACE = 7; // 0x7
+    field public static final String NO_NAMESPACE = "";
+    field public static final int PROCESSING_INSTRUCTION = 8; // 0x8
+    field public static final int START_DOCUMENT = 0; // 0x0
+    field public static final int START_TAG = 2; // 0x2
+    field public static final int TEXT = 4; // 0x4
+    field public static final String[] TYPES;
+  }
+
+  public class XmlPullParserException extends java.lang.Exception {
+    ctor public XmlPullParserException(String);
+    ctor public XmlPullParserException(String, org.xmlpull.v1.XmlPullParser, Throwable);
+    method public int getColumnNumber();
+    method public Throwable getDetail();
+    method public int getLineNumber();
+    field protected int column;
+    field protected Throwable detail;
+    field protected int row;
+  }
+
+  public class XmlPullParserFactory {
+    ctor protected XmlPullParserFactory();
+    method public boolean getFeature(String);
+    method public boolean isNamespaceAware();
+    method public boolean isValidating();
+    method public static org.xmlpull.v1.XmlPullParserFactory newInstance() throws org.xmlpull.v1.XmlPullParserException;
+    method public static org.xmlpull.v1.XmlPullParserFactory newInstance(String, Class) throws org.xmlpull.v1.XmlPullParserException;
+    method public org.xmlpull.v1.XmlPullParser newPullParser() throws org.xmlpull.v1.XmlPullParserException;
+    method public org.xmlpull.v1.XmlSerializer newSerializer() throws org.xmlpull.v1.XmlPullParserException;
+    method public void setFeature(String, boolean) throws org.xmlpull.v1.XmlPullParserException;
+    method public void setNamespaceAware(boolean);
+    method public void setValidating(boolean);
+    field public static final String PROPERTY_NAME = "org.xmlpull.v1.XmlPullParserFactory";
+    field protected String classNamesLocation;
+    field protected java.util.HashMap<java.lang.String,java.lang.Boolean> features;
+    field protected java.util.ArrayList parserClasses;
+    field protected java.util.ArrayList serializerClasses;
+  }
+
+  public interface XmlSerializer {
+    method public org.xmlpull.v1.XmlSerializer attribute(String, String, String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void cdsect(String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void comment(String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void docdecl(String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void endDocument() throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public org.xmlpull.v1.XmlSerializer endTag(String, String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void entityRef(String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void flush() throws java.io.IOException;
+    method public int getDepth();
+    method public boolean getFeature(String);
+    method public String getName();
+    method public String getNamespace();
+    method public String getPrefix(String, boolean) throws java.lang.IllegalArgumentException;
+    method public Object getProperty(String);
+    method public void ignorableWhitespace(String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void processingInstruction(String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setFeature(String, boolean) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setOutput(java.io.OutputStream, String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setOutput(java.io.Writer) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setPrefix(String, String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void setProperty(String, Object) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public void startDocument(String, Boolean) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public org.xmlpull.v1.XmlSerializer startTag(String, String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public org.xmlpull.v1.XmlSerializer text(String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+    method public org.xmlpull.v1.XmlSerializer text(char[], int, int) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
+  }
+
+}
+
+package org.xmlpull.v1.sax2 {
+
+  public class Driver implements org.xml.sax.Attributes org.xml.sax.Locator org.xml.sax.XMLReader {
+    ctor public Driver() throws org.xmlpull.v1.XmlPullParserException;
+    ctor public Driver(org.xmlpull.v1.XmlPullParser) throws org.xmlpull.v1.XmlPullParserException;
+    method public int getColumnNumber();
+    method public org.xml.sax.ContentHandler getContentHandler();
+    method public org.xml.sax.DTDHandler getDTDHandler();
+    method public org.xml.sax.EntityResolver getEntityResolver();
+    method public org.xml.sax.ErrorHandler getErrorHandler();
+    method public boolean getFeature(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public int getIndex(String, String);
+    method public int getIndex(String);
+    method public int getLength();
+    method public int getLineNumber();
+    method public String getLocalName(int);
+    method public Object getProperty(String) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public String getPublicId();
+    method public String getQName(int);
+    method public String getSystemId();
+    method public String getType(int);
+    method public String getType(String, String);
+    method public String getType(String);
+    method public String getURI(int);
+    method public String getValue(int);
+    method public String getValue(String, String);
+    method public String getValue(String);
+    method public void parse(org.xml.sax.InputSource) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parse(String) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void parseSubTree(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xml.sax.SAXException;
+    method public void setContentHandler(org.xml.sax.ContentHandler);
+    method public void setDTDHandler(org.xml.sax.DTDHandler);
+    method public void setEntityResolver(org.xml.sax.EntityResolver);
+    method public void setErrorHandler(org.xml.sax.ErrorHandler);
+    method public void setFeature(String, boolean) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method public void setProperty(String, Object) throws org.xml.sax.SAXNotRecognizedException, org.xml.sax.SAXNotSupportedException;
+    method protected void startElement(String, String, String) throws org.xml.sax.SAXException;
+    field protected static final String APACHE_DYNAMIC_VALIDATION_FEATURE = "http://apache.org/xml/features/validation/dynamic";
+    field protected static final String APACHE_SCHEMA_VALIDATION_FEATURE = "http://apache.org/xml/features/validation/schema";
+    field protected static final String DECLARATION_HANDLER_PROPERTY = "http://xml.org/sax/properties/declaration-handler";
+    field protected static final String LEXICAL_HANDLER_PROPERTY = "http://xml.org/sax/properties/lexical-handler";
+    field protected static final String NAMESPACES_FEATURE = "http://xml.org/sax/features/namespaces";
+    field protected static final String NAMESPACE_PREFIXES_FEATURE = "http://xml.org/sax/features/namespace-prefixes";
+    field protected static final String VALIDATION_FEATURE = "http://xml.org/sax/features/validation";
+    field protected org.xml.sax.ContentHandler contentHandler;
+    field protected org.xml.sax.ErrorHandler errorHandler;
+    field protected org.xmlpull.v1.XmlPullParser pp;
+    field protected String systemId;
+  }
+
+}
+
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
new file mode 100644
index 0000000..15741a7
--- /dev/null
+++ b/api/module-lib-current.txt
@@ -0,0 +1,865 @@
+// Signature format: 2.0
+package android.compat {
+
+  public final class Compatibility {
+    method public static void clearBehaviorChangeDelegate();
+    method public static void clearOverrides();
+    method public static boolean isChangeEnabled(long);
+    method public static void reportUnconditionalChange(long);
+    method public static void setBehaviorChangeDelegate(android.compat.Compatibility.BehaviorChangeDelegate);
+    method public static void setOverrides(android.compat.Compatibility.ChangeConfig);
+  }
+
+  public static interface Compatibility.BehaviorChangeDelegate {
+    method public default boolean isChangeEnabled(long);
+    method public default void onChangeReported(long);
+  }
+
+  public static final class Compatibility.ChangeConfig {
+    ctor public Compatibility.ChangeConfig(@NonNull java.util.Set<java.lang.Long>, @NonNull java.util.Set<java.lang.Long>);
+    method @NonNull public long[] getDisabledChangesArray();
+    method @NonNull public java.util.Set<java.lang.Long> getDisabledSet();
+    method @NonNull public long[] getEnabledChangesArray();
+    method @NonNull public java.util.Set<java.lang.Long> getEnabledSet();
+    method public boolean isEmpty();
+    method public boolean isForceDisabled(long);
+    method public boolean isForceEnabled(long);
+  }
+
+}
+
+package android.system {
+
+  public final class NetlinkSocketAddress extends java.net.SocketAddress {
+    ctor public NetlinkSocketAddress(int, int);
+    method public int getGroupsMask();
+    method public int getPortId();
+  }
+
+  public final class Os {
+    method @Nullable public static android.system.StructCapUserData[] capget(@NonNull android.system.StructCapUserHeader) throws android.system.ErrnoException;
+    method public static void capset(@NonNull android.system.StructCapUserHeader, @NonNull android.system.StructCapUserData[]) throws android.system.ErrnoException;
+    method public static int getpgid(int) throws android.system.ErrnoException;
+    method @Nullable public static android.system.StructRlimit getrlimit(int) throws android.system.ErrnoException;
+    method public static int getsockoptInt(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method @Nullable public static android.system.StructLinger getsockoptLinger(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method public static int ioctlInt(@NonNull java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method @Nullable public static java.io.FileDescriptor[] pipe2(int) throws android.system.ErrnoException;
+    method @Nullable public static String realpath(@Nullable String) throws android.system.ErrnoException;
+    method public static void setpgid(int, int) throws android.system.ErrnoException;
+    method public static void setregid(int, int) throws android.system.ErrnoException;
+    method public static void setreuid(int, int) throws android.system.ErrnoException;
+    method public static void setsockoptIfreq(@NonNull java.io.FileDescriptor, int, int, @Nullable String) throws android.system.ErrnoException;
+    method public static void setsockoptLinger(@NonNull java.io.FileDescriptor, int, int, @NonNull android.system.StructLinger) throws android.system.ErrnoException;
+    method public static long splice(@NonNull java.io.FileDescriptor, @Nullable android.system.Int64Ref, @NonNull java.io.FileDescriptor, @Nullable android.system.Int64Ref, long, int) throws android.system.ErrnoException;
+    method public static void unlink(@Nullable String) throws android.system.ErrnoException;
+  }
+
+  public final class OsConstants {
+    method public static int CAP_TO_INDEX(int);
+    method public static int CAP_TO_MASK(int);
+    field public static final int ARPHRD_LOOPBACK;
+    field public static final int EUSERS;
+    field public static final int MAP_POPULATE;
+    field public static final int O_DIRECT;
+    field public static final int PR_CAP_AMBIENT;
+    field public static final int PR_CAP_AMBIENT_RAISE;
+    field public static final int RLIMIT_NOFILE;
+    field public static final int RTMGRP_IPV4_IFADDR;
+    field public static final int SPLICE_F_MORE;
+    field public static final int SPLICE_F_MOVE;
+    field public static final int TIOCOUTQ;
+    field public static final int UDP_ENCAP;
+    field public static final int UDP_ENCAP_ESPINUDP;
+    field public static final int XATTR_CREATE;
+    field public static final int XATTR_REPLACE;
+    field public static final int _LINUX_CAPABILITY_VERSION_3;
+  }
+
+  public final class PacketSocketAddress extends java.net.SocketAddress {
+    ctor public PacketSocketAddress(int, int, byte[]);
+  }
+
+  public final class StructCapUserData {
+    ctor public StructCapUserData(int, int, int);
+    field public final int effective;
+    field public final int inheritable;
+    field public final int permitted;
+  }
+
+  public final class StructCapUserHeader {
+    ctor public StructCapUserHeader(int, int);
+  }
+
+  public final class StructLinger {
+    ctor public StructLinger(int, int);
+    method public boolean isOn();
+    field public final int l_linger;
+  }
+
+  public final class StructRlimit {
+    field public final long rlim_cur;
+  }
+
+  public final class UnixSocketAddress extends java.net.SocketAddress {
+    method public static android.system.UnixSocketAddress createFileSystem(String);
+  }
+
+  public final class VmSocketAddress extends java.net.SocketAddress {
+    ctor public VmSocketAddress(int, int);
+    method public int getSvmCid();
+    method public int getSvmPort();
+  }
+
+}
+
+package com.android.okhttp.internalandroidapi {
+
+  public final class AndroidResponseCacheAdapter {
+    ctor public AndroidResponseCacheAdapter(@NonNull com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder);
+    method public void close() throws java.io.IOException;
+    method public void delete() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method @Nullable public java.net.CacheResponse get(@NonNull java.net.URI, @NonNull String, @Nullable java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method @NonNull public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
+    method public int getHitCount();
+    method public long getMaxSize();
+    method public int getNetworkCount();
+    method public int getRequestCount();
+    method public long getSize() throws java.io.IOException;
+    method @Nullable public java.net.CacheRequest put(@NonNull java.net.URI, @NonNull java.net.URLConnection) throws java.io.IOException;
+  }
+
+  public interface HasCacheHolder {
+    method @NonNull public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
+  }
+
+  public static final class HasCacheHolder.CacheHolder {
+    method @NonNull public static com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder create(@NonNull java.io.File, long);
+    method public boolean isEquivalent(@NonNull java.io.File, long);
+  }
+
+}
+
+package dalvik.annotation.codegen {
+
+  @java.lang.annotation.Repeatable(CovariantReturnType.CovariantReturnTypes.class) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CovariantReturnType {
+    method public abstract int presentAfter();
+    method public abstract Class<?> returnType();
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public static @interface CovariantReturnType.CovariantReturnTypes {
+    method public abstract dalvik.annotation.codegen.CovariantReturnType[] value();
+  }
+
+}
+
+package dalvik.annotation.optimization {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface CriticalNative {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface FastNative {
+  }
+
+}
+
+package dalvik.system {
+
+  public final class AnnotatedStackTraceElement {
+    method @Nullable public Object getBlockedOn();
+    method @Nullable public Object[] getHeldLocks();
+    method @NonNull public StackTraceElement getStackTraceElement();
+  }
+
+  public class AppSpecializationHooks {
+    method public static void handleCompatChangesBeforeBindingApplication();
+  }
+
+  public class BaseDexClassLoader extends java.lang.ClassLoader {
+    method public void addDexPath(@Nullable String);
+    method public void addNativePath(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public String getLdLibraryPath();
+    method public void reportClassLoaderChain();
+    method public static void setReporter(@Nullable dalvik.system.BaseDexClassLoader.Reporter);
+  }
+
+  public static interface BaseDexClassLoader.Reporter {
+    method public void report(@NonNull java.util.Map<java.lang.String,java.lang.String>);
+  }
+
+  public final class BlockGuard {
+    method @NonNull public static dalvik.system.BlockGuard.Policy getThreadPolicy();
+    method @NonNull public static dalvik.system.BlockGuard.VmPolicy getVmPolicy();
+    method public static void setThreadPolicy(@NonNull dalvik.system.BlockGuard.Policy);
+    method public static void setVmPolicy(@NonNull dalvik.system.BlockGuard.VmPolicy);
+    field public static final dalvik.system.BlockGuard.Policy LAX_POLICY;
+    field public static final dalvik.system.BlockGuard.VmPolicy LAX_VM_POLICY;
+  }
+
+  public static interface BlockGuard.Policy {
+    method public int getPolicyMask();
+    method public void onReadFromDisk();
+    method public void onUnbufferedIO();
+    method public void onWriteToDisk();
+  }
+
+  public static interface BlockGuard.VmPolicy {
+    method public void onPathAccess(@NonNull String);
+  }
+
+  public final class CloseGuard {
+    method public void close();
+    method public static dalvik.system.CloseGuard get();
+    method public static dalvik.system.CloseGuard.Reporter getReporter();
+    method public void open(String);
+    method public void openWithCallSite(String, String);
+    method public static void setEnabled(boolean);
+    method public static void setReporter(dalvik.system.CloseGuard.Reporter);
+    method public void warnIfOpen();
+  }
+
+  public static interface CloseGuard.Reporter {
+    method public void report(String, Throwable);
+    method public default void report(String);
+  }
+
+  public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
+    ctor public DelegateLastClassLoader(String, String, ClassLoader, ClassLoader[]);
+  }
+
+  @Deprecated public final class DexFile {
+    method @Deprecated @NonNull public static dalvik.system.DexFile.OptimizationInfo getDexFileOptimizationInfo(@NonNull String, @NonNull String) throws java.io.FileNotFoundException;
+    method @Deprecated @Nullable public static String[] getDexFileOutputPaths(@NonNull String, @NonNull String) throws java.io.FileNotFoundException;
+    method @Deprecated public static int getDexOptNeeded(@NonNull String, @NonNull String, @NonNull String, @Nullable String, boolean, boolean) throws java.io.FileNotFoundException, java.io.IOException;
+    method @Deprecated @NonNull public static String getSafeModeCompilerFilter(@NonNull String);
+    method @Deprecated public static boolean isProfileGuidedCompilerFilter(@NonNull String);
+    method @Deprecated public static boolean isValidCompilerFilter(@NonNull String);
+    field @Deprecated public static final int DEX2OAT_FOR_FILTER = 3; // 0x3
+    field @Deprecated public static final int NO_DEXOPT_NEEDED = 0; // 0x0
+  }
+
+  @Deprecated public static final class DexFile.OptimizationInfo {
+    method @Deprecated @NonNull public String getReason();
+    method @Deprecated @NonNull public String getStatus();
+  }
+
+  public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
+    ctor public PathClassLoader(@NonNull String, @Nullable String, @Nullable ClassLoader, @Nullable ClassLoader[]);
+  }
+
+  public final class RuntimeHooks {
+    method public static void setTimeZoneIdSupplier(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public static void setUncaughtExceptionPreHandler(@Nullable java.lang.Thread.UncaughtExceptionHandler);
+  }
+
+  public abstract class SocketTagger {
+    ctor public SocketTagger();
+    method public static dalvik.system.SocketTagger get();
+    method public static void set(dalvik.system.SocketTagger);
+    method public abstract void tag(java.io.FileDescriptor) throws java.net.SocketException;
+    method public final void tag(java.net.Socket) throws java.net.SocketException;
+    method public final void tag(java.net.DatagramSocket) throws java.net.SocketException;
+    method public abstract void untag(java.io.FileDescriptor) throws java.net.SocketException;
+    method public final void untag(java.net.Socket) throws java.net.SocketException;
+    method public final void untag(java.net.DatagramSocket) throws java.net.SocketException;
+  }
+
+  public final class VMDebug {
+    method public static void attachAgent(String, ClassLoader) throws java.io.IOException;
+    method public static long countInstancesOfClass(Class, boolean);
+    method public static long[] countInstancesOfClasses(Class[], boolean);
+    method public static void dumpHprofData(String) throws java.io.IOException;
+    method public static void dumpHprofData(String, java.io.FileDescriptor) throws java.io.IOException;
+    method public static void dumpHprofDataDdms();
+    method public static void dumpReferenceTables();
+    method public static int getAllocCount(int);
+    method @dalvik.annotation.optimization.FastNative public static int getLoadedClassCount();
+    method public static int getMethodTracingMode();
+    method public static String getRuntimeStat(String);
+    method public static java.util.Map<java.lang.String,java.lang.String> getRuntimeStats();
+    method public static String[] getVmFeatureList();
+    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggerConnected();
+    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggingEnabled();
+    method @dalvik.annotation.optimization.FastNative public static long lastDebuggerActivity();
+    method @dalvik.annotation.optimization.FastNative public static void printLoadedClasses(int);
+    method public static void resetAllocCount(int);
+    method public static void setAllocTrackerStackDepth(int);
+    method public static void startAllocCounting();
+    method public static void startMethodTracing(String, int, int, boolean, int);
+    method public static void startMethodTracing(String, java.io.FileDescriptor, int, int, boolean, int, boolean);
+    method public static void startMethodTracingDdms(int, int, boolean, int);
+    method public static void stopAllocCounting();
+    method public static void stopMethodTracing();
+    method @dalvik.annotation.optimization.FastNative public static long threadCpuTimeNanos();
+    field public static final int KIND_ALL_COUNTS = -1; // 0xffffffff
+    field public static final int KIND_GLOBAL_ALLOCATED_BYTES = 2; // 0x2
+    field public static final int KIND_GLOBAL_ALLOCATED_OBJECTS = 1; // 0x1
+    field public static final int KIND_GLOBAL_CLASS_INIT_COUNT = 32; // 0x20
+    field public static final int KIND_GLOBAL_CLASS_INIT_TIME = 64; // 0x40
+    field public static final int KIND_GLOBAL_FREED_BYTES = 8; // 0x8
+    field public static final int KIND_GLOBAL_FREED_OBJECTS = 4; // 0x4
+    field public static final int KIND_GLOBAL_GC_INVOCATIONS = 16; // 0x10
+    field public static final int KIND_THREAD_ALLOCATED_BYTES = 131072; // 0x20000
+    field public static final int KIND_THREAD_ALLOCATED_OBJECTS = 65536; // 0x10000
+    field public static final int KIND_THREAD_GC_INVOCATIONS = 1048576; // 0x100000
+    field public static final int TRACE_COUNT_ALLOCS = 1; // 0x1
+  }
+
+  public final class VMRuntime {
+    method @dalvik.annotation.optimization.FastNative public long addressOf(Object);
+    method public static void bootCompleted();
+    method public void clampGrowthLimit();
+    method public void clearGrowthLimit();
+    method public static String getCurrentInstructionSet();
+    method public static String getInstructionSet(String);
+    method public static dalvik.system.VMRuntime getRuntime();
+    method public int getTargetSdkVersion();
+    method @dalvik.annotation.optimization.FastNative public boolean is64Bit();
+    method public static boolean is64BitAbi(String);
+    method public static boolean is64BitInstructionSet(String);
+    method @dalvik.annotation.optimization.FastNative public boolean isCheckJniEnabled();
+    method @dalvik.annotation.optimization.FastNative public boolean isNativeDebuggable();
+    method public static boolean isValidClassLoaderContext(String);
+    method @dalvik.annotation.optimization.FastNative public Object newNonMovableArray(Class<?>, int);
+    method @dalvik.annotation.optimization.FastNative public Object newUnpaddedArray(Class<?>, int);
+    method public void notifyStartupCompleted();
+    method public void preloadDexCaches();
+    method public static void registerAppInfo(String, String, String, String[], int);
+    method public void registerNativeAllocation(long);
+    method @Deprecated public void registerNativeAllocation(int);
+    method public void registerNativeFree(long);
+    method @Deprecated public void registerNativeFree(int);
+    method public static void registerSensitiveThread();
+    method public void requestConcurrentGC();
+    method public static void resetJitCounters();
+    method public static void setDedupeHiddenApiWarnings(boolean);
+    method public void setDisabledCompatChanges(long[]);
+    method public void setHiddenApiAccessLogSamplingRate(int);
+    method public void setHiddenApiExemptions(String[]);
+    method public static void setHiddenApiUsageLogger(dalvik.system.VMRuntime.HiddenApiUsageLogger);
+    method public static void setNonSdkApiUsageConsumer(java.util.function.Consumer<java.lang.String>);
+    method public static void setProcessDataDirectory(String);
+    method public static void setProcessPackageName(String);
+    method public void setTargetSdkVersion(int);
+    method public void updateProcessState(int);
+    method public String vmInstructionSet();
+    method public String vmLibrary();
+    field public static final int CODE_PATH_TYPE_PRIMARY_APK = 1; // 0x1
+    field public static final int CODE_PATH_TYPE_SECONDARY_DEX = 4; // 0x4
+    field public static final int CODE_PATH_TYPE_SPLIT_APK = 2; // 0x2
+    field public static final int SDK_VERSION_CUR_DEVELOPMENT = 10000; // 0x2710
+  }
+
+  public static interface VMRuntime.HiddenApiUsageLogger {
+    method public void hiddenApiUsed(int, String, String, int, boolean);
+    field public static final int ACCESS_METHOD_JNI = 2; // 0x2
+    field public static final int ACCESS_METHOD_LINKING = 3; // 0x3
+    field public static final int ACCESS_METHOD_NONE = 0; // 0x0
+    field public static final int ACCESS_METHOD_REFLECTION = 1; // 0x1
+  }
+
+  public final class VMStack {
+    method @Nullable @dalvik.annotation.optimization.FastNative public static dalvik.system.AnnotatedStackTraceElement[] getAnnotatedThreadStackTrace(Thread);
+  }
+
+  public final class ZygoteHooks {
+    method public static void gcAndFinalize();
+    method public static boolean isIndefiniteThreadSuspensionSafe();
+    method public static void onBeginPreload();
+    method public static void onEndPreload();
+    method public static void postForkChild(int, boolean, boolean, String);
+    method public static void postForkCommon();
+    method public static void postForkSystemServer(int);
+    method public static void preFork();
+    method public static void startZygoteNoThreadCreation();
+    method public static void stopZygoteNoThreadCreation();
+  }
+
+}
+
+package java.io {
+
+  public final class FileDescriptor {
+    method public int getInt$();
+    method public void setInt$(int);
+  }
+
+  public class FileInputStream extends java.io.InputStream {
+    ctor public FileInputStream(java.io.FileDescriptor, boolean);
+  }
+
+}
+
+package java.lang {
+
+  public class Thread implements java.lang.Runnable {
+    method public static java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionPreHandler();
+    method public static void setUncaughtExceptionPreHandler(java.lang.Thread.UncaughtExceptionHandler);
+  }
+
+}
+
+package java.net {
+
+  public class DatagramSocket implements java.io.Closeable {
+    method public java.io.FileDescriptor getFileDescriptor$();
+  }
+
+  public final class Inet4Address extends java.net.InetAddress {
+    field public static final java.net.InetAddress ALL;
+    field public static final java.net.InetAddress ANY;
+    field public static final java.net.InetAddress LOOPBACK;
+  }
+
+  public final class Inet6Address extends java.net.InetAddress {
+    field public static final java.net.InetAddress ANY;
+    field public static final java.net.InetAddress LOOPBACK;
+  }
+
+  public class InetAddress implements java.io.Serializable {
+    method public static void clearDnsCache();
+    method @NonNull public static java.net.InetAddress[] getAllByNameOnNet(@Nullable String, int) throws java.net.UnknownHostException;
+    method @NonNull public static java.net.InetAddress getByNameOnNet(@Nullable String, int) throws java.net.UnknownHostException;
+    method @Deprecated public static boolean isNumeric(String);
+    method @Deprecated public static java.net.InetAddress parseNumericAddress(String);
+  }
+
+  public class InetSocketAddress extends java.net.SocketAddress {
+    ctor public InetSocketAddress();
+  }
+
+  public class ServerSocket implements java.io.Closeable {
+    method public java.net.SocketImpl getImpl() throws java.net.SocketException;
+  }
+
+  public class Socket implements java.io.Closeable {
+    method public java.io.FileDescriptor getFileDescriptor$();
+  }
+
+  public abstract class SocketImpl implements java.net.SocketOptions {
+    method public java.io.FileDescriptor getFD$();
+  }
+
+}
+
+package java.nio {
+
+  public abstract class ByteBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.ByteBuffer> {
+    method public void setAccessible(boolean);
+  }
+
+  public class DirectByteBuffer extends java.nio.MappedByteBuffer {
+    ctor public DirectByteBuffer(int, long, java.io.FileDescriptor, Runnable, boolean);
+    method public final long address();
+    method public final void setAccessible(boolean);
+  }
+
+  public final class NIOAccess {
+    method public static Object getBaseArray(java.nio.Buffer);
+    method public static int getBaseArrayOffset(java.nio.Buffer);
+  }
+
+  public final class NioUtils {
+    method public static void freeDirectBuffer(java.nio.ByteBuffer);
+    method public static byte[] unsafeArray(java.nio.ByteBuffer);
+    method public static int unsafeArrayOffset(java.nio.ByteBuffer);
+  }
+
+}
+
+package java.security {
+
+  public abstract class Provider extends java.util.Properties {
+    method public void warmUpServiceProvision();
+  }
+
+  public abstract class Signature extends java.security.SignatureSpi {
+    method public java.security.SignatureSpi getCurrentSpi();
+  }
+
+}
+
+package java.text {
+
+  public abstract class DateFormat extends java.text.Format {
+    method public static final void set24HourTimePref(Boolean);
+  }
+
+}
+
+package java.util {
+
+  public class LinkedHashMap<K, V> extends java.util.HashMap<K,V> implements java.util.Map<K,V> {
+    method public java.util.Map.Entry<K,V> eldest();
+  }
+
+}
+
+package java.util.zip {
+
+  public class ZipEntry implements java.lang.Cloneable {
+    method public long getDataOffset();
+  }
+
+}
+
+package javax.crypto {
+
+  public class Cipher {
+    method public javax.crypto.CipherSpi getCurrentSpi();
+  }
+
+  public class Mac implements java.lang.Cloneable {
+    method public javax.crypto.MacSpi getCurrentSpi();
+  }
+
+}
+
+package javax.net.ssl {
+
+  public abstract class HttpsURLConnection extends java.net.HttpURLConnection {
+    method public static javax.net.ssl.HostnameVerifier getStrictHostnameVerifier();
+  }
+
+}
+
+package libcore.content.type {
+
+  public final class MimeMap {
+    method @NonNull public libcore.content.type.MimeMap.Builder buildUpon();
+    method @NonNull public static libcore.content.type.MimeMap.Builder builder();
+    method @NonNull public java.util.Set<java.lang.String> extensions();
+    method @NonNull public static libcore.content.type.MimeMap getDefault();
+    method @Nullable public String guessExtensionFromMimeType(@Nullable String);
+    method @Nullable public String guessMimeTypeFromExtension(@Nullable String);
+    method public boolean hasExtension(@Nullable String);
+    method public boolean hasMimeType(@Nullable String);
+    method @NonNull public java.util.Set<java.lang.String> mimeTypes();
+    method public static void setDefaultSupplier(@NonNull java.util.function.Supplier<libcore.content.type.MimeMap>);
+  }
+
+  public static final class MimeMap.Builder {
+    method @NonNull public libcore.content.type.MimeMap.Builder addMimeMapping(@NonNull String, @NonNull java.util.List<java.lang.String>);
+    method @NonNull public libcore.content.type.MimeMap build();
+  }
+
+}
+
+package libcore.io {
+
+  public class ForwardingOs implements libcore.io.Os {
+    ctor protected ForwardingOs(@NonNull libcore.io.Os);
+    method public boolean access(@Nullable String, int) throws android.system.ErrnoException;
+    method public java.io.FileDescriptor open(@Nullable String, int, int) throws android.system.ErrnoException;
+    method public void remove(@Nullable String) throws android.system.ErrnoException;
+    method public void rename(@Nullable String, @Nullable String) throws android.system.ErrnoException;
+    method @Nullable public android.system.StructStat stat(@Nullable String) throws android.system.ErrnoException;
+    method public void unlink(@Nullable String) throws android.system.ErrnoException;
+  }
+
+  public final class IoBridge {
+    method public static void closeAndSignalBlockedThreads(@NonNull java.io.FileDescriptor) throws java.io.IOException;
+    method @NonNull public static java.io.FileDescriptor open(@NonNull String, int) throws java.io.FileNotFoundException;
+    method public static int read(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int) throws java.io.IOException;
+    method public static void write(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int) throws java.io.IOException;
+  }
+
+  public final class IoUtils {
+    method public static int acquireRawFd(@NonNull java.io.FileDescriptor);
+    method public static void close(@Nullable java.io.FileDescriptor) throws java.io.IOException;
+    method public static void closeQuietly(@Nullable AutoCloseable);
+    method public static void closeQuietly(@Nullable java.io.FileDescriptor);
+    method public static void closeQuietly(@Nullable java.net.Socket);
+    method @NonNull public static byte[] readFileAsByteArray(@NonNull String) throws java.io.IOException;
+    method @NonNull public static String readFileAsString(@NonNull String) throws java.io.IOException;
+    method public static void setBlocking(@NonNull java.io.FileDescriptor, boolean) throws java.io.IOException;
+    method public static void setFdOwner(@NonNull java.io.FileDescriptor, @NonNull Object);
+  }
+
+  public final class Memory {
+    method public static void memmove(@NonNull Object, int, @NonNull Object, int, long);
+    method public static int peekInt(@NonNull byte[], int, @NonNull java.nio.ByteOrder);
+    method public static short peekShort(@NonNull byte[], int, @NonNull java.nio.ByteOrder);
+    method public static void pokeInt(@NonNull byte[], int, int, @NonNull java.nio.ByteOrder);
+    method public static void pokeLong(@NonNull byte[], int, long, @NonNull java.nio.ByteOrder);
+    method public static void pokeShort(@NonNull byte[], int, short, @NonNull java.nio.ByteOrder);
+  }
+
+  public interface Os {
+    method public static boolean compareAndSetDefault(libcore.io.Os, libcore.io.Os);
+    method public static libcore.io.Os getDefault();
+  }
+
+  public final class Streams {
+    method public static int copy(@NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws java.io.IOException;
+    method public static void readFully(@NonNull java.io.InputStream, @NonNull byte[]) throws java.io.IOException;
+    method @NonNull public static byte[] readFully(@NonNull java.io.InputStream) throws java.io.IOException;
+    method @NonNull public static String readFully(@NonNull java.io.Reader) throws java.io.IOException;
+    method @NonNull public static byte[] readFullyNoClose(@NonNull java.io.InputStream) throws java.io.IOException;
+    method public static int readSingleByte(@NonNull java.io.InputStream) throws java.io.IOException;
+    method public static long skipByReading(@NonNull java.io.InputStream, long) throws java.io.IOException;
+    method public static void writeSingleByte(@NonNull java.io.OutputStream, int) throws java.io.IOException;
+  }
+
+}
+
+package libcore.net {
+
+  public class InetAddressUtils {
+    method public static boolean isNumericAddress(String);
+    method public static java.net.InetAddress parseNumericAddress(String);
+  }
+
+  public abstract class NetworkSecurityPolicy {
+    ctor public NetworkSecurityPolicy();
+    method public static libcore.net.NetworkSecurityPolicy getInstance();
+    method public abstract boolean isCertificateTransparencyVerificationRequired(String);
+    method public abstract boolean isCleartextTrafficPermitted();
+    method public abstract boolean isCleartextTrafficPermitted(String);
+    method public static void setInstance(libcore.net.NetworkSecurityPolicy);
+  }
+
+}
+
+package libcore.net.event {
+
+  public final class NetworkEventDispatcher {
+    method public void dispatchNetworkConfigurationChange();
+    method public static libcore.net.event.NetworkEventDispatcher getInstance();
+  }
+
+}
+
+package libcore.net.http {
+
+  public interface Dns {
+    method @NonNull public java.util.List<java.net.InetAddress> lookup(@Nullable String) throws java.net.UnknownHostException;
+  }
+
+  public class HttpURLConnectionFactory {
+    method @NonNull public static libcore.net.http.HttpURLConnectionFactory createInstance();
+    method public java.net.URLConnection openConnection(@NonNull java.net.URL, @NonNull javax.net.SocketFactory, @NonNull java.net.Proxy) throws java.io.IOException;
+    method public void setDns(@NonNull libcore.net.http.Dns);
+    method public void setNewConnectionPool(int, long, @NonNull java.util.concurrent.TimeUnit);
+  }
+
+}
+
+package libcore.util {
+
+  public final class EmptyArray {
+    field @NonNull public static final boolean[] BOOLEAN;
+    field @NonNull public static final byte[] BYTE;
+    field @NonNull public static final float[] FLOAT;
+    field @NonNull public static final int[] INT;
+    field @NonNull public static final long[] LONG;
+    field @NonNull public static final Object[] OBJECT;
+    field @NonNull public static final String[] STRING;
+  }
+
+  public final class FP16 {
+    method public static short ceil(short);
+    method public static int compare(short, short);
+    method public static boolean equals(short, short);
+    method public static short floor(short);
+    method public static boolean greater(short, short);
+    method public static boolean greaterEquals(short, short);
+    method public static boolean isInfinite(short);
+    method public static boolean isNaN(short);
+    method public static boolean isNormalized(short);
+    method public static boolean less(short, short);
+    method public static boolean lessEquals(short, short);
+    method public static short max(short, short);
+    method public static short min(short, short);
+    method public static short rint(short);
+    method public static float toFloat(short);
+    method public static short toHalf(float);
+    method public static String toHexString(short);
+    method public static short trunc(short);
+    field public static final short EPSILON = 5120; // 0x1400
+    field public static final int EXPONENT_BIAS = 15; // 0xf
+    field public static final int EXPONENT_SHIFT = 10; // 0xa
+    field public static final int EXPONENT_SIGNIFICAND_MASK = 32767; // 0x7fff
+    field public static final short LOWEST_VALUE = -1025; // 0xfffffbff
+    field public static final int MAX_EXPONENT = 15; // 0xf
+    field public static final short MAX_VALUE = 31743; // 0x7bff
+    field public static final int MIN_EXPONENT = -14; // 0xfffffff2
+    field public static final short MIN_NORMAL = 1024; // 0x400
+    field public static final short MIN_VALUE = 1; // 0x1
+    field public static final short NEGATIVE_INFINITY = -1024; // 0xfffffc00
+    field public static final short NEGATIVE_ZERO = -32768; // 0xffff8000
+    field public static final short NaN = 32256; // 0x7e00
+    field public static final short POSITIVE_INFINITY = 31744; // 0x7c00
+    field public static final short POSITIVE_ZERO = 0; // 0x0
+    field public static final int SHIFTED_EXPONENT_MASK = 31; // 0x1f
+    field public static final int SIGNIFICAND_MASK = 1023; // 0x3ff
+    field public static final int SIGN_MASK = 32768; // 0x8000
+    field public static final int SIGN_SHIFT = 15; // 0xf
+    field public static final int SIZE = 16; // 0x10
+  }
+
+  public class HexEncoding {
+    method public static byte[] decode(String) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(String, boolean) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(char[]) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(char[], boolean) throws java.lang.IllegalArgumentException;
+    method public static char[] encode(byte[]);
+    method public static char[] encode(byte[], boolean);
+    method public static char[] encode(byte[], int, int);
+    method public static String encodeToString(byte, boolean);
+    method public static String encodeToString(byte[]);
+    method public static String encodeToString(byte[], boolean);
+  }
+
+  public class NativeAllocationRegistry {
+    ctor public NativeAllocationRegistry(@NonNull ClassLoader, long, long);
+    method public static void applyFreeFunction(long, long);
+    method public static libcore.util.NativeAllocationRegistry createMalloced(@NonNull ClassLoader, long, long);
+    method public static libcore.util.NativeAllocationRegistry createMalloced(@NonNull ClassLoader, long);
+    method public static libcore.util.NativeAllocationRegistry createNonmalloced(@NonNull ClassLoader, long, long);
+    method @NonNull public Runnable registerNativeAllocation(@NonNull Object, long);
+  }
+
+  public class SneakyThrow {
+    method public static void sneakyThrow(@NonNull Throwable);
+  }
+
+  public class XmlObjectFactory {
+    method @NonNull public static org.xml.sax.XMLReader newXMLReader();
+    method @NonNull public static org.xmlpull.v1.XmlPullParser newXmlPullParser();
+    method @NonNull public static org.xmlpull.v1.XmlSerializer newXmlSerializer();
+  }
+
+}
+
+package org.apache.harmony.dalvik.ddmc {
+
+  public class Chunk {
+    ctor public Chunk(int, byte[], int, int);
+    ctor public Chunk(int, java.nio.ByteBuffer);
+    field public int type;
+  }
+
+  public abstract class ChunkHandler {
+    ctor public ChunkHandler();
+    method public static org.apache.harmony.dalvik.ddmc.Chunk createFailChunk(int, String);
+    method public abstract org.apache.harmony.dalvik.ddmc.Chunk handleChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    method public static String name(int);
+    method public abstract void onConnected();
+    method public abstract void onDisconnected();
+    method public static int type(String);
+    method public static java.nio.ByteBuffer wrapChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    field public static final java.nio.ByteOrder CHUNK_ORDER;
+  }
+
+  public final class DdmServer {
+    method public static void registerHandler(int, org.apache.harmony.dalvik.ddmc.ChunkHandler);
+    method public static void registrationComplete();
+    method public static void sendChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    method public static org.apache.harmony.dalvik.ddmc.ChunkHandler unregisterHandler(int);
+  }
+
+  public final class DdmVmInternal {
+    method public static void setRecentAllocationsTrackingEnabled(boolean);
+    method public static void setThreadNotifyEnabled(boolean);
+  }
+
+}
+
+package org.json {
+
+  public class JSONObject {
+    method @NonNull public java.util.Set<java.lang.String> keySet();
+  }
+
+}
+
+package sun.misc {
+
+  public class Cleaner extends java.lang.ref.PhantomReference<java.lang.Object> {
+    method public void clean();
+    method public static sun.misc.Cleaner create(Object, Runnable);
+  }
+
+  public final class Unsafe {
+    method public int arrayBaseOffset(Class);
+    method public int arrayIndexScale(Class);
+    method @dalvik.annotation.optimization.FastNative public void copyMemory(long, long, long);
+    method @dalvik.annotation.optimization.FastNative public boolean getBoolean(Object, long);
+    method @dalvik.annotation.optimization.FastNative public byte getByte(Object, long);
+    method @dalvik.annotation.optimization.FastNative public byte getByte(long);
+    method @dalvik.annotation.optimization.FastNative public double getDouble(Object, long);
+    method @dalvik.annotation.optimization.FastNative public double getDouble(long);
+    method @dalvik.annotation.optimization.FastNative public float getFloat(Object, long);
+    method @dalvik.annotation.optimization.FastNative public float getFloat(long);
+    method @dalvik.annotation.optimization.FastNative public int getInt(Object, long);
+    method @dalvik.annotation.optimization.FastNative public int getInt(long);
+    method @dalvik.annotation.optimization.FastNative public long getLong(Object, long);
+    method @dalvik.annotation.optimization.FastNative public long getLong(long);
+    method @dalvik.annotation.optimization.FastNative public Object getObject(Object, long);
+    method public static sun.misc.Unsafe getUnsafe();
+    method public long objectFieldOffset(java.lang.reflect.Field);
+    method @dalvik.annotation.optimization.FastNative public void putBoolean(Object, long, boolean);
+    method @dalvik.annotation.optimization.FastNative public void putByte(Object, long, byte);
+    method @dalvik.annotation.optimization.FastNative public void putByte(long, byte);
+    method @dalvik.annotation.optimization.FastNative public void putDouble(Object, long, double);
+    method @dalvik.annotation.optimization.FastNative public void putDouble(long, double);
+    method @dalvik.annotation.optimization.FastNative public void putFloat(Object, long, float);
+    method @dalvik.annotation.optimization.FastNative public void putFloat(long, float);
+    method @dalvik.annotation.optimization.FastNative public void putInt(Object, long, int);
+    method @dalvik.annotation.optimization.FastNative public void putInt(long, int);
+    method @dalvik.annotation.optimization.FastNative public void putLong(Object, long, long);
+    method @dalvik.annotation.optimization.FastNative public void putLong(long, long);
+    method @dalvik.annotation.optimization.FastNative public void putObject(Object, long, Object);
+  }
+
+}
+
+package sun.security.jca {
+
+  public class Providers {
+    method public static Object startJarVerification();
+    method public static void stopJarVerification(Object);
+  }
+
+}
+
+package sun.security.pkcs {
+
+  public class PKCS7 {
+    ctor public PKCS7(java.io.InputStream) throws java.io.IOException, sun.security.pkcs.ParsingException;
+    ctor public PKCS7(byte[]) throws sun.security.pkcs.ParsingException;
+    method public java.security.cert.X509Certificate[] getCertificates();
+    method public sun.security.pkcs.SignerInfo[] getSignerInfos();
+    method public sun.security.pkcs.SignerInfo verify(sun.security.pkcs.SignerInfo, java.io.InputStream) throws java.io.IOException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
+    method public sun.security.pkcs.SignerInfo[] verify(byte[]) throws java.security.NoSuchAlgorithmException, java.security.SignatureException;
+  }
+
+  public class ParsingException extends java.io.IOException {
+  }
+
+  public class SignerInfo {
+    ctor public SignerInfo();
+    method public java.util.ArrayList<java.security.cert.X509Certificate> getCertificateChain(sun.security.pkcs.PKCS7) throws java.io.IOException;
+  }
+
+}
+
+package sun.security.util {
+
+  public final class ObjectIdentifier implements java.io.Serializable {
+    ctor public ObjectIdentifier(String) throws java.io.IOException;
+  }
+
+}
+
+package sun.security.x509 {
+
+  public class AlgorithmId implements java.io.Serializable {
+    ctor public AlgorithmId(sun.security.util.ObjectIdentifier);
+    method public String getName();
+  }
+
+}
+
diff --git a/mmodules/core_platform_api/api/platform/current-removed.txt b/api/module-lib-removed.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/current-removed.txt
copy to api/module-lib-removed.txt
diff --git a/mmodules/core_platform_api/api/platform/current-removed.txt b/api/removed.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/current-removed.txt
copy to api/removed.txt
diff --git a/mmodules/core_platform_api/api/platform/current-removed.txt b/api/system-current.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/current-removed.txt
copy to api/system-current.txt
diff --git a/mmodules/core_platform_api/api/platform/current-removed.txt b/api/system-removed.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/current-removed.txt
copy to api/system-removed.txt
diff --git a/benchmarks/Android.bp b/benchmarks/Android.bp
index 4f2f157..8548176 100644
--- a/benchmarks/Android.bp
+++ b/benchmarks/Android.bp
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_license"],
+}
+
 java_test {
     name: "benchmarks",
     srcs: ["src/**/*.java"],
diff --git a/benchmarks/src/benchmarks/BigIntegerBenchmark.java b/benchmarks/src/benchmarks/BigIntegerBenchmark.java
new file mode 100644
index 0000000..2b78c0a
--- /dev/null
+++ b/benchmarks/src/benchmarks/BigIntegerBenchmark.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2020 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 benchmarks;
+
+import java.math.BigInteger;
+
+/**
+ * Tries to measure important BigInteger operations across a variety of BigInteger sizes.
+ * Note that BigInteger implementations commonly need to use wildly different algorithms
+ * for different sizes, so relative performance may change substantially depending on the
+ * size of the integer.
+ * This is not structured as a proper benchmark; just run main(), e.g. with
+ * vogar libcore/benchmarks/src/benchmarks/BigIntegerBenchmark.java.
+ */
+public class BigIntegerBenchmark {
+  private static final boolean PRINT_TIMES = true;
+
+  private static long getStartTime() {
+    if (PRINT_TIMES) {
+      return System.nanoTime();
+    } else {
+      return 0;
+    }
+  }
+
+  private static void printTime(String s, long startTime, int reps) {
+    if (PRINT_TIMES) {
+      System.out.println(s
+          + (double)(System.nanoTime() - startTime) / 1000.0 / reps + " usecs / iter");
+    }
+  }
+
+  // A simple sum of products computation, mostly so we can check timing in the
+  // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2,
+  // repeating the multiplication, but not addition of 1, each time through the loop.
+  // Check the last few bits of the result as we go. Assumes n < 2^30.
+  // Note that we're actually squaring values in computing the product.
+  // That affects the algorithm used by some implementations.
+  private static void inner(int n, int prec) {
+    BigInteger big = BigInteger.TEN.pow(prec).shiftLeft(30).add(BigInteger.ONE);
+    BigInteger sum = BigInteger.ZERO;
+    for (int i = 0; i < n; ++i) {
+      sum = sum.add(big.multiply(big));
+    }
+    if (sum.and(BigInteger.valueOf(0x3fffffff)).intValue() != n) {
+      System.out.println("inner() got " + sum.and(BigInteger.valueOf(0x3fffffff))
+          + " instead of " + n);
+    }
+  }
+
+  // Execute the above rep times, optionally timing it.
+  private static void repeatInner(int n, int prec, int rep) {
+    long startTime = getStartTime();
+    for (int i = 0; i < rep; ++i) {
+      inner(n, prec);
+    }
+    printTime("inner(" + n + "," + prec + ") took ", startTime, rep);
+  }
+
+  // Approximate the sum of the first 1000 terms of the harmonic series (sum of 1/m as m
+  // goes from 1 to n) to about prec digits. The result has an implicit decimal point
+  // prec digits from the right.
+  private static BigInteger harmonic1000(int prec) {
+    BigInteger scaledOne = BigInteger.TEN.pow(prec);
+    BigInteger sum = BigInteger.ZERO;
+    for (int i = 1; i <= 1000; ++i) {
+      sum = sum.add(scaledOne.divide(BigInteger.valueOf(i)));
+    }
+    return sum;
+  }
+
+  // Execute the above rep times, optionally timing it.
+  // Check results for equality, and print one, to compaare against reference.
+  private static void repeatHarmonic1000(int prec, int rep) {
+    long startTime = getStartTime();
+    BigInteger refRes = harmonic1000(prec);
+    for (int i = 1; i < rep; ++i) {
+      BigInteger newRes = harmonic1000(prec);
+      if (!newRes.equals(refRes)) {
+        throw new AssertionError(newRes + " != " + refRes);
+      }
+    }
+    printTime("harmonic(1000) to " + prec + " digits took ", startTime, rep);
+    if (prec >= 50 && !refRes.toString()
+        .startsWith("748547086055034491265651820433390017652167916970")) {
+      throw new AssertionError("harmanic(" + prec + ") incorrectly produced " + refRes);
+    }
+  }
+
+  // Repeatedly execute just the base conversion from the last test, allowing
+  // us to time and check it for consistency as well.
+  private static void repeatToString(int prec, int rep) {
+    BigInteger refRes = harmonic1000(prec);
+    long startTime = getStartTime();
+    String refString = refRes.toString();
+    for (int i = 1; i < rep; ++i) {
+      // Disguise refRes to avoid compiler optimization issues.
+      BigInteger newRes = refRes.shiftLeft(30).add(BigInteger.valueOf(i)).shiftRight(30);
+      // The time-consuming part:
+      String newString = newRes.toString();
+      if (!newString.equals(refString)) {
+        System.out.println(newString + " != " + refString);
+      }
+    }
+    printTime("toString(" + prec + ") took ", startTime, rep);
+  }
+
+  // Compute base^exp, where base and result are scaled/multiplied by scaleBy to make them
+  // integers. exp >= 0 .
+  private static BigInteger myPow(BigInteger base, int exp, BigInteger scaleBy) {
+    if (exp == 0) {
+      return scaleBy; // Return one.
+    } else if ((exp & 1) != 0) {
+      BigInteger tmp = myPow(base, exp - 1, scaleBy);
+      return tmp.multiply(base).divide(scaleBy);
+    } else {
+      BigInteger tmp = myPow(base, exp / 2, scaleBy);
+      return tmp.multiply(tmp).divide(scaleBy);
+    }
+  }
+
+  // Approximate e by computing (1 + 1/n)^n to prec decimal digits.
+  // This isn't necessarily a very good approximation to e.
+  // Return the result, scaled by 10^prec.
+  private static BigInteger eApprox(int n, int prec) {
+    BigInteger scaledOne = BigInteger.TEN.pow(prec);
+    BigInteger base = scaledOne.add(scaledOne.divide(BigInteger.valueOf(n)));
+    return myPow(base, n, scaledOne);
+  }
+
+  // Repeatedly execute and check the above, printing one of the results
+  // to compare to reference.
+  private static void repeatEApprox(int n, int prec, int rep) {
+    long startTime = getStartTime();
+    BigInteger refRes = eApprox(n, prec);
+    for (int i = 1; i < rep; ++i) {
+      BigInteger newRes = eApprox(n, prec);
+      if (!newRes.equals(refRes)) {
+        throw new AssertionError(newRes + " != " + refRes);
+      }
+    }
+    printTime("eApprox(" + n + "," + prec + ") took ", startTime, rep);
+    if (n >= 100000 && prec >= 10 && !refRes.toString().startsWith("271826")) {
+      throw new AssertionError("eApprox(" + n + "," + prec + ") incorrectly produced "
+          + refRes);
+    }
+  }
+
+  // Test / time modPow()
+  private static void repeatModPow(int len, int rep) {
+    BigInteger odd1 = BigInteger.TEN.pow(len / 2).add(BigInteger.ONE);
+    BigInteger odd2 = BigInteger.TEN.pow(len / 2).add(BigInteger.valueOf(17));
+    BigInteger product = odd1.multiply(odd2);
+    BigInteger exponent = BigInteger.TEN.pow(len / 2 - 1);
+    BigInteger base = BigInteger.TEN.pow(len / 4);
+    long startTime = getStartTime();
+    BigInteger lastRes = null;
+    for (int i = 0; i < rep; ++i) {
+      BigInteger newRes = base.modPow(exponent, product);
+      if (i != 0 && !newRes.equals(lastRes)) {
+        System.out.println(newRes + " != " + lastRes);
+      }
+      lastRes = newRes;
+    }
+    printTime("ModPow() at decimal length " + len + " took ", startTime, rep);
+    if (!lastRes.mod(odd1).equals(base.modPow(exponent, odd1))) {
+      throw new AssertionError("ModPow() result incorrect mod odd1:" + odd1
+          + "; lastRes.mod(odd1)=" + lastRes.mod(odd1) + " vs. "
+          + "base.modPow(exponent, odd1)=" + base.modPow(exponent, odd1) + " base="
+          + base + " exponent=" + exponent);
+    }
+    if (!lastRes.mod(odd2).equals(base.modPow(exponent, odd2))) {
+      throw new AssertionError("ModPow() result incorrect mod odd2");
+    }
+  }
+
+  // Test / time modInverse()
+  private static void repeatModInverse(int len, int rep) {
+    BigInteger odd1 = BigInteger.TEN.pow(len / 2).add(BigInteger.ONE);
+    BigInteger odd2 = BigInteger.TEN.pow(len / 2).add(BigInteger.valueOf(17));
+    BigInteger product = odd1.multiply(odd2);
+    BigInteger arg = BigInteger.ONE.shiftLeft(len / 4);
+    long startTime = getStartTime();
+    BigInteger lastRes = null;
+    for (int i = 0; i < rep; ++i) {
+      BigInteger newRes = arg.modInverse(product);
+      if (i != 0 && !newRes.equals(lastRes)) {
+        System.out.println(newRes + " != " + lastRes);
+      }
+      lastRes = newRes;
+    }
+    printTime("ModInverse() at decimal length " + len + " took ", startTime, rep);
+    if (!lastRes.mod(odd1).equals(arg.modInverse(odd1))) {
+      throw new AssertionError("ModInverse() result incorrect mod odd1");
+    }
+    if (!lastRes.mod(odd2).equals(arg.modInverse(odd2))) {
+      throw new AssertionError("ModInverse() result incorrect mod odd2");
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    for (int i = 10; i <= 10_000; i *= 10) {
+      repeatInner(1000, i, PRINT_TIMES ? Math.min(20_000 / i, 3_000) : 2);
+    }
+    for (int i = 5; i <= 5_000; i *= 10) {
+      repeatHarmonic1000(i, PRINT_TIMES ? Math.min(20_000 / i, 3_000) : 2);
+    }
+    for (int i = 5; i <= 5_000; i *= 10) {
+      repeatToString(i, PRINT_TIMES ? Math.min(20_000 / i, 3_000) : 2);
+    }
+    for (int i = 10; i <= 10_000; i *= 10) {
+      repeatEApprox(100_000, i, PRINT_TIMES ? 50_000 / i : 2);
+    }
+    for (int i = 5; i <= 5_000; i *= 10) {
+      repeatModPow(i, PRINT_TIMES ? 10_000 / i : 2);
+    }
+    for (int i = 10; i <= 10_000; i *= 10) {
+      repeatModInverse(i, PRINT_TIMES ? 20_000 / i : 2);
+    }
+  }
+}
diff --git a/benchmarks/src/benchmarks/LocaleDataBenchmark.java b/benchmarks/src/benchmarks/LocaleDataBenchmark.java
new file mode 100644
index 0000000..adf6768
--- /dev/null
+++ b/benchmarks/src/benchmarks/LocaleDataBenchmark.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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 benchmarks;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import java.util.Locale;
+import libcore.icu.LocaleData;
+
+public final class LocaleDataBenchmark {
+    private static final Locale[] TEST_LOCALES = new Locale[]  {
+        Locale.forLanguageTag("en-US"),
+        Locale.forLanguageTag("jp-JP"),
+        Locale.forLanguageTag("es-419"),
+        Locale.forLanguageTag("ar-EG"),
+        Locale.forLanguageTag("zh-CN"),
+    };
+
+    public void timeInitLocaleData(int reps) {
+        for (int rep = 0; rep < reps; ++rep) {
+            for (Locale locale : TEST_LOCALES) {
+                LocaleData.initLocaleData(locale);
+            }
+        }
+    }
+}
diff --git a/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java b/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java
index 81a3ab5..f513bf4 100644
--- a/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java
+++ b/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java
@@ -20,12 +20,13 @@
 import java.util.Random;
 
 /**
- * This pretends to measure performance of operations on small BigIntegers.
- * Given our current implementation, this is really a way to measure performance of
- * finalization and JNI.
+ * This measures performance of operations on small BigIntegers.
  * We manually determine the number of iterations so that it should cause total memory
  * allocation on the order of a few hundred megabytes.  Due to BigInteger's reliance on
  * finalization, these may unfortunately all be kept around at once.
+ *
+ * This is not structured as a proper benchmark; just run main(), e.g. with
+ * vogar libcore/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java
  */
 public class SmallBigIntegerBenchmark {
     // We allocate about 2 1/3 BigIntegers per iteration.
diff --git a/benchmarks/src/benchmarks/regression/CharacterBenchmark.java b/benchmarks/src/benchmarks/regression/CharacterBenchmark.java
index bb9a51f..bd907f8 100644
--- a/benchmarks/src/benchmarks/regression/CharacterBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/CharacterBenchmark.java
@@ -56,21 +56,21 @@
 
     // A fake benchmark to give us a baseline.
     public boolean timeIsSpace(int reps) {
-        boolean dummy = false;
+        boolean fake = false;
         if (overload == Overload.CHAR) {
             for (int i = 0; i < reps; ++i) {
                 for (int ch = 0; ch < 65536; ++ch) {
-                    dummy ^= ((char) ch == ' ');
+                    fake ^= ((char) ch == ' ');
                 }
             }
         } else {
             for (int i = 0; i < reps; ++i) {
                 for (int ch = 0; ch < 65536; ++ch) {
-                    dummy ^= (ch == ' ');
+                    fake ^= (ch == ' ');
                 }
             }
         }
-        return dummy;
+        return fake;
     }
 
     public void timeDigit(int reps) {
diff --git a/benchmarks/src/benchmarks/regression/LoopingBackwardsBenchmark.java b/benchmarks/src/benchmarks/regression/LoopingBackwardsBenchmark.java
index f0a474c..7456af2 100644
--- a/benchmarks/src/benchmarks/regression/LoopingBackwardsBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/LoopingBackwardsBenchmark.java
@@ -27,22 +27,22 @@
   @Param({"2", "20", "2000", "20000000"}) int max;
 
   public int timeForwards(int reps) {
-    int dummy = 0;
+    int fake = 0;
     for (int i = 0; i < reps; i++) {
       for (int j = 0; j < max; j++) {
-        dummy += j;
+        fake += j;
       }
     }
-    return dummy;
+    return fake;
   }
 
   public int timeBackwards(int reps) {
-    int dummy = 0;
+    int fake = 0;
     for (int i = 0; i < reps; i++) {
       for (int j = max - 1; j >= 0; j--) {
-        dummy += j;
+        fake += j;
       }
     }
-    return dummy;
+    return fake;
   }
 }
diff --git a/benchmarks/src/benchmarks/regression/NativeMethodBenchmark.java b/benchmarks/src/benchmarks/regression/NativeMethodBenchmark.java
deleted file mode 100644
index dbb6308..0000000
--- a/benchmarks/src/benchmarks/regression/NativeMethodBenchmark.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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 benchmarks.regression;
-
-import org.apache.harmony.dalvik.NativeTestTarget;
-
-public class NativeMethodBenchmark {
-    public void time_emptyJniStaticSynchronizedMethod0(int reps) throws Exception {
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticSynchronizedMethod0();
-        }
-    }
-
-    public void time_emptyJniSynchronizedMethod0(int reps) throws Exception {
-        NativeTestTarget n = new NativeTestTarget();
-        for (int i = 0; i < reps; ++i) {
-            n.emptyJniSynchronizedMethod0();
-        }
-    }
-
-
-    public void time_emptyJniMethod0(int reps) throws Exception {
-        NativeTestTarget n = new NativeTestTarget();
-        for (int i = 0; i < reps; ++i) {
-            n.emptyJniMethod0();
-        }
-    }
-
-    public void time_emptyJniMethod6(int reps) throws Exception {
-        int a = -1;
-        int b = 0;
-        NativeTestTarget n = new NativeTestTarget();
-        for (int i = 0; i < reps; ++i) {
-            n.emptyJniMethod6(a, b, 1, 2, 3, i);
-        }
-    }
-
-    public void time_emptyJniMethod6L(int reps) throws Exception {
-        NativeTestTarget n = new NativeTestTarget();
-        for (int i = 0; i < reps; ++i) {
-            n.emptyJniMethod6L(null, null, null, null, null, null);
-        }
-    }
-
-    public void time_emptyJniStaticMethod6L(int reps) throws Exception {
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod6L(null, null, null, null, null, null);
-        }
-    }
-    public void time_emptyJniStaticMethod0(int reps) throws Exception {
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod0();
-        }
-    }
-
-    public void time_emptyJniStaticMethod6(int reps) throws Exception {
-        int a = -1;
-        int b = 0;
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod6(a, b, 1, 2, 3, i);
-        }
-    }
-
-    public void time_emptyJniMethod0_Fast(int reps) throws Exception {
-        NativeTestTarget n = new NativeTestTarget();
-        for (int i = 0; i < reps; ++i) {
-            n.emptyJniMethod0_Fast();
-        }
-    }
-
-    public void time_emptyJniMethod6_Fast(int reps) throws Exception {
-        int a = -1;
-        int b = 0;
-        NativeTestTarget n = new NativeTestTarget();
-        for (int i = 0; i < reps; ++i) {
-            n.emptyJniMethod6_Fast(a, b, 1, 2, 3, i);
-        }
-    }
-
-    public void time_emptyJniMethod6L_Fast(int reps) throws Exception {
-        NativeTestTarget n = new NativeTestTarget();
-        for (int i = 0; i < reps; ++i) {
-            n.emptyJniMethod6L_Fast(null, null, null, null, null, null);
-        }
-    }
-
-    public void time_emptyJniStaticMethod6L_Fast(int reps) throws Exception {
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod6L_Fast(null, null, null, null, null, null);
-        }
-    }
-    public void time_emptyJniStaticMethod0_Fast(int reps) throws Exception {
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod0_Fast();
-        }
-    }
-
-    public void time_emptyJniStaticMethod6_Fast(int reps) throws Exception {
-        int a = -1;
-        int b = 0;
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod6_Fast(a, b, 1, 2, 3, i);
-        }
-    }
-
-    public void time_emptyJniStaticMethod0_Critical(int reps) throws Exception {
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod0_Critical();
-        }
-    }
-
-    public void time_emptyJniStaticMethod6_Critical(int reps) throws Exception {
-        int a = -1;
-        int b = 0;
-        for (int i = 0; i < reps; ++i) {
-            NativeTestTarget.emptyJniStaticMethod6_Critical(a, b, 1, 2, 3, i);
-        }
-    }
-}
diff --git a/benchmarks/src/benchmarks/regression/PriorityQueueBenchmark.java b/benchmarks/src/benchmarks/regression/PriorityQueueBenchmark.java
index a5b02b4..5b80785 100644
--- a/benchmarks/src/benchmarks/regression/PriorityQueueBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/PriorityQueueBenchmark.java
@@ -67,7 +67,7 @@
     }
 
     public boolean timeRemove(int reps) {
-        boolean dummy = false;
+        boolean fake = false;
         int elementsSize = seekElements.size();
         // At most allow the queue to empty 10%.
         int resizingThreshold = queueSize / 10;
@@ -78,8 +78,8 @@
             if (i % resizingThreshold == 0) {
                 usepq = new PriorityQueue<Integer>(pq);
             }
-            dummy = usepq.remove(seekElements.get(i % elementsSize));
+            fake = usepq.remove(seekElements.get(i % elementsSize));
         }
-        return dummy;
+        return fake;
     }
 }
diff --git a/benchmarks/src/benchmarks/regression/StringToBytesBenchmark.java b/benchmarks/src/benchmarks/regression/StringToBytesBenchmark.java
index 8b22224..f43b32e 100644
--- a/benchmarks/src/benchmarks/regression/StringToBytesBenchmark.java
+++ b/benchmarks/src/benchmarks/regression/StringToBytesBenchmark.java
@@ -26,7 +26,11 @@
         L_16(makeString(16)),
         L_64(makeString(64)),
         L_256(makeString(256)),
-        L_512(makeString(512));
+        L_512(makeString(512)),
+        A_16(makeAsciiString(16)),
+        A_64(makeAsciiString(64)),
+        A_256(makeAsciiString(256)),
+        A_512(makeAsciiString(512));
 
         private final String value;
 
@@ -43,6 +47,14 @@
         return new String(chars);
     }
 
+    private static final String makeAsciiString(int length) {
+        char[] chars = new char[length];
+        for (int i = 0; i < length; ++i) {
+            chars[i] = ((i & 0x7f) != 0) ? (char) (i & 0x7f) : '?';
+        }
+        return new String(chars);
+    }
+
     @Param StringLengths string;
 
     public void timeGetBytesUtf8(int nreps) {
diff --git a/dalvik/src/main/java/dalvik/annotation/codegen/CovariantReturnType.java b/dalvik/src/main/java/dalvik/annotation/codegen/CovariantReturnType.java
index b43600d..95a87af 100644
--- a/dalvik/src/main/java/dalvik/annotation/codegen/CovariantReturnType.java
+++ b/dalvik/src/main/java/dalvik/annotation/codegen/CovariantReturnType.java
@@ -16,12 +16,15 @@
 
 package dalvik.annotation.codegen;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Repeatable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import libcore.api.CorePlatformApi;
 
 /**
  * Indicates to the platform toolchain that there is an upcoming public SDK API change for a method.
@@ -61,29 +64,39 @@
 @Repeatable(CovariantReturnType.CovariantReturnTypes.class)
 @Retention(RetentionPolicy.CLASS)
 @Target({ ElementType.METHOD})
-@CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public @interface CovariantReturnType {
 
     /**
      * The return type of the synthetic method to generate. Must be a subclass of the return type
      * of the method being annotated.
+     *
+     * @hide
      */
-    @CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     Class<?> returnType();
 
     /**
      * The last Android API level not to have the generated synthetic method. The annotation can be
      * removed and the actual return type updated when support for this API level is dropped.
+     *
+     * @hide
      */
-    @CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     int presentAfter();
 
     /** @hide */
     @Retention(RetentionPolicy.CLASS)
     @Target({ElementType.METHOD})
-    @CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @interface CovariantReturnTypes {
-        @CorePlatformApi
+        /** @hide */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         CovariantReturnType[] value();
     }
 }
diff --git a/dalvik/src/main/java/dalvik/annotation/compat/VersionCodes.java b/dalvik/src/main/java/dalvik/annotation/compat/VersionCodes.java
index 8f7bcd2..9d4d3fa 100644
--- a/dalvik/src/main/java/dalvik/annotation/compat/VersionCodes.java
+++ b/dalvik/src/main/java/dalvik/annotation/compat/VersionCodes.java
@@ -15,7 +15,6 @@
  */
 package dalvik.annotation.compat;
 
-import libcore.api.CorePlatformApi;
 import libcore.api.IntraCoreApi;
 
 /**
@@ -30,7 +29,6 @@
  *
  * {@hide}
  */
-@CorePlatformApi
 @IntraCoreApi
 public class VersionCodes {
 
@@ -38,23 +36,39 @@
     }
 
     /**
+     * The version code for current development build, which has not yet turned into an official
+     * release.
+     */
+    @IntraCoreApi
+    public static final int CUR_DEVELOPMENT = 10000;
+
+    /**
      * The version code for Android Oreo (API version 26).
      */
-    @CorePlatformApi
     @IntraCoreApi
     public static final int O = 26;
 
     /**
      * The version code for Android Pie (API version 28).
      */
-    @CorePlatformApi
     @IntraCoreApi
     public static final int P = 28;
 
     /**
      * The version code for Android Q (API version 29).
      */
-    @CorePlatformApi
     @IntraCoreApi
     public static final int Q = 29;
+
+    /**
+     * The version code for Android R (API version 30).
+     */
+    @IntraCoreApi
+    public static final int R = 30;
+
+    /**
+     * The version code for Android S (API version 31).
+     */
+    @IntraCoreApi
+    public static final int S = 31;
 }
diff --git a/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java b/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java
index c64aa5f..0dac897 100644
--- a/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java
+++ b/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java
@@ -16,6 +16,10 @@
 
 package dalvik.annotation.optimization;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -93,7 +97,8 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = MODULE_LIBRARIES)
 @Retention(RetentionPolicy.CLASS)  // Save memory, don't instantiate as an object at runtime.
 @Target(ElementType.METHOD)
 public @interface CriticalNative {}
diff --git a/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java
index f25233c..db3efe0 100644
--- a/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java
+++ b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java
@@ -16,6 +16,10 @@
 
 package dalvik.annotation.optimization;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -72,7 +76,9 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.IntraCoreApi
 @Retention(RetentionPolicy.CLASS)  // Save memory, don't instantiate as an object at runtime.
 @Target(ElementType.METHOD)
 public @interface FastNative {}
diff --git a/dalvik/src/main/java/dalvik/system/AppSpecializationHooks.java b/dalvik/src/main/java/dalvik/system/AppSpecializationHooks.java
new file mode 100644
index 0000000..e311902
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/system/AppSpecializationHooks.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 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 dalvik.system;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
+/**
+ * Used by frameworks to specialize libcore in an app process.
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+public class AppSpecializationHooks {
+
+    private AppSpecializationHooks() {}
+
+    /**
+     * Called in {@link android.app.ActivityThread}, but before binding the application. This method
+     * should be called only after {@link android.compat.Compatibility#setCallbacks}
+     * has been invoked to handle the app compat queries from
+     * {@link android.compat.Compatibility#isChangeEnabled(long)}.
+     *
+     * This is a good place to change system properties / clear cache due to an app compat change
+     * before an app starts.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void handleCompatChangesBeforeBindingApplication() {
+        com.android.i18n.system.AppSpecializationHooks
+                .handleCompatChangesBeforeBindingApplication();
+    }
+}
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index ee20d80..60511a9 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -16,6 +16,9 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import java.io.File;
 import java.io.IOException;
@@ -29,6 +32,8 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
 import sun.misc.CompoundEnumeration;
 
 /**
@@ -128,6 +133,9 @@
                 : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
         this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
 
+        // Run background verification after having set 'pathList'.
+        this.pathList.maybeRunBackgroundVerification(this);
+
         reportClassLoaderChain();
     }
 
@@ -136,7 +144,8 @@
      *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public void reportClassLoaderChain() {
         if (reporter == null) {
             return;
@@ -186,6 +195,8 @@
         this.sharedLibraryLoaders = null;
         this.pathList = new DexPathList(this, librarySearchPath);
         this.pathList.initByteBufferDexPath(dexFiles);
+        // Run background verification after having set 'pathList'.
+        this.pathList.maybeRunBackgroundVerification(this);
     }
 
     @Override
@@ -215,11 +226,16 @@
     }
 
     /**
+     * Adds a new dex path to path list.
+     *
+     * @param dexPath dex path to add to path list
+     *
      * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public void addDexPath(String dexPath) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public void addDexPath(@Nullable String dexPath) {
         addDexPath(dexPath, false /*isTrusted*/);
     }
 
@@ -233,11 +249,15 @@
 
     /**
      * Adds additional native paths for consideration in subsequent calls to
-     * {@link #findLibrary(String)}
+     * {@link #findLibrary(String)}.
+     *
+     * @param libPaths collection of paths to be added to path list
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public void addNativePath(Collection<String> libPaths) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public void addNativePath(@NonNull Collection<String> libPaths) {
         pathList.addNativePath(libPaths);
     }
 
@@ -289,7 +309,7 @@
      * provide it, in order to make all those hopeful callers of
      * {@code myClass.getPackage().getName()} happy. Thus we construct
      * a {@code Package} object the first time it is being requested
-     * and fill most of the fields with dummy values. The {@code
+     * and fill most of the fields with fake values. The {@code
      * Package} object is then put into the {@code ClassLoader}'s
      * package cache, so we see the same one next time. We don't
      * create {@code Package} objects for {@code null} arguments or
@@ -324,11 +344,17 @@
     }
 
     /**
+     * Returns colon-separated set of directories where libraries should be
+     * searched for first, before the standard set of directories.
+     *
+     * @return colon-separated set of search directories
+     *
      * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public String getLdLibraryPath() {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public @NonNull String getLdLibraryPath() {
         StringBuilder result = new StringBuilder();
         for (File directory : pathList.getNativeLibraryDirectories()) {
             if (result.length() > 0) {
@@ -349,11 +375,12 @@
      * Once set, all new instances of BaseDexClassLoader will report upon
      * constructions the loaded dex files.
      *
-     * @param newReporter the new Reporter. Setting null will cancel reporting.
+     * @param newReporter the new Reporter. Setting {@code null} will cancel reporting.
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void setReporter(Reporter newReporter) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void setReporter(@Nullable Reporter newReporter) {
         reporter = newReporter;
     }
 
@@ -365,9 +392,13 @@
     }
 
     /**
+     * Reports the construction of a {@link BaseDexClassLoader} and provides opaque
+     * information about the class loader chain.
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public interface Reporter {
         /**
          * Reports the construction of a BaseDexClassLoader and provides opaque information about
@@ -378,8 +409,11 @@
          *
          * @param contextsMap A map from dex file paths to the class loader context used to load
          *     each dex file.
+         *
+         * @hide
          */
-        @libcore.api.CorePlatformApi
-        void report(Map<String, String> contextsMap);
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+        void report(@NonNull Map<String, String> contextsMap);
     }
 }
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java
index 192e450..3472f70 100644
--- a/dalvik/src/main/java/dalvik/system/BlockGuard.java
+++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java
@@ -16,6 +16,9 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.Objects;
@@ -34,7 +37,8 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.IntraCoreApi
 public final class BlockGuard {
 
@@ -46,24 +50,33 @@
      *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public interface Policy {
         /**
          * Called on disk writes.
+         *
+         * @hide
          */
-        @libcore.api.CorePlatformApi
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         void onWriteToDisk();
 
         /**
          * Called on disk reads.
+         *
+         * @hide
          */
         @UnsupportedAppUsage
-        @libcore.api.CorePlatformApi
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         void onReadFromDisk();
 
         /**
          * Called on network operations.
+         *
+         * @hide
          */
         @UnsupportedAppUsage
         @libcore.api.IntraCoreApi
@@ -71,22 +84,32 @@
 
         /**
          * Called on unbuffered input/ouput operations.
+         *
+         * @hide
          */
-        @libcore.api.CorePlatformApi
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         void onUnbufferedIO();
 
         /**
          * Called on explicit GC request, i.e. Runtime.gc().
+         *
+         * @hide
          */
         void onExplicitGc();
 
         /**
          * Returns the policy bitmask, for shipping over Binder calls
          * to remote threads/processes and reinstantiating the policy
-         * there.  The bits in the mask are from the DISALLOW_* and
-         * PENALTY_* constants.
+         * there. The bits in the mask are from the {@code DISALLOW_*} and
+         * {@code PENALTY_*} constants declared in {@code StrictMode} class.
+         *
+         * @return policy bitmask
+         *
+         * @hide
          */
-        @libcore.api.CorePlatformApi
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         int getPolicyMask();
     }
 
@@ -94,7 +117,8 @@
      * Per-process interface used to implement {@code StrictMode.VmPolicy}.
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public interface VmPolicy {
         /**
          * Called by core libraries code when the given path is accessed. This
@@ -114,13 +138,18 @@
          *
          * @param path The path in the local file system that is being accessed
          *            for reading or writing.
+         *
+         * @hide
          */
-        @libcore.api.CorePlatformApi
-        void onPathAccess(String path);
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+        void onPathAccess(@NonNull String path);
     }
 
     /**
-     * @deprecated no longer actively used, but kept intact for greylist.
+     * @deprecated no longer actively used, but kept intact for hidden API lists.
+     *
+     * @hide
      */
     @Deprecated
     public static class BlockGuardPolicyException extends RuntimeException {
@@ -166,9 +195,12 @@
 
     /**
      * The default, permissive per-thread policy.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final Policy LAX_POLICY = new Policy() {
         @Override public String toString() { return "LAX_POLICY"; }
         @Override public void onWriteToDisk() {}
@@ -185,8 +217,11 @@
 
     /**
      * The default, permissive per-process policy.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final VmPolicy LAX_VM_POLICY = new VmPolicy() {
         @Override public String toString() { return "LAX_VM_POLICY"; }
         @Override public void onPathAccess(String path) {}
@@ -206,9 +241,12 @@
      *
      * @return the current thread's policy. Will return the {@link #LAX_POLICY}
      *         instance if nothing else is set.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public static @NonNull Policy getThreadPolicy() {
         return threadPolicy.get();
@@ -222,9 +260,12 @@
      *
      * @param policy policy to set. Use the public {@link #LAX_POLICY} if you
      *            want to unset the active policy.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setThreadPolicy(@NonNull Policy policy) {
         threadPolicy.set(Objects.requireNonNull(policy));
     }
@@ -234,8 +275,11 @@
      *
      * @return the current process's policy. Will return the
      *         {@link #LAX_VM_POLICY} instance if nothing else is set.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static @NonNull VmPolicy getVmPolicy() {
         return vmPolicy;
     }
@@ -248,8 +292,11 @@
      *
      * @param policy policy to set. Use the public {@link #LAX_VM_POLICY} if you
      *            want to unset the active policy.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setVmPolicy(@NonNull VmPolicy policy) {
         vmPolicy = Objects.requireNonNull(policy);
     }
diff --git a/dalvik/src/main/java/dalvik/system/CloseGuard.java b/dalvik/src/main/java/dalvik/system/CloseGuard.java
index 99ddf3e..959a5a3 100644
--- a/dalvik/src/main/java/dalvik/system/CloseGuard.java
+++ b/dalvik/src/main/java/dalvik/system/CloseGuard.java
@@ -16,6 +16,9 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
@@ -110,7 +113,8 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.IntraCoreApi
 public final class CloseGuard {
 
@@ -140,9 +144,14 @@
     /**
      * Returns a CloseGuard instance. {@code #open(String)} can be used to set
      * up the instance to warn on failure to close.
+     *
+     * @return {@link CloseGuard} instance.
+     *
+     * @hide
      */
     @UnsupportedAppUsage(trackingBug=111170242)
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public static CloseGuard get() {
         return new CloseGuard();
@@ -154,15 +163,22 @@
      * if enabled is true. If a stack trace was captured, the {@link
      * #getReporter() reporter} is informed of unclosed resources; otherwise a
      * one-line warning is logged.
+     *
+     * @param enabled whether stack capture and tracking is enabled.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setEnabled(boolean enabled) {
         CloseGuard.stackAndTrackingEnabled = enabled;
     }
 
     /**
      * True if CloseGuard stack capture and tracking are enabled.
+     *
+     * @hide
      */
     public static boolean isEnabled() {
         return stackAndTrackingEnabled;
@@ -171,9 +187,14 @@
     /**
      * Used to replace default Reporter used to warn of CloseGuard
      * violations when stack tracking is enabled. Must be non-null.
+     *
+     * @param rep replacement for default Reporter.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setReporter(Reporter rep) {
         if (rep == null) {
             throw new NullPointerException("reporter == null");
@@ -183,8 +204,13 @@
 
     /**
      * Returns non-null CloseGuard.Reporter.
+     *
+     * @return CloseGuard's Reporter.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static Reporter getReporter() {
         return reporter;
     }
@@ -196,6 +222,8 @@
      *
      * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
      * MUST NOT be used for any other purposes.
+     *
+     * @hide
      */
     public static void setTracker(Tracker tracker) {
         currentTracker = tracker;
@@ -207,6 +235,8 @@
      *
      * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
      * MUST NOT be used for any other purposes.
+     *
+     * @hide
      */
     public static Tracker getTracker() {
         return currentTracker;
@@ -222,9 +252,12 @@
      *
      * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
      * @throws NullPointerException if closer is null.
+     *
+     * @hide
      */
     @UnsupportedAppUsage(trackingBug=111170242)
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public void open(String closer) {
         openWithCallSite(closer, null /* callsite */);
@@ -239,8 +272,11 @@
      *
      * @param closer Non-null name of explicit termination method. Printed by warnIfOpen.
      * @param callsite Non-null string uniquely identifying the callsite.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public void openWithCallSite(String closer, String callsite) {
         // always perform the check for valid API usage...
         if (closer == null) {
@@ -274,9 +310,12 @@
     /**
      * Marks this CloseGuard instance as closed to avoid warnings on
      * finalization.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public void close() {
         Tracker tracker = currentTracker;
@@ -293,9 +332,12 @@
      * when the CloseGuard was created, passes the stacktrace associated with
      * the allocation to the current reporter. If it was not enabled, it just
      * directly logs a brief message.
+     *
+     * @hide
      */
     @UnsupportedAppUsage(trackingBug=111170242)
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public void warnIfOpen() {
         if (closerNameOrAllocationInfo != null) {
@@ -316,6 +358,8 @@
      *
      * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
      * MUST NOT be used for any other purposes.
+     *
+     * @hide
      */
     public interface Tracker {
         void open(Throwable allocationSite);
@@ -326,13 +370,24 @@
      * Interface to allow customization of reporting behavior.
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public interface Reporter {
+        /**
+         *
+         * @hide
+         */
         @UnsupportedAppUsage
-        @libcore.api.CorePlatformApi
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         void report(String message, Throwable allocationSite);
 
-        @libcore.api.CorePlatformApi
+        /**
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         default void report(String message) {}
     }
 
diff --git a/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java b/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java
deleted file mode 100644
index 9d5eff1..0000000
--- a/dalvik/src/main/java/dalvik/system/DalvikLogHandler.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2009 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 dalvik.system;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * An optimized handler for efficient publishing of basic log messages.
- * Implementers should also be subclasses of {@link java.util.logging.Handler}.
- *
- * <p>Unlike the default log handler, this API doesn't require intermediate
- * objects to be allocated for log handling. It also includes a short tag, which
- * may otherwise need to be calculated for each published message.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public interface DalvikLogHandler {
-
-    /**
-     * Publishes a log message. Unlike {@link
-     * java.util.logging.Handler#publish(java.util.logging.LogRecord)}, this
-     * method includes only the raw log message. Log messages that were created
-     * with additional fields (parameters, source methods, etc.) will flow
-     * through the conventional channels instead.
-     *
-     * @param tag the short (23 characters or fewer) logger tag identifying
-     *      {@code logger}.
-     */
-    @libcore.api.CorePlatformApi
-    void publish(Logger source, String tag, Level level, String message);
-
-    // TODO: support messages with throwables?
-}
diff --git a/dalvik/src/main/java/dalvik/system/DalvikLogging.java b/dalvik/src/main/java/dalvik/system/DalvikLogging.java
deleted file mode 100644
index 5c529a6..0000000
--- a/dalvik/src/main/java/dalvik/system/DalvikLogging.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2009 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 dalvik.system;
-
-/**
- * Utility methods for logging to {@code DalvikHandlers}.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class DalvikLogging {
-    private DalvikLogging() {}
-
-    /**
-     * Returns the short logger tag (up to 23 chars) for the given logger name.
-     * Traditionally loggers are named by fully-qualified Java classes; this
-     * method attempts to return a concise identifying part of such names.
-     */
-    @libcore.api.CorePlatformApi
-    public static String loggerNameToTag(String loggerName) {
-        // Anonymous logger.
-        if (loggerName == null) {
-            return "null";
-        }
-
-        int length = loggerName.length();
-        if (length <= 23) {
-            return loggerName;
-        }
-
-        int lastPeriod = loggerName.lastIndexOf(".");
-        return length - (lastPeriod + 1) <= 23
-                ? loggerName.substring(lastPeriod + 1)
-                : loggerName.substring(loggerName.length() - 23);
-    }
-}
diff --git a/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java b/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java
index 9481e3f..5966139 100644
--- a/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java
@@ -16,6 +16,10 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import sun.misc.CompoundEnumeration;
 
 import java.io.IOException;
@@ -104,9 +108,32 @@
     }
 
     /**
+     * Creates a {@code DelegateLastClassLoader} that operates on a given {@code dexPath}
+     * and a {@code librarySearchPath}.
+     *
+     * The {@code dexPath} should consist of one or more of the following, separated by
+     * {@code File.pathSeparator}, which is {@code ":"} on Android.
+     *
+     * <ul>
+     * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as well as arbitrary
+     * resources.
+     * <li>Raw ".dex" files (not inside a zip file).
+     * </ul>
+     *
+     * @param dexPath the list of jar/apk files containing classes and resources, delimited by
+     *                {@code File.pathSeparator}, which defaults to {@code ":"} on Android.
+     * @param librarySearchPath the list of directories containing native libraries, delimited
+     *                          by {@code File.pathSeparator}; may be {@code null}.
+     * @param parent the parent class loader. May be {@code null} for the boot classloader.
+     * @param sharedLibraryLoaders class loaders of Java shared libraries
+     *                             used by this new class loader. The shared library loaders are
+     *                             always checked before the {@code dexPath} when looking
+     *                             up classes and resources.
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
     public DelegateLastClassLoader(
             String dexPath, String librarySearchPath, ClassLoader parent,
             ClassLoader[] sharedLibraryLoaders) {
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 176fde4..f8e7f42 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -16,6 +16,9 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.system.ErrnoException;
 
@@ -27,6 +30,8 @@
 import java.util.Enumeration;
 import java.util.List;
 import libcore.io.Libcore;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
 
 import dalvik.annotation.optimization.ReachabilitySensitive;
 
@@ -40,7 +45,6 @@
  *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
  *     in a future Android release</b>.
  */
-@libcore.api.CorePlatformApi
 @Deprecated
 public final class DexFile {
   /**
@@ -398,15 +402,11 @@
      * DexPathList to have been initialized for its classes to be resolvable by ART.
      * DexPathList will open the dex files first, finalize `dexElements` and then call this.
      */
-    /*package*/ void verifyInBackground(ClassLoader classLoader, String classLoaderContext) {
-        verifyInBackgroundNative(mCookie, classLoader, classLoaderContext);
+    /*package*/ void verifyInBackground(ClassLoader classLoader) {
+        verifyInBackgroundNative(mCookie, classLoader);
     }
 
-    private static native void verifyInBackgroundNative(Object mCookie, ClassLoader classLoader,
-            String classLoaderContext);
-
-    /*package*/ static native String getClassLoaderContext(ClassLoader classLoader,
-            DexPathList.Element[] elements);
+    private static native void verifyInBackgroundNative(Object mCookie, ClassLoader classLoader);
 
     /*
      * Returns true if the dex file is backed by a valid oat file.
@@ -464,7 +464,8 @@
      *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int NO_DEXOPT_NEEDED = 0;
 
     /**
@@ -494,7 +495,8 @@
      *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int DEX2OAT_FOR_FILTER = 3;
 
 
@@ -519,6 +521,7 @@
      * is used to indicate whether profile information has changed recently.
      *
      * @param fileName the absolute path to the apk/jar file to examine.
+     * @param instructionSet instruction set to examine
      * @param compilerFilter a compiler filter to use for what a caller considers up-to-date.
      * @param classLoaderContext a string encoding the class loader context the dex file
      *        is intended to have at runtime.
@@ -536,13 +539,14 @@
      *         not a file, or not present.
      * @throws java.io.IOException if fileName is not a valid apk/jar file or
      *         if problems occur while parsing it.
-     * @throws java.lang.NullPointerException if fileName is null.
+     * @throws java.lang.NullPointerException if {@code fileName} is {@code null}.
      *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static native int getDexOptNeeded(String fileName,
-            String instructionSet, String compilerFilter, String classLoaderContext,
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static native int getDexOptNeeded(@NonNull String fileName,
+            @NonNull String instructionSet, @NonNull String compilerFilter, @Nullable String classLoaderContext,
             boolean newProfile, boolean downgrade)
             throws FileNotFoundException, IOException;
 
@@ -565,9 +569,10 @@
      *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final class OptimizationInfo {
-        // The optimization status.
+        // The human readable refined optimization status of the validity of the odex file.
         private final String status;
         // The optimization reason. The reason might be "unknown" if the
         // the compiler artifacts were not annotated during optimizations.
@@ -578,13 +583,29 @@
             this.reason = reason;
         }
 
-        @libcore.api.CorePlatformApi
-        public String getStatus() {
+        /**
+         * Returns the human readable refined status of the validity of the odex file.
+         *
+         * @return optimization status
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+        public @NonNull String getStatus() {
             return status;
         }
 
-        @libcore.api.CorePlatformApi
-        public String getReason() {
+        /**
+         * Returns the reason of a particular optimization used.
+         *
+         * @return optimization reason
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+        public @NonNull String getReason() {
             return reason;
         }
     }
@@ -592,11 +613,17 @@
     /**
      * Retrieves the optimization info for a dex file.
      *
+     * @param fileName       path to dex file
+     * @param instructionSet instruction set to get optimization info for
+     * @return {@link OptimizationInfo} for {@code fileName} dex file
+     * @throws FileNotFoundException if {@code fileName} not found
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static OptimizationInfo getDexFileOptimizationInfo(
-            String fileName, String instructionSet) throws FileNotFoundException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull OptimizationInfo getDexFileOptimizationInfo(
+            @NonNull String fileName, @NonNull String instructionSet) throws FileNotFoundException {
         String[] status = getDexFileOptimizationStatus(fileName, instructionSet);
         return new OptimizationInfo(status[0], status[1]);
     }
@@ -618,34 +645,52 @@
 
     /**
      * Returns the paths of the optimized files generated for {@code fileName}.
-     * If no optimized code exists the method returns null.
+     * If no optimized code exists the method returns {@code null}.
+     *
+     * @param fileName       path to dex file
+     * @param instructionSet instruction set to get optimized files for
+     * @return paths to optimized code, or {@code null} if they do not exist
+     * @throws FileNotFoundException
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static native String[] getDexFileOutputPaths(String fileName, String instructionSet)
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static native @Nullable String[] getDexFileOutputPaths(@NonNull String fileName, @NonNull String instructionSet)
         throws FileNotFoundException;
 
     /**
      * Returns whether the given filter is a valid filter.
      *
+     * @param filter filter string
+     * @return whether given filter string is a valid filter
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public native static boolean isValidCompilerFilter(String filter);
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public native static boolean isValidCompilerFilter(@NonNull String filter);
 
     /**
      * Returns whether the given filter is based on profiles.
      *
+     * @param filter filter string
+     * @return whether given filter string is based on profiles
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public native static boolean isProfileGuidedCompilerFilter(String filter);
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public native static boolean isProfileGuidedCompilerFilter(@NonNull String filter);
 
     /**
      * Returns the version of the compiler filter that is not based on profiles.
      * If the input is not a valid filter, or the filter is already not based on
      * profiles, this returns the input.
      *
+     * @param filter filter string
+     * @return version of the compiler filter that is not based on profiles
+     *
      * @hide
      */
     public native static String getNonProfileGuidedCompilerFilter(String filter);
@@ -655,10 +700,14 @@
      * If the input is not a valid filter, or the filter is already suitable for
      * safe mode, this returns the input.
      *
+     * @param filter filter string
+     * @return version of the compiler filter that is suitable for safe mode
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public native static String getSafeModeCompilerFilter(String filter);
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public native static @NonNull String getSafeModeCompilerFilter(@NonNull String filter);
 
     /**
      * Returns the static file size of the original dex file.
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 798166c..acf5e66 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -262,23 +262,7 @@
         try {
             Element[] null_elements = null;
             DexFile dex = new DexFile(dexFiles, definingContext, null_elements);
-            // Capture class loader context from *before* `dexElements` is set (see comment below).
-            String classLoaderContext = dex.isBackedByOatFile()
-                    ? null : DexFile.getClassLoaderContext(definingContext, null_elements);
             dexElements = new Element[] { new Element(dex) };
-            // Spawn background thread to verify all classes and cache verification results.
-            // Must be called *after* `dexElements` has been initialized for ART to find
-            // its classes (the field is hardcoded in ART and dex files iterated over in
-            // the order of the array), but with class loader context from *before*
-            // `dexElements` was set because that is what it will be compared against next
-            // time the same bytecode is loaded.
-            // We only spawn the background thread if the bytecode is not backed by an oat
-            // file, i.e. this is the first time this bytecode is being loaded and/or
-            // verification results have not been cached yet. Skip spawning the thread on
-            // all subsequent loads of the same bytecode in the same class loader context.
-            if (classLoaderContext != null) {
-                dex.verifyInBackground(definingContext, classLoaderContext);
-            }
         } catch (IOException suppressed) {
             System.logE("Unable to load dex files", suppressed);
             suppressedExceptions.add(suppressed);
@@ -291,6 +275,21 @@
         }
     }
 
+    /* package */ void maybeRunBackgroundVerification(ClassLoader loader) {
+        // Spawn background thread to verify all classes and cache verification results.
+        // Must be called *after* `this.dexElements` has been initialized and `loader.pathList`
+        // has been set for ART to find its classes (the fields are hardcoded in ART and dex
+        // files iterated over in the order of the array).
+        // We only spawn the background thread if the bytecode is not backed by an oat
+        // file, i.e. this is the first time this bytecode is being loaded and/or
+        // verification results have not been cached yet.
+        for (Element element : dexElements) {
+            if (element.dexFile != null && !element.dexFile.isBackedByOatFile()) {
+                element.dexFile.verifyInBackground(loader);
+            }
+        }
+    }
+
     /**
      * Splits the given dex path string into elements using the path
      * separator, pruning out any elements that do not refer to existing
diff --git a/dalvik/src/main/java/dalvik/system/NativeStart.java b/dalvik/src/main/java/dalvik/system/NativeStart.java
index 1382823..ba2661e 100644
--- a/dalvik/src/main/java/dalvik/system/NativeStart.java
+++ b/dalvik/src/main/java/dalvik/system/NativeStart.java
@@ -17,7 +17,7 @@
 package dalvik.system;
 
 /**
- * Dummy class used during JNI initialization.  The JNI functions want
+ * Fake class used during JNI initialization.  The JNI functions want
  * to be able to create objects, and the VM needs to discard the references
  * when the function returns.  That gets a little weird when we're
  * calling JNI functions from the C main(), and there's no Java stack frame
@@ -33,7 +33,7 @@
 class NativeStart {
     private NativeStart() {}
 
-    private static native void main(String[] dummy);
+    private static native void main(String[] fake);
 
     private static native void run();
 }
diff --git a/dalvik/src/main/java/dalvik/system/PathClassLoader.java b/dalvik/src/main/java/dalvik/system/PathClassLoader.java
index cbd494f..1b74cd9 100644
--- a/dalvik/src/main/java/dalvik/system/PathClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/PathClassLoader.java
@@ -16,6 +16,13 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
 /**
  * Provides a simple {@link ClassLoader} implementation that operates on a list
  * of files and directories in the local file system, but does not attempt to
@@ -65,12 +72,38 @@
     }
 
     /**
+     * Creates a {@code PathClassLoader} that operates on two given
+     * lists of files and directories. The entries of the first list
+     * should be one of the following:
+     *
+     * <ul>
+     * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as
+     * well as arbitrary resources.
+     * <li>Raw ".dex" files (not inside a zip file).
+     * </ul>
+     *
+     * The entries of the second list should be directories containing
+     * native library files.
+     *
+     * @param dexPath the list of jar/apk files containing classes and
+     * resources, delimited by {@code File.pathSeparator}, which
+     * defaults to {@code ":"} on Android
+     * @param librarySearchPath the list of directories containing native
+     * libraries, delimited by {@code File.pathSeparator}; may be
+     * {@code null}
+     * @param parent the parent class loader
+     * @param sharedLibraryLoaders class loaders of Java shared libraries
+     * used by this new class loader. The shared library loaders are always
+     * checked before the {@code dexPath} when looking
+     * up classes and resources.
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public PathClassLoader(
-            String dexPath, String librarySearchPath, ClassLoader parent,
-            ClassLoader[] sharedLibraryLoaders) {
+            @NonNull String dexPath, @Nullable String librarySearchPath, @Nullable ClassLoader parent,
+            @Nullable ClassLoader[] sharedLibraryLoaders) {
         super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
     }
 }
diff --git a/dalvik/src/main/java/dalvik/system/RuntimeHooks.java b/dalvik/src/main/java/dalvik/system/RuntimeHooks.java
index f1af867..d86998b 100644
--- a/dalvik/src/main/java/dalvik/system/RuntimeHooks.java
+++ b/dalvik/src/main/java/dalvik/system/RuntimeHooks.java
@@ -16,12 +16,13 @@
 
 package dalvik.system;
 
-import dalvik.system.ThreadPrioritySetter;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 
 import java.util.Objects;
 import java.util.TimeZone;
 import java.util.function.Supplier;
-
 import libcore.util.NonNull;
 import libcore.util.Nullable;
 
@@ -35,15 +36,12 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class RuntimeHooks {
 
     private static Supplier<String> zoneIdSupplier;
 
-    // BEGIN Android-added: Customize behavior of Thread.setPriority(). http://b/139521784
-    private static volatile ThreadPrioritySetter threadPrioritySetter;
-    // END Android-added: Customize behavior of Thread.setPriority(). http://b/139521784
-
     private RuntimeHooks() {
         // No need to construct an instance. All methods are static.
     }
@@ -54,12 +52,17 @@
      *
      * <p>This method also clears the current {@link TimeZone} default ensuring that the supplier
      * will be used next time {@link TimeZone#getDefault()} is called (unless
-     * {@link TimeZone#setDefault(TimeZone)} is called with a non-null value in the interim).
+     * {@link TimeZone#setDefault(TimeZone)} is called with a non-{@code null} value in the interim).
      *
      * <p>Once set the supplier cannot be changed.
+     *
+     * @param zoneIdSupplier new {@link Supplier} of the time zone ID
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void setTimeZoneIdSupplier(Supplier<String> zoneIdSupplier) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void setTimeZoneIdSupplier(@NonNull Supplier<String> zoneIdSupplier) {
         if (RuntimeHooks.zoneIdSupplier != null) {
             throw new UnsupportedOperationException("zoneIdSupplier instance already set");
         }
@@ -69,6 +72,8 @@
 
     /**
      * Returns the {@link Supplier} that should be used to discover the time zone.
+     *
+     * @hide
      */
     public static Supplier<String> getTimeZoneIdSupplier() {
         return RuntimeHooks.zoneIdSupplier;
@@ -80,33 +85,15 @@
      * handlers to run, this handler should never terminate this process. Any
      * throwables thrown by the handler will be ignored by
      * {@link Thread#dispatchUncaughtException(Throwable)}.
+     *
+     * @param uncaughtExceptionHandler handler for uncaught exceptions
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setUncaughtExceptionPreHandler(
-            Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
+            @Nullable Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
         Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
     }
-
-    // BEGIN Android-added: Customize behavior of Thread.setPriority(). http://b/139521784
-    /**
-     * Sets a {@link ThreadPrioritySetter} that will be invoked instead of
-     * the default implementation during {@link Thread.setPriority(int)}.
-     * @hide
-     */
-    @libcore.api.CorePlatformApi
-    public static void setThreadPrioritySetter(@NonNull ThreadPrioritySetter threadPrioritySetter) {
-        RuntimeHooks.threadPrioritySetter = Objects.requireNonNull(threadPrioritySetter);
-    }
-
-    /**
-     * Returns the last {@code ThreadPrioritySetter} that has been
-     * {@code #setThreadPrioritySetter(ThreadPrioritySetter) set}, or
-     * null if the setter has not yet been called.
-     * @hide
-     */
-    @libcore.api.CorePlatformApi
-    public static @Nullable ThreadPrioritySetter getThreadPrioritySetter() {
-        return threadPrioritySetter;
-    }
-    // END Android-added: Customize behavior of Thread.setPriority(). http://b/139521784
 }
diff --git a/dalvik/src/main/java/dalvik/system/SocketTagger.java b/dalvik/src/main/java/dalvik/system/SocketTagger.java
index 0493daa..62803d9 100644
--- a/dalvik/src/main/java/dalvik/system/SocketTagger.java
+++ b/dalvik/src/main/java/dalvik/system/SocketTagger.java
@@ -16,6 +16,9 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import java.io.FileDescriptor;
 import java.net.DatagramSocket;
@@ -27,7 +30,8 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public abstract class SocketTagger {
 
     private static SocketTagger tagger = new SocketTagger() {
@@ -35,16 +39,27 @@
         @Override public void untag(FileDescriptor socketDescriptor) throws SocketException {}
     };
 
-    @libcore.api.CorePlatformApi
+    /**
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public SocketTagger() {
     }
 
     /**
-     * Notified when {@code socketDescriptor} is either assigned to the current
+     * Notified when {@code socketDescriptor} is assigned to the current
      * thread. The socket is either newly connected or reused from a connection
      * pool. Implementations of this method should be thread-safe.
+     *
+     * @param socketDescriptor to be assigned to the current thread
+     * @throws SocketException when {@link SocketException} occurs
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public abstract void tag(FileDescriptor socketDescriptor) throws SocketException;
 
     /**
@@ -54,34 +69,92 @@
      *
      * <p><strong>Note:</strong> this method will not be invoked when the socket
      * is closed.
+     *
+     * @param socketDescriptor to be released from the current thread to a connection pool
+     * @throws SocketException when {@link SocketException} occurs
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public abstract void untag(FileDescriptor socketDescriptor) throws SocketException;
 
+    /**
+     * Notified when {@code socket} is assigned to the current
+     * thread. The socket is either newly connected or reused from a connection
+     * pool. Implementations of this method should be thread-safe.
+     *
+     * @param socket to be assigned to the current thread
+     * @throws SocketException when {@link SocketException} occurs
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final void tag(Socket socket) throws SocketException {
         if (!socket.isClosed()) {
             tag(socket.getFileDescriptor$());
         }
     }
 
+    /**
+     * Notified when {@code socket} is released from the current
+     * thread to a connection pool. Implementations of this method should be
+     * thread-safe.
+     *
+     * <p><strong>Note:</strong> this method will not be invoked when the socket
+     * is closed.
+     *
+     * @param socket           to be released from the current thread
+     *                         to a connection pool
+     * @throws SocketException when {@link SocketException} occurs
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final void untag(Socket socket) throws SocketException {
         if (!socket.isClosed()) {
             untag(socket.getFileDescriptor$());
         }
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Notified when {@code socket} is assigned to the current thread.
+     * The socket is either newly connected or reused from a connection
+     * pool. Implementations of this method should be thread-safe.
+     *
+     * @param socket           to be assigned to the current thread
+     * @throws SocketException when {@link SocketException} occurs
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final void tag(DatagramSocket socket) throws SocketException {
         if (!socket.isClosed()) {
             tag(socket.getFileDescriptor$());
         }
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Notified when {@code socket} is released from the current
+     * thread to a connection pool. Implementations of this method should be
+     * thread-safe.
+     *
+     * <p><strong>Note:</strong> this method will not be invoked when the socket
+     * is closed.
+     *
+     * @param socket           to be released from the current thread
+     *                         to a connection pool
+     * @throws SocketException when {@link SocketException} occurs
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final void untag(DatagramSocket socket) throws SocketException {
         if (!socket.isClosed()) {
             untag(socket.getFileDescriptor$());
@@ -90,8 +163,13 @@
 
     /**
      * Sets this process' socket tagger to {@code tagger}.
+     *
+     * @param tagger socket tagger to be assigned to this process
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static synchronized void set(SocketTagger tagger) {
         if (tagger == null) {
             throw new NullPointerException("tagger == null");
@@ -101,9 +179,14 @@
 
     /**
      * Returns this process socket tagger.
+     *
+     * @return {@link SocketTagger} assigned to this process
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static synchronized SocketTagger get() {
         return tagger;
     }
diff --git a/dalvik/src/main/java/dalvik/system/ThreadPrioritySetter.java b/dalvik/src/main/java/dalvik/system/ThreadPrioritySetter.java
deleted file mode 100644
index 484784b..0000000
--- a/dalvik/src/main/java/dalvik/system/ThreadPrioritySetter.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dalvik.system;
-
-/**
- * Interface for hooking to thread priority runtime setting
- * @hide
- */
-@libcore.api.CorePlatformApi
-@FunctionalInterface
-public interface ThreadPrioritySetter {
-    @libcore.api.CorePlatformApi
-    void setPriority(int nativeTid, int priority);
-}
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index 4436e3f..512d5ab 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -16,6 +16,9 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.FileDescriptor;
@@ -34,13 +37,18 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class VMDebug {
     /**
      * flag for startMethodTracing(), which adds the results from
      * startAllocCounting to the trace key file.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    // Must match android.os.Debug.TRACE_COUNT_ALLOCS.
     public static final int TRACE_COUNT_ALLOCS = 1;
 
     /* constants for getAllocCount */
@@ -56,63 +64,123 @@
     private static final int KIND_EXT_FREED_OBJECTS     = 1<<14;
     private static final int KIND_EXT_FREED_BYTES       = 1<<15;
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the number of all allocated objects.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_GLOBAL_ALLOCATED_OBJECTS =
         KIND_ALLOCATED_OBJECTS;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the cumulative size of all objects allocated.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_GLOBAL_ALLOCATED_BYTES =
         KIND_ALLOCATED_BYTES;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the number of freed objects.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_GLOBAL_FREED_OBJECTS =
         KIND_FREED_OBJECTS;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the cumulative size of all freed objects.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_GLOBAL_FREED_BYTES =
         KIND_FREED_BYTES;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the number of times an allocation triggered a blocking GC.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_GLOBAL_GC_INVOCATIONS =
         KIND_GC_INVOCATIONS;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the number of initialized classes.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_GLOBAL_CLASS_INIT_COUNT =
         KIND_CLASS_INIT_COUNT;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the cumulative time spent in class initialization.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_GLOBAL_CLASS_INIT_TIME =
         KIND_CLASS_INIT_TIME;
-    public static final int KIND_GLOBAL_EXT_ALLOCATED_OBJECTS =
-        KIND_EXT_ALLOCATED_OBJECTS;
-    public static final int KIND_GLOBAL_EXT_ALLOCATED_BYTES =
-        KIND_EXT_ALLOCATED_BYTES;
-    public static final int KIND_GLOBAL_EXT_FREED_OBJECTS =
-        KIND_EXT_FREED_OBJECTS;
-    public static final int KIND_GLOBAL_EXT_FREED_BYTES =
-        KIND_EXT_FREED_BYTES;
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the number of all allocated objects for current thread.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_THREAD_ALLOCATED_OBJECTS =
         KIND_ALLOCATED_OBJECTS << 16;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the cumulative size of all objects allocated for current thread.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_THREAD_ALLOCATED_BYTES =
         KIND_ALLOCATED_BYTES << 16;
-    public static final int KIND_THREAD_FREED_OBJECTS =
-        KIND_FREED_OBJECTS << 16;
-    public static final int KIND_THREAD_FREED_BYTES =
-        KIND_FREED_BYTES << 16;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Constant for {@link #getAllocCount(int)}
+     * to get the number of times an allocation triggered a blocking GC for current thread.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_THREAD_GC_INVOCATIONS =
         KIND_GC_INVOCATIONS << 16;
-    public static final int KIND_THREAD_CLASS_INIT_COUNT =
-        KIND_CLASS_INIT_COUNT << 16;
-    public static final int KIND_THREAD_CLASS_INIT_TIME =
-        KIND_CLASS_INIT_TIME << 16;
-    public static final int KIND_THREAD_EXT_ALLOCATED_OBJECTS =
-        KIND_EXT_ALLOCATED_OBJECTS << 16;
-    public static final int KIND_THREAD_EXT_ALLOCATED_BYTES =
-        KIND_EXT_ALLOCATED_BYTES << 16;
-    public static final int KIND_THREAD_EXT_FREED_OBJECTS =
-        KIND_EXT_FREED_OBJECTS << 16;
-    public static final int KIND_THREAD_EXT_FREED_BYTES =
-        KIND_EXT_FREED_BYTES << 16;
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Constant for {@link #getAllocCount(int)} to get all possible stats.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int KIND_ALL_COUNTS = 0xffffffff;
 
     /* all methods are static */
@@ -122,8 +190,11 @@
      * Returns the time since the last known debugger activity.
      *
      * @return the time in milliseconds, or -1 if the debugger is not connected
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public static native long lastDebuggerActivity();
 
@@ -132,8 +203,11 @@
      * enabled, a debugger cannot be attached.
      *
      * @return true if debugging is enabled
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public static native boolean isDebuggingEnabled();
 
@@ -141,9 +215,12 @@
      * Determines if a debugger is currently attached.
      *
      * @return true if (and only if) a debugger is connected
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public static native boolean isDebuggerConnected();
 
@@ -151,20 +228,14 @@
      * Returns an array of strings that identify VM features.  This is
      * used by DDMS to determine what sorts of operations the VM can
      * perform.
-     */
-    @libcore.api.CorePlatformApi
-    public static native String[] getVmFeatureList();
-
-    /**
-     * Start method tracing with default name, size, and with <code>0</code>
-     * flags.
      *
-     * @deprecated Not used, not needed.
+     * @return array of strings identifying VM features
+     *
+     * @hide
      */
-    @Deprecated
-    public static void startMethodTracing() {
-        throw new UnsupportedOperationException();
-    }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static native String[] getVmFeatureList();
 
     /**
      * Start method tracing, specifying a file name as well as a default
@@ -178,39 +249,47 @@
      * be created under the /sdcard/ directory. If a name is not given,
      * the default is /sdcard/dmtrace.trace.</p>
      *
-     * @param traceFileName name to give the trace file
-     * @param bufferSize the maximum size of both files combined. If passed
-     * as <code>0</code>, it defaults to 8MB.
-     * @param flags flags to control method tracing. The only one that
-     * is currently defined is {@link #TRACE_COUNT_ALLOCS}.
+     * @param traceFileName   name to give the trace file
+     * @param bufferSize      the maximum size of both files combined. If passed
+     *                        as {@code 0}, it defaults to 8MB.
+     * @param flags           flags to control method tracing. The only one that
+     *                        is currently defined is {@link #TRACE_COUNT_ALLOCS}.
      * @param samplingEnabled if true, sample profiling is enabled. Otherwise,
-     * method instrumentation is used.
-     * @param intervalUs the time between samples in microseconds when
-     * sampling is enabled.
+     *                        method instrumentation is used.
+     * @param intervalUs      the time between samples in microseconds when
+     *                        sampling is enabled.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void startMethodTracing(String traceFileName, int bufferSize, int flags, boolean samplingEnabled, int intervalUs) {
         startMethodTracingFilename(traceFileName, checkBufferSize(bufferSize), flags, samplingEnabled, intervalUs);
     }
 
     /**
-     * Like startMethodTracing(String, int, int), but taking an already-opened
-     * FileDescriptor in which the trace is written.  The file name is also
-     * supplied simply for logging.  Makes a dup of the file descriptor.
-     */
-    public static void startMethodTracing(String traceFileName, FileDescriptor fd, int bufferSize,
-                                          int flags, boolean samplingEnabled, int intervalUs) {
-        startMethodTracing(traceFileName, fd, bufferSize, flags, samplingEnabled, intervalUs,
-                           false);
-    }
-
-    /**
-     * Like startMethodTracing(String, int, int), but taking an already-opened
-     * FileDescriptor in which the trace is written.  The file name is also
+     * Like {@link #startMethodTracing(String, int, int)}, but taking an already-opened
+     * {@code FileDescriptor} in which the trace is written.  The file name is also
      * supplied simply for logging.  Makes a dup of the file descriptor.
      * Streams tracing data to the file if streamingOutput is true.
+     *
+     * @param traceFileName   name to give the trace file
+     * @param fd              already opened {@code FileDescriptor} in which trace is written
+     * @param bufferSize      the maximum size of both files combined. If passed
+     *                        as {@code 0}, it defaults to 8MB.
+     * @param flags           flags to control method tracing. The only one that
+     *                        is currently defined is {@link #TRACE_COUNT_ALLOCS}.
+     * @param samplingEnabled if true, sample profiling is enabled. Otherwise,
+     *                        method instrumentation is used.
+     * @param intervalUs      the time between samples in microseconds when
+     *                        sampling is enabled.
+     * @param streamingOutput streams tracing data to the duped {@code fd} file descriptor
+     *                        if {@code streamingOutput} is {@code true}.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void startMethodTracing(String traceFileName, FileDescriptor fd, int bufferSize,
                                           int flags, boolean samplingEnabled, int intervalUs,
                                           boolean streamingOutput) {
@@ -222,11 +301,23 @@
     }
 
     /**
-     * Starts method tracing without a backing file.  When stopMethodTracing
+     * Starts method tracing without a backing file.  When {@link #stopMethodTracing()}
      * is called, the result is sent directly to DDMS.  (If DDMS is not
      * attached when tracing ends, the profiling data will be discarded.)
+     *
+     * @param bufferSize      the maximum size of both files combined. If passed
+     *                        as {@code 0}, it defaults to 8MB.
+     * @param flags           flags to control method tracing. The only one that
+     *                        is currently defined is {@link #TRACE_COUNT_ALLOCS}.
+     * @param samplingEnabled if true, sample profiling is enabled. Otherwise,
+     *                        method instrumentation is used.
+     * @param intervalUs      the time between samples in microseconds when
+     *                        sampling is enabled.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void startMethodTracingDdms(int bufferSize, int flags, boolean samplingEnabled, int intervalUs) {
         startMethodTracingDdmsImpl(checkBufferSize(bufferSize), flags, samplingEnabled, intervalUs);
     }
@@ -250,29 +341,23 @@
     /**
      * Determine whether method tracing is currently active and what type is
      * active.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native int getMethodTracingMode();
 
     /**
      * Stops method tracing.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void stopMethodTracing();
 
     /**
-     * Starts sending Dalvik method trace info to the emulator.
-     */
-    @libcore.api.CorePlatformApi
-    public static native void startEmulatorTracing();
-
-    /**
-     * Stops sending Dalvik method trace info to the emulator.
-     */
-    @libcore.api.CorePlatformApi
-    public static native void stopEmulatorTracing();
-
-    /**
      * Get an indication of thread CPU usage. The value returned indicates the
      * amount of time that the current thread has spent executing code or
      * waiting for certain types of I/O.
@@ -283,27 +368,60 @@
      *
      * @return the CPU usage. A value of -1 means the system does not support
      *         this feature.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public static native long threadCpuTimeNanos();
 
     /**
-     * Count the number and aggregate size of memory allocations between
-     * two points.
+     * Starts counting the number and aggregate size of memory allocations.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void startAllocCounting();
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Stops counting the number and aggregate size of memory allocations.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void stopAllocCounting();
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Returns information on the number of objects allocated by the runtime between a
+     * {@link #startAllocCounting() start} and {@link #stopAllocCounting() stop}.
+     *
+     * @param kind either {@code KIND_GLOBAL_*} or {@code KIND_THREAD_*}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native int getAllocCount(int kind);
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Resets counting the number and aggregate size of memory allocations for the given kinds.
+     *
+     * @param kinds a union of {@code KIND_GLOBAL_*} and {@code KIND_THREAD_*}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void resetAllocCount(int kinds);
 
     /**
      * This method exists for binary compatibility.  It was part of
      * the allocation limits API which was removed in Android 3.0 (Honeycomb).
+     *
+     * @hide
      */
     @Deprecated
     public static int setAllocationLimit(int limit) {
@@ -313,6 +431,8 @@
     /**
      * This method exists for binary compatibility.  It was part of
      * the allocation limits API which was removed in Android 3.0 (Honeycomb).
+     *
+     * @hide
      */
     @Deprecated
     public static int setGlobalAllocationLimit(int limit) {
@@ -321,16 +441,43 @@
 
     /**
      * Count the number of instructions executed between two points.
+     *
+     * @hide
      */
-    public static native void startInstructionCounting();
-    public static native void stopInstructionCounting();
-    public static native void getInstructionCount(int[] counts);
-    public static native void resetInstructionCount();
+    @Deprecated
+    public static void startInstructionCounting() {}
+
+    /**
+     *
+     * @hide
+     */
+    @Deprecated
+    public static void stopInstructionCounting() {}
+
+    /**
+     *
+     * @hide
+     */
+    @Deprecated
+    public static void getInstructionCount(int[] counts) {}
+
+    /**
+     *
+     * @hide
+     */
+    @Deprecated
+    public static void resetInstructionCount() {}
 
     /**
      * Dumps a list of loaded class to the log file.
+     *
+     * @param flags a union of {@link android.os.Debug.SHOW_FULL_DETAIL},
+     *    {@link android.os.Debug.SHOW_CLASSLOADER}, and {@link android.os.Debug.SHOW_INITIALIZED}.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public static native void printLoadedClasses(int flags);
 
@@ -338,8 +485,11 @@
      * Gets the number of loaded classes.
      *
      * @return the number of loaded classes
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public static native int getLoadedClassCount();
 
@@ -352,8 +502,11 @@
      * @throws UnsupportedOperationException if the VM was built without
      *         HPROF support.
      * @throws IOException if an error occurs while opening or writing files.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void dumpHprofData(String filename) throws IOException {
         if (filename == null) {
             throw new NullPointerException("filename == null");
@@ -366,8 +519,11 @@
      *
      * @throws UnsupportedOperationException if the VM was built without
      *         HPROF support.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void dumpHprofDataDdms();
 
     /**
@@ -377,8 +533,12 @@
      *        file name is only used in log messages (and may be null).
      * @param fd Descriptor of open file that will receive the output.
      *        If this is null, the fileName is used instead.
+     * @throws {@link IOException} if an error occurs while opening or writing files.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void dumpHprofData(String fileName, FileDescriptor fd)
             throws IOException {
        dumpHprofData(fileName, fd != null ? fd.getInt$() : -1);
@@ -388,59 +548,32 @@
             throws IOException;
 
     /**
-     * Primes the register map cache.
-     */
-    @libcore.api.CorePlatformApi
-    public static native boolean cacheRegisterMap(String classAndMethodDesc);
-
-    /**
      * Dumps the contents of the VM reference tables (e.g. JNI locals and
      * globals) to the log file.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void dumpReferenceTables();
 
     /**
-     * Crashes the VM.  Seriously.  Dumps the interpreter stack trace for
-     * the current thread and then aborts the VM so you can see the native
-     * stack trace.  Useful for figuring out how you got somewhere when
-     * lots of native code is involved.
-     */
-    public static native void crash();
-
-    /**
-     * Together with gdb, provide a handy way to stop the VM at user-tagged
-     * locations.
-     */
-    public static native void infopoint(int id);
-
-    /*
-     * Fake method, inserted into dmtrace output when the garbage collector
-     * runs.  Not actually called.
-     */
-    private static void startGC() {}
-
-    /*
-     * Fake method, inserted into dmtrace output during class preparation
-     * (loading and linking, but not verification or initialization).  Not
-     * actually called.
-     */
-    private static void startClassPrep() {}
-
-    /**
      * Counts the instances of a class.
      * It is the caller's responsibility to do GC if they don't want unreachable
      * objects to get counted.
      *
      * @param klass the class to be counted.
      * @param assignable if true, any instance whose class is assignable to
-     *                   <code>klass</code>, as defined by {@link Class#isAssignableFrom},
+     *                   {@code klass}, as defined by {@link Class#isAssignableFrom},
      *                   is counted. If false, only instances whose class is
-     *                   equal to <code>klass</code> are counted.
+     *                   equal to {@code klass} are counted.
      * @return the number of matching instances.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native long countInstancesOfClass(Class klass, boolean assignable);
 
     /**
@@ -450,14 +583,17 @@
      *
      * @param classes the classes to be counted.
      * @param assignable if true, any instance whose class is assignable to
-     *                   <code>classes[i]</code>, as defined by {@link Class#isAssignableFrom},
+     *                   {@code classes[i]}, as defined by {@link Class#isAssignableFrom},
      *                   is counted. If false, only instances whose class is
-     *                   equal to <code>classes[i]</code> are counted.
+     *                   equal to {@code classes[i]} are counted.
      * @return an array containing the number of matching instances. The value
-     *         for index <code>i</code> is the number of instances of
-     *         the class <code>classes[i]</code>
+     *         for index {@code i} is the number of instances of
+     *         the class {@code classes[i]}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native long[] countInstancesOfClasses(Class[] classes, boolean assignable);
 
     /**
@@ -467,36 +603,17 @@
      *
      * @param classes the classes to get instances of.
      * @param assignable if true, any instance whose class is assignable to
-     *                   <code>classes[i]</code>, as defined by {@link Class#isAssignableFrom},
+     *                   {@code classes[i]}, as defined by {@link Class#isAssignableFrom},
      *                   is included. If false, only instances whose class is
-     *                   equal to <code>classes[i]</code> are included.
+     *                   equal to {@code classes[i]} are included.
      * @return an array containing the list of matching instances. The value
-     *         for index <code>i</code> is an array containing the instances
-     *         of the class <code>classes[i]</code>
+     *         for index {@code i} is an array containing the instances
+     *         of the class {@code classes[i]}
+     *
+     * @hide
      */
     public static native Object[][] getInstancesOfClasses(Class[] classes, boolean assignable);
 
-    /**
-     * Export the heap per-space stats for dumpsys meminfo.
-     *
-     * The content of the array is:
-     *
-     * <pre>
-     *   data[0] : the application heap space size
-     *   data[1] : the application heap space allocated bytes
-     *   data[2] : the application heap space free bytes
-     *   data[3] : the zygote heap space size
-     *   data[4] : the zygote heap space allocated size
-     *   data[5] : the zygote heap space free size
-     *   data[6] : the large object space size
-     *   data[7] : the large object space allocated bytes
-     *   data[8] : the large object space free bytes
-     * </pre>
-     *
-     * @param data the array into which the stats are written.
-     */
-    public static native void getHeapSpaceStats(long[] data);
-
     /* Map from the names of the runtime stats supported by getRuntimeStat() to their IDs */
     private static final HashMap<String, Integer> runtimeStatsMap = new HashMap<>();
 
@@ -509,17 +626,22 @@
         runtimeStatsMap.put("art.gc.blocking-gc-time", 5);
         runtimeStatsMap.put("art.gc.gc-count-rate-histogram", 6);
         runtimeStatsMap.put("art.gc.blocking-gc-count-rate-histogram", 7);
+        runtimeStatsMap.put("art.gc.objects-allocated", 8);
+        runtimeStatsMap.put("art.gc.total-time-waiting-for-gc", 9);
     }
 
     /**
      * Returns the value of a particular runtime statistic or {@code null} if no
      * such runtime statistic exists.
      *
-     * @param statName
-     *            the name of the runtime statistic to look up.
+     * @param statName the name of the runtime statistic to look up.
+     *
      * @return the value of the runtime statistic.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static String getRuntimeStat(String statName) {
         if (statName == null) {
             throw new NullPointerException("statName == null");
@@ -536,8 +658,11 @@
      * that {@link #getRuntimeStat()} supports.
      *
      * @return a map of the names/values of the supported runtime statistics.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static Map<String, String> getRuntimeStats() {
         HashMap<String, String> map = new HashMap<>();
         String[] values = getRuntimeStatsInternal();
@@ -555,19 +680,15 @@
     /**
      * Attaches an agent to the VM.
      *
-     * @param agent The path to the agent .so file plus optional agent arguments.
-     */
-    public static void attachAgent(String agent) throws IOException {
-        attachAgent(agent, null);
-    }
-
-    /**
-     * Attaches an agent to the VM.
-     *
-     * @param agent The path to the agent .so file plus optional agent arguments.
+     * @param agent       The path to the agent .so file plus optional agent arguments.
      * @param classLoader The classloader to use as a loading context.
+     *
+     * @throws IOException if an error occurs while opening {@code agent} file.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void attachAgent(String agent, ClassLoader classLoader) throws IOException {
         nativeAttachAgent(agent, classLoader);
     }
@@ -584,6 +705,8 @@
      * inherited from a superclass or an implemented interface.
      *
      * @param klass The class whose methods should be exempted.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public static native void allowHiddenApiReflectionFrom(Class<?> klass);
@@ -592,7 +715,10 @@
      * Sets the number of frames recorded for allocation tracking.
      *
      * @param stackDepth The number of frames captured for each stack trace.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void setAllocTrackerStackDepth(int stackDepth);
 }
diff --git a/dalvik/src/main/java/dalvik/system/ZygoteHooks.java b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
index 7e8fe36..a17a257 100644
--- a/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
+++ b/dalvik/src/main/java/dalvik/system/ZygoteHooks.java
@@ -16,12 +16,18 @@
 
 package dalvik.system;
 
-import android.icu.impl.CacheValue;
-import android.icu.text.DecimalFormatSymbols;
-import android.icu.util.ULocale;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
+import libcore.icu.ICU;
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.lang.reflect.Method;
+import java.lang.ClassNotFoundException;
+import java.lang.NoSuchMethodException;
+import java.lang.ReflectiveOperationException;
 
 /**
  * Provides hooks for the zygote to call back into the runtime to perform
@@ -29,9 +35,11 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class ZygoteHooks {
     private static long token;
+    private static Method enableMemoryMappedDataMethod;
 
     /** All methods are static, no need to instantiate. */
     private ZygoteHooks() {
@@ -40,40 +48,48 @@
     /**
      * Called by the zygote when starting up. It marks the point when any thread
      * start should be an error, as only internal daemon threads are allowed there.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void startZygoteNoThreadCreation();
 
     /**
      * Called when the zygote begins preloading classes and data.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void onBeginPreload() {
-        // Pin ICU data in memory from this point that would normally be held by soft references.
-        // Without this, any references created immediately below or during class preloading
-        // would be collected when the Zygote GC runs in gcAndFinalize().
-        CacheValue.setStrength(CacheValue.Strength.STRONG);
+        com.android.i18n.system.ZygoteHooks.onBeginPreload();
 
-        // Explicitly exercise code to cache data apps are likely to need.
-        ULocale[] localesToPin = { ULocale.ROOT, ULocale.US, ULocale.getDefault() };
-        for (ULocale uLocale : localesToPin) {
-            new DecimalFormatSymbols(uLocale);
+        ICU.initializeCacheInZygote();
+
+        // Look up JaCoCo on the boot classpath, if it exists. This will be used later for enabling
+        // memory-mapped Java coverage.
+        try {
+          Class<?> jacocoOfflineClass = Class.forName("org.jacoco.agent.rt.internal.Offline");
+          enableMemoryMappedDataMethod = jacocoOfflineClass.getMethod("enableMemoryMappedData");
+        } catch (ClassNotFoundException e) {
+          // JaCoCo was not on the boot classpath, so this is not a coverage build.
+        } catch (NoSuchMethodException e) {
+          // Method was not found in the JaCoCo Offline class. The version of JaCoCo is not
+          // compatible with memory-mapped coverage.
+          throw new RuntimeException(e);
         }
-
-        // Framework's LocalLog is used during app start-up. It indirectly uses the current ICU time
-        // zone. Pre-loading the current time zone in ICU improves app startup time. b/150605074
-        // We're being explicit about the fully qualified name of the TimeZone class to avoid
-        // confusion with java.util.TimeZome.getDefault().
-        android.icu.util.TimeZone.getDefault();
     }
 
     /**
      * Called when the zygote has completed preloading classes and data.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void onEndPreload() {
-        // All cache references created by ICU from this point will be soft.
-        CacheValue.setStrength(CacheValue.Strength.SOFT);
+        com.android.i18n.system.ZygoteHooks.onEndPreload();
 
         // Clone standard descriptors as originals closed / rebound during zygote post fork.
         FileDescriptor.in.cloneForFork();
@@ -85,8 +101,11 @@
      * Runs several special GCs to try to clean up a few generations of
      * softly- and final-reachable objects, along with any other garbage.
      * This is only useful just before a fork().
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void gcAndFinalize() {
         final VMRuntime runtime = VMRuntime.getRuntime();
 
@@ -101,8 +120,11 @@
     /**
      * Called by the zygote when startup is finished. It marks the point when it is
      * conceivable that threads would be started again, e.g., restarting daemons.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void stopZygoteNoThreadCreation();
 
     /**
@@ -111,8 +133,11 @@
      * the child process and {@link #postForkCommon()} on both the parent and the child
      * process. {@code postForkCommon} is called after {@code postForkChild} in
      * the child process.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void preFork() {
         Daemons.stop();
         token = nativePreFork();
@@ -122,37 +147,77 @@
     /**
      * Called by the zygote in the system server process after forking. This method is is called
      * before {@code postForkChild} for system server.
+     *
+     * @param runtimeFlags The flags listed in com.android.internal.os.Zygote passed to the runtime.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void postForkSystemServer(int runtimeFlags) {
         nativePostForkSystemServer(runtimeFlags);
     }
 
     /**
-     * Called by the zygote in the child process after every fork. The runtime
-     * flags from {@code runtimeFlags} are applied to the child process. The string
-     * {@code instructionSet} determines whether to use a native bridge.
+     * Called by the zygote in the child process after every fork.
+     *
+     * @param runtimeFlags The runtime flags to apply to the child process.
+     * @param isSystemServer Whether the child process is system server.
+     * @param isChildZygote Whether the child process is a child zygote.
+     * @param instructionSet The instruction set of the child, used to determine
+     *                       whether to use a native bridge.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void postForkChild(int runtimeFlags, boolean isSystemServer, boolean isZygote,
-            String instructionSet) {
-        nativePostForkChild(token, runtimeFlags, isSystemServer, isZygote, instructionSet);
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void postForkChild(int runtimeFlags, boolean isSystemServer,
+            boolean isChildZygote, String instructionSet) {
+        nativePostForkChild(token, runtimeFlags, isSystemServer, isChildZygote, instructionSet);
 
         Math.setRandomSeedInternal(System.currentTimeMillis());
+
+        // Enable memory-mapped coverage if JaCoCo is in the boot classpath. system_server is
+        // skipped due to being persistent and having its own coverage writing mechanism.
+        if (!isSystemServer && enableMemoryMappedDataMethod != null) {
+          try {
+            enableMemoryMappedDataMethod.invoke(null);
+          } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+          }
+        }
     }
 
     /**
      * Called by the zygote in both the parent and child processes after
      * every fork. In the child process, this method is called after
      * {@code postForkChild}.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void postForkCommon() {
         // Notify the runtime before creating new threads.
         nativePostZygoteFork();
         Daemons.startPostZygoteFork();
     }
 
+    /**
+     * Is it safe to keep all ART daemon threads stopped indefinitely in the zygote?
+     * The answer may change from false to true dynamically, but not in the other
+     * direction. Only called in Zygote.
+     *
+     * @return {@code true} if it's safe to keep all ART daemon threads stopped
+     *         indefinitely in the zygote; and {@code false} otherwise
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static boolean isIndefiniteThreadSuspensionSafe() {
+        return nativeZygoteLongSuspendOk();
+    }
 
     // Hook for SystemServer specific early initialization post-forking.
     private static native void nativePostForkSystemServer(int runtimeFlags);
@@ -165,6 +230,8 @@
                                                    boolean isSystemServer, boolean isZygote,
                                                    String instructionSet);
 
+    private static native boolean nativeZygoteLongSuspendOk();
+
     /**
      * We must not fork until we're single-threaded again. Wait until /proc shows we're
      * down to just one thread.
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
deleted file mode 100644
index 271a985..0000000
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2007 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.dalvik;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-/**
- * Methods used to test calling into native code. The methods in this
- * class are all effectively no-ops and may be used to test the mechanisms
- * and performance of calling native methods.
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class NativeTestTarget {
-    @libcore.api.CorePlatformApi
-    public NativeTestTarget() {
-    }
-
-    /**
-     * This is used to benchmark dalvik's inline natives.
-     */
-    @libcore.api.CorePlatformApi
-    public static void emptyInlineMethod() {
-    }
-
-    /**
-     * This is used to benchmark dalvik's inline natives.
-     */
-    @libcore.api.CorePlatformApi
-    public static native void emptyInternalStaticMethod();
-
-    // Synchronized methods. Test normal JNI only.
-    @libcore.api.CorePlatformApi
-    public static native synchronized void emptyJniStaticSynchronizedMethod0();
-    @libcore.api.CorePlatformApi
-    public native synchronized void emptyJniSynchronizedMethod0();
-
-    // Static methods without object parameters. Test all optimization combinations.
-
-    // Normal native.
-    @libcore.api.CorePlatformApi
-    public static native void emptyJniStaticMethod0();
-    // Normal native.
-    @libcore.api.CorePlatformApi
-    public static native void emptyJniStaticMethod6(int a, int b, int c, int d, int e, int f);
-
-    @libcore.api.CorePlatformApi
-    @FastNative
-    public static native void emptyJniStaticMethod0_Fast();
-    @libcore.api.CorePlatformApi
-    @FastNative
-    public static native void emptyJniStaticMethod6_Fast(int a, int b, int c, int d, int e, int f);
-
-    @libcore.api.CorePlatformApi
-    @CriticalNative
-    public static native void emptyJniStaticMethod0_Critical();
-    @libcore.api.CorePlatformApi
-    @CriticalNative
-    public static native void emptyJniStaticMethod6_Critical(int a, int b, int c, int d, int e, int f);
-    // Instance methods or methods with object parameters. Test {Normal, @FastNative} combinations.
-
-    // Normal native.
-    @libcore.api.CorePlatformApi
-    public native void emptyJniMethod0();
-    // Normal native.
-    @libcore.api.CorePlatformApi
-    public native void emptyJniMethod6(int a, int b, int c, int d, int e, int f);
-
-    /**
-     * This is an empty native static method with six args, hooked up
-     * using JNI. These have more complex args to show the cost of
-     * parsing the signature. All six values should be null
-     * references.
-     */
-    // Normal native.
-    @libcore.api.CorePlatformApi
-    public static native void emptyJniStaticMethod6L(String a, String[] b,
-        int[][] c, Object d, Object[] e, Object[][][][] f);
-
-    // Normal native.
-    @libcore.api.CorePlatformApi
-    public native void emptyJniMethod6L(String a, String[] b,
-        int[][] c, Object d, Object[] e, Object[][][][] f);
-
-    @libcore.api.CorePlatformApi
-    @FastNative
-    public native void emptyJniMethod0_Fast();
-    @libcore.api.CorePlatformApi
-    @FastNative
-    public native void emptyJniMethod6_Fast(int a, int b, int c, int d, int e, int f);
-
-    /**
-     * This is an empty native static method with six args, hooked up
-     * using JNI. These have more complex args to show the cost of
-     * parsing the signature. All six values should be null
-     * references.
-     */
-    @libcore.api.CorePlatformApi
-    @FastNative
-    public static native void emptyJniStaticMethod6L_Fast(String a, String[] b,
-        int[][] c, Object d, Object[] e, Object[][][][] f);
-
-    @libcore.api.CorePlatformApi
-    @FastNative
-    public native void emptyJniMethod6L_Fast(String a, String[] b,
-        int[][] c, Object d, Object[] e, Object[][][][] f);
-}
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java
index 292e967..38b4113 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java
@@ -16,6 +16,9 @@
 
 package org.apache.harmony.dalvik.ddmc;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import java.nio.ByteBuffer;
 
@@ -28,26 +31,51 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public class Chunk {
 
-    /*
+    /**
      * Public members.  Do not rename without updating the VM.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public int type;                // chunk type
+    /**
+     * @hide
+     */
     public byte[] data;             // chunk data
-    public int offset, length;      // position within "data"
+    /**
+     * @hide
+     */
+    public int offset;              // position within "dataf"
+
+    /**
+     * @hide
+     */
+    public int length;
 
     /**
      * Blank constructor.  Fill in your own fields.
+     *
+     * @hide
      */
     public Chunk() {}
 
     /**
      * Constructor with all fields.
+     *
+     * @param type   chunk type
+     * @param data   chunk data
+     * @param offset offset in {@code data} where actual data starts from
+     * @param length length of the {@code data}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public Chunk(int type, byte[] data, int offset, int length) {
         this.type = type;
         this.data = data;
@@ -56,11 +84,17 @@
     }
 
     /**
-     * Construct from a ByteBuffer.  The chunk is assumed to start at
+     * Construct from a {@link ByteBuffer}.  The chunk is assumed to start at
      * offset 0 and continue to the current position.
+     *
+     * @param type chunk type
+     * @param buf  {@link ByteBuffer} containing chunk data
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public Chunk(int type, ByteBuffer buf) {
         this.type = type;
 
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java
index 0fb74f8..fe936d5 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java
@@ -16,6 +16,9 @@
 
 package org.apache.harmony.dalvik.ddmc;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -23,52 +26,86 @@
 /**
  * Handle a chunk of data sent from a DDM server.
  *
- * To handle a chunk type, sub-class ChunkHandler and register your class
- * with DdmServer.
+ * To handle a chunk type, sub-class {@link ChunkHandler} and register your class
+ * with {@link DdmServer}.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public abstract class ChunkHandler {
 
+    /**
+     * Byte order of the data in the chunk.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final ByteOrder CHUNK_ORDER = ByteOrder.BIG_ENDIAN;
 
+    /**
+     * @hide
+     */
     public static final int CHUNK_FAIL = type("FAIL");
 
-
-    @libcore.api.CorePlatformApi
+    /**
+     * Constructs chunk handler.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public ChunkHandler() {}
 
     /**
      * Called when the DDM server connects.  The handler is allowed to
      * send messages to the server.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public abstract void connected();
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public abstract void onConnected();
 
     /**
      * Called when the DDM server disconnects.  Can be used to disable
      * periodic transmissions or clean up saved state.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public abstract void disconnected();
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public abstract void onDisconnected();
 
     /**
-     * Handle a single chunk of data.  "request" includes the type and
+     * Handle a single chunk of data.  {@code request} includes the type and
      * the chunk payload.
      *
-     * Returns a response in a Chunk.
+     * Returns a response in a {@link Chunk}.
+     *
+     * @param request chunk type and payload
+     * @return        {@link Chunk} with response
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public abstract Chunk handleChunk(Chunk request);
 
     /**
-     * Create a FAIL chunk.  The "handleChunk" methods can use this to
+     * Create a FAIL chunk.  The {@link #handleChunk(Chunk)} methods can use this to
      * return an error message when they are not able to process a chunk.
+     *
+     * @param errorCode arbitrary number to distinguish error
+     * @param msg       error message
+     * @return          {@link Chunk} with response
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static Chunk createFailChunk(int errorCode, String msg) {
         if (msg == null)
             msg = "";
@@ -77,15 +114,24 @@
         out.order(ChunkHandler.CHUNK_ORDER);
         out.putInt(errorCode);
         out.putInt(msg.length());
-        putString(out, msg);
+        final int len = msg.length();
+        for (int i = 0; i < len; i++) {
+            out.putChar(msg.charAt(i));
+        }
 
         return new Chunk(CHUNK_FAIL, out);
     }
 
     /**
-     * Utility function to wrap a ByteBuffer around a Chunk.
+     * Utility function to wrap a {@link ByteBuffer} around a {@link Chunk}.
+     *
+     * @param request chunk to be wrapped
+     * @return        {@link ByteBuffer} wrapping data from the given chunk
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static ByteBuffer wrapChunk(Chunk request) {
         ByteBuffer in;
 
@@ -94,35 +140,13 @@
         return in;
     }
 
-
-    /**
-     * Utility function to copy a String out of a ByteBuffer.
-     *
-     * This is here because multiple chunk handlers can make use of it,
-     * and there's nowhere better to put it.
-     */
-    @libcore.api.CorePlatformApi
-    public static String getString(ByteBuffer buf, int len) {
-        char[] data = new char[len];
-        for (int i = 0; i < len; i++)
-            data[i] = buf.getChar();
-        return new String(data);
-    }
-
-    /**
-     * Utility function to copy a String into a ByteBuffer.
-     */
-    @libcore.api.CorePlatformApi
-    public static void putString(ByteBuffer buf, String str) {
-        int len = str.length();
-        for (int i = 0; i < len; i++)
-            buf.putChar(str.charAt(i));
-    }
-
     /**
      * Convert a 4-character string to a 32-bit type.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int type(String typeName) {
         if (typeName.length() != 4) {
             throw new IllegalArgumentException("Bad type name: " + typeName);
@@ -136,8 +160,11 @@
 
     /**
      * Convert an integer type to a 4-character string.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static String name(int type)
     {
         char[] ascii = new char[4];
@@ -149,5 +176,4 @@
 
         return new String(ascii);
     }
-
 }
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
index f1f58b6..1465641 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java
@@ -16,6 +16,9 @@
 
 package org.apache.harmony.dalvik.ddmc;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.Collection;
@@ -30,8 +33,9 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
-public class DdmServer {
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+public final class DdmServer {
 
     private static HashMap<Integer,ChunkHandler> mHandlerMap =
         new HashMap<Integer,ChunkHandler>();
@@ -49,12 +53,19 @@
     private DdmServer() {}
 
     /**
-     * Register an instance of the ChunkHandler class to handle a specific
+     * Register an instance of the {@link ChunkHandler} class to handle a specific
      * chunk type.
      *
      * Throws an exception if the type already has a handler registered.
+     *
+     * @param type    int describing registered handler
+     * @param handler handler to be registered
+     * @throws NullPointerException if {@code handler} is {@code null}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void registerHandler(int type, ChunkHandler handler) {
         if (handler == null) {
             throw new NullPointerException("handler == null");
@@ -72,7 +83,11 @@
      * Unregister the existing handler for the specified type.
      *
      * Returns the old handler.
+     *
+     * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static ChunkHandler unregisterHandler(int type) {
         synchronized (mHandlerMap) {
             return mHandlerMap.remove(type);
@@ -82,8 +97,11 @@
     /**
      * The application must call here after it finishes registering
      * handlers.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void registrationComplete() {
         // sync on mHandlerMap because it's convenient and makes a kind of
         // sense
@@ -98,9 +116,14 @@
      * JDWP "event", which does not elicit a response from the server.
      *
      * Use this for "unsolicited" chunks.
+     *
+     * @param chunk to send
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void sendChunk(Chunk chunk) {
         nativeSendChunk(chunk.type, chunk.data, chunk.offset, chunk.length);
     }
@@ -112,6 +135,8 @@
 
     /*
      * Called by the VM when the DDM server connects or disconnects.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     private static void broadcast(int event)
@@ -124,10 +149,10 @@
                 ChunkHandler handler = (ChunkHandler) iter.next();
                 switch (event) {
                     case CONNECTED:
-                        handler.connected();
+                        handler.onConnected();
                         break;
                     case DISCONNECTED:
-                        handler.disconnected();
+                        handler.onDisconnected();
                         break;
                     default:
                         throw new UnsupportedOperationException();
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
index 2c961da..8aca688 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmVmInternal.java
@@ -16,16 +16,19 @@
 
 package org.apache.harmony.dalvik.ddmc;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import dalvik.annotation.optimization.FastNative;
 
 /**
  * Declarations for some VM-internal DDM stuff.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
-public class DdmVmInternal {
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+public final class DdmVmInternal {
 
     /* do not instantiate */
     private DdmVmInternal() {}
@@ -34,69 +37,43 @@
      * Enable thread notification.
      *
      * This is built into the VM, since that's where threads get managed.
-     */
-    @libcore.api.CorePlatformApi
-    native public static void threadNotify(boolean enable);
-
-    /**
-     * Enable heap info updates.
      *
-     * This is built into the VM, since that's where the heap is managed.
+     * @param enabled {@code true} to enable thread notification; {@code false} to disable
      *
-     * @param when when to send the next HPIF chunk
-     * @return true on success.  false if 'when' is bad or if there was
-     *         an internal error.
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    @FastNative
-    native public static boolean heapInfoNotify(int when);
-
-    /**
-     * Enable heap segment updates for the java (isNative == false) or
-     * native (isNative == true) heap.
-     *
-     * This is built into the VM, since that's where the heap is managed.
-     */
-    @libcore.api.CorePlatformApi
-    native public static boolean heapSegmentNotify(int when, int what,
-        boolean isNative);
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    native public static void setThreadNotifyEnabled(boolean enabled);
 
     /**
      * Get status info for all threads.  This is for the THST chunk.
      *
      * Returns a byte array with the THST data, or null if something
      * went wrong.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     native public static byte[] getThreadStats();
 
     /**
      * Get a stack trace for the specified thread ID.  The ID can be found
      * in the data from getThreadStats.
+     * *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     native public static StackTraceElement[] getStackTraceById(int threadId);
 
     /**
      * Enable or disable "recent allocation" tracking.
+     *
+     * @param enabled {@code true} to enable recent allocation tracking; {@code false} to disable
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    native public static void enableRecentAllocations(boolean enable);
-
-    /*
-     * Return a boolean indicating whether or not the "recent allocation"
-     * feature is currently enabled.
-     */
-    @libcore.api.CorePlatformApi
-    @FastNative
-    native public static boolean getRecentAllocationStatus();
-
-    /**
-     * Fill a buffer with data on recent heap allocations.
-     */
-    @libcore.api.CorePlatformApi
-    @FastNative
-    native public static byte[] getRecentAllocations();
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    native public static void setRecentAllocationsTrackingEnabled(boolean enabled);
 }
diff --git a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
deleted file mode 100644
index 9f2b429..0000000
--- a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#define LOG_TAG "NativeTestTarget"
-
-#include <nativehelper/JNIHelp.h>
-
-#define NATIVE_METHOD(className, functionName, signature)               \
-    { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
-
-static void NativeTestTarget_emptyJniStaticSynchronizedMethod0(JNIEnv*, jclass) { }
-static void NativeTestTarget_emptyJniSynchronizedMethod0(JNIEnv*, jclass) { }
-
-static JNINativeMethod gMethods_NormalOnly[] = {
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticSynchronizedMethod0, "()V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniSynchronizedMethod0, "()V"),
-};
-
-
-static void NativeTestTarget_emptyJniMethod0(JNIEnv*, jobject) { }
-static void NativeTestTarget_emptyJniMethod6(JNIEnv*, jobject, int, int, int, int, int, int) { }
-static void NativeTestTarget_emptyJniMethod6L(JNIEnv*, jobject, jobject, jarray, jarray, jobject, jarray, jarray) { }
-static void NativeTestTarget_emptyJniStaticMethod6L(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { }
-
-static void NativeTestTarget_emptyJniStaticMethod0(JNIEnv*, jclass) { }
-static void NativeTestTarget_emptyJniStaticMethod6(JNIEnv*, jclass, int, int, int, int, int, int) { }
-
-static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(NativeTestTarget, emptyJniMethod0, "()V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniMethod6, "(IIIIII)V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod0, "()V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6, "(IIIIII)V"),
-};
-
-static void NativeTestTarget_emptyJniMethod0_Fast(JNIEnv*, jobject) { }
-static void NativeTestTarget_emptyJniMethod6_Fast(JNIEnv*, jobject, int, int, int, int, int, int) { }
-static void NativeTestTarget_emptyJniMethod6L_Fast(JNIEnv*, jobject, jobject, jarray, jarray, jobject, jarray, jarray) { }
-static void NativeTestTarget_emptyJniStaticMethod6L_Fast(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { }
-
-static void NativeTestTarget_emptyJniStaticMethod0_Fast(JNIEnv*, jclass) { }
-static void NativeTestTarget_emptyJniStaticMethod6_Fast(JNIEnv*, jclass, int, int, int, int, int, int) { }
-
-static JNINativeMethod gMethods_Fast[] = {
-    NATIVE_METHOD(NativeTestTarget, emptyJniMethod0_Fast, "()V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniMethod6_Fast, "(IIIIII)V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniMethod6L_Fast, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6L_Fast, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod0_Fast, "()V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6_Fast, "(IIIIII)V"),
-};
-
-
-static void NativeTestTarget_emptyJniStaticMethod0_Critical() { }
-static void NativeTestTarget_emptyJniStaticMethod6_Critical( int, int, int, int, int, int) { }
-
-static JNINativeMethod gMethods_Critical[] = {
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod0_Critical, "()V"),
-    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6_Critical, "(IIIIII)V"),
-};
-int register_org_apache_harmony_dalvik_NativeTestTarget(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods_NormalOnly, NELEM(gMethods_NormalOnly));
-    jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods, NELEM(gMethods));
-    jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods_Fast, NELEM(gMethods_Fast));
-    jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods_Critical, NELEM(gMethods_Critical));
-
-    return 0;
-}
diff --git a/dom/src/test/java/org/w3c/domts/level1/core/hc_nodegetownerdocumentnull.java b/dom/src/test/java/org/w3c/domts/level1/core/hc_nodegetownerdocumentnull.java
index df1f508..12708ec 100644
--- a/dom/src/test/java/org/w3c/domts/level1/core/hc_nodegetownerdocumentnull.java
+++ b/dom/src/test/java/org/w3c/domts/level1/core/hc_nodegetownerdocumentnull.java
@@ -31,7 +31,7 @@
  *     The "getOwnerDocument()" method returns null if the target
  *     node itself is a document.
  *     
- *     Invoke the "getOwnerDocument()" method on the master 
+ *     Invoke the "getOwnerDocument()" method on the root
  *     document.   The Document returned should be null.
 * @author Curt Arnold
 * @see <a href="http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#node-ownerDoc">http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/level-one-core#node-ownerDoc</a>
diff --git a/dom/src/test/java/org/w3c/domts/level2/core/ownerDocument01.java b/dom/src/test/java/org/w3c/domts/level2/core/ownerDocument01.java
index f77f32a..92e519a 100644
--- a/dom/src/test/java/org/w3c/domts/level2/core/ownerDocument01.java
+++ b/dom/src/test/java/org/w3c/domts/level2/core/ownerDocument01.java
@@ -34,7 +34,7 @@
  *     The "getOwnerDocument()" method returns null if the target
  *     node itself is a DocumentType which is not used with any document yet.
  *     
- *     Invoke the "getOwnerDocument()" method on the master 
+ *     Invoke the "getOwnerDocument()" method on the root
  *     document.   The DocumentType returned should be null.
 * @author NIST
 * @author Mary Brady
diff --git a/expectations/Android.bp b/expectations/Android.bp
index c2667d3..2aadab9 100644
--- a/expectations/Android.bp
+++ b/expectations/Android.bp
@@ -12,18 +12,34 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_license":
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-GPL
+    //   SPDX-license-identifier-GPL-2.0
+    //   SPDX-license-identifier-LGPL
+    //   SPDX-license-identifier-MIT
+    //   SPDX-license-identifier-W3C
+    default_applicable_licenses: ["libcore_license"],
+}
+
 expectations_visibility = [
+    "//art/build/sdk",
     "//cts/tests/libcore:__subpackages__",
 ]
 
-filegroup {
-    name: "libcore-expectations-knownfailures",
+java_library {
+    name: "libcore-expectations-knownfailures-jar",
     visibility: expectations_visibility,
-    srcs: ["knownfailures.txt"],
+    java_resources: ["knownfailures.txt"],
+    sdk_version: "core_current",
 }
 
-filegroup {
-    name: "libcore-expectations-virtualdeviceknownfailures",
+java_library {
+    name: "libcore-expectations-virtualdeviceknownfailures-jar",
     visibility: expectations_visibility,
-    srcs: ["virtualdeviceknownfailures.txt"],
+    java_resources: ["virtualdeviceknownfailures.txt"],
+    sdk_version: "core_current",
 }
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 80e1c97..b128717 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -100,11 +100,6 @@
   name: "org.apache.harmony.security.tests.java.security.SignatureTest#testUpdatebyteArrayintint2"
 },
 {
-  description: "Android doesn't allow null parent.",
-  result: EXEC_FAILED,
-  name: "tests.java.security.SecureClassLoaderTest#testSecureClassLoaderClassLoader"
-},
-{
   description: "not supported",
   result: EXEC_FAILED,
   names: [
@@ -1272,7 +1267,7 @@
   description: "Android's XPath fails 29 of 250 Jaxen XPath tests",
   bug: 3270761,
   names: [
-    "libcore.xml.JaxenXPathTestSuite$3#xml/defaultNamespace.xml / /dummy:a/dummy:b/dummy:c",
+    "libcore.xml.JaxenXPathTestSuite$3#xml/defaultNamespace.xml / /fake:a/fake:b/fake:c",
     "libcore.xml.JaxenXPathTestSuite$3#xml/evaluate.xml / //metatest[ evaluate(@select) = . ]",
     "libcore.xml.JaxenXPathTestSuite$3#xml/evaluate.xml / evaluate('//jumps/*')",
     "libcore.xml.JaxenXPathTestSuite$3#xml/evaluate.xml / evaluate('//jumps/*')/dog",
@@ -1743,9 +1738,9 @@
   names: [
       "libcore.java.security.OldDHTest#testDHGen",
       "libcore.java.security.OldKeyPairGeneratorTestDH#testKeyPairGenerator",
-      "libcore.javax.crypto.spec.KeyPairGeneratorTestDH#testKeyPairGenerator",
-      "libcore.javax.crypto.spec.AlgorithmParametersTestDH#testAlgorithmParameters",
-      "libcore.javax.crypto.spec.AlgorithmParameterGeneratorTestDH#testAlgorithmParameterGenerator"
+      "com.android.org.conscrypt.java.security.KeyPairGeneratorTestDH#testKeyPairGenerator",
+      "com.android.org.conscrypt.java.security.AlgorithmParametersTestDH#testAlgorithmParameters",
+      "com.android.org.conscrypt.java.security.AlgorithmParameterGeneratorTestDH#testAlgorithmParameterGenerator"
   ]
 },
 {
@@ -1791,5 +1786,37 @@
   result: EXEC_FAILED,
   bug: 62408076,
   name: "libcore.java.lang.reflect.annotations.AnnotatedElementParameterTest#testImplicitConstructorParameters_singleAnnotation"
+},
+{
+  description: "Android has different implementation in classes, e.g. SecurityManager.",
+  result: EXEC_FAILED,
+  bug: 181171596,
+  names: [
+    "test.java.util.concurrent.tck.AbstractExecutorServiceTest#testSubmitFailedPrivilegedExceptionAction",
+    "test.java.util.concurrent.tck.AbstractExecutorServiceTest#testSubmitPrivilegedAction",
+    "test.java.util.concurrent.tck.AbstractExecutorServiceTest#testSubmitPrivilegedExceptionAction",
+    "test.java.util.concurrent.tck.CompletableFutureTest#testMinimalCompletionStage_toCompletableFuture_exceptionalCompletion",
+    "test.java.util.concurrent.tck.CompletableFutureTest#testMinimalCompletionStage_toCompletableFuture_join",
+    "test.java.util.concurrent.tck.CompletableFutureTest#testMinimalCompletionStage_toCompletableFuture_mutable",
+    "test.java.util.concurrent.tck.CompletableFutureTest#testMinimalCompletionStage_toCompletableFuture_normalCompletion",
+    "test.java.util.concurrent.tck.CompletableFutureTest#testMinimalCompletionStage_toCompletableFuture_oneWayPropagation",
+    "test.java.util.concurrent.tck.CompletableFutureTest#testToCompletableFutureGarbageRetention",
+    "test.java.util.concurrent.tck.ExecutorsTest#testCreatePrivilegedCallableUsingCCLWithNoPrivs",
+    "test.java.util.concurrent.tck.ExecutorsTest#testPrivilegedCallableUsingCCLWithPrivs",
+    "test.java.util.concurrent.tck.ExecutorsTest#testPrivilegedCallableWithNoPrivs",
+    "test.java.util.concurrent.tck.ExecutorsTest#testPrivilegedCallableWithPrivs",
+    "test.java.util.concurrent.tck.ExecutorsTest#testPrivilegedThreadFactory",
+    "test.java.util.concurrent.tck.ForkJoinPoolTest#testSubmitFailedPrivilegedExceptionAction",
+    "test.java.util.concurrent.tck.ForkJoinPoolTest#testSubmitPrivilegedAction",
+    "test.java.util.concurrent.tck.ForkJoinPoolTest#testSubmitPrivilegedExceptionAction",
+    "test.java.util.concurrent.tck.PriorityQueueTest#testOfferNonComparable",
+    "test.java.util.concurrent.tck.StampedLockTest#testDeeplyNestedReadLocks",
+    "test.java.util.concurrent.tck.StampedLockTest#testReadLock_lockUnlock",
+    "test.java.util.concurrent.tck.StampedLockTest#testTryConvertToOptimisticRead",
+    "test.java.util.concurrent.tck.StampedLockTest#testTryConvertToReadLock",
+    "test.java.util.concurrent.tck.StampedLockTest#testTryConvertToWriteLock",
+    "test.java.util.concurrent.tck.ThreadPoolExecutorTest#testPoolSizeInvariants",
+    "test.java.util.concurrent.tck.ThreadTest#testGetAndSetDefaultUncaughtExceptionHandler"
+ ]
 }
 ]
diff --git a/expectations/virtualdeviceknownfailures.txt b/expectations/virtualdeviceknownfailures.txt
index b689133..4ab4074 100644
--- a/expectations/virtualdeviceknownfailures.txt
+++ b/expectations/virtualdeviceknownfailures.txt
@@ -27,7 +27,8 @@
   names: ["org.apache.harmony.tests.java.net.MulticastSocketTest",
           "libcore.java.net.MulticastSocketTest#testGroupReceiveIPv6",
           "libcore.java.nio.channels.DatagramChannelMulticastTest#test_joinAnySource_IPv4",
-          "libcore.java.nio.channels.DatagramChannelMulticastTest#test_joinAnySource_multicastLoopOption_IPv4"
+          "libcore.java.nio.channels.DatagramChannelMulticastTest#test_joinAnySource_multicastLoopOption_IPv4",
+	  "libcore.java.nio.channels.DatagramChannelMulticastTest#test_joinSourceSpecific_anyBind_ipv6"
           ],
   bug: 35922755
 },
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
index 9f397f4..2bf5660 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/FileTest.java
@@ -410,17 +410,22 @@
 
         // Test create an illegal file
         String sep = File.separator;
-        f1 = new File(sep + "..");
+        f1 = new File(sep + "a" + sep + ".." + sep + ".." + sep);
         try {
             f1.createNewFile();
             fail("should throw IOE");
         } catch (IOException e) {
             // expected;
         }
-        f1 = new File(sep + "a" + sep + ".." + sep + ".." + sep);
+
+        // Prior to kernel version 5.7, creating "/.." returns EISDIR, and in 5.7 or later,
+        // such syscall returns EEXIST. In the first case, IOException is thrown. In the second
+        // case, false is returned. The below test is modified to accept both of them.
+        // See http://b/176057454 for details.
+        f1 = new File(sep + "..");
         try {
-            f1.createNewFile();
-            fail("should throw IOE");
+            boolean result = f1.createNewFile();
+            assertFalse(result);
         } catch (IOException e) {
             // expected;
         }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOErrorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOErrorTest.java
index b95adc9..0b9339d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOErrorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOErrorTest.java
@@ -36,7 +36,7 @@
         String errorMsg = "java.io.IOError"; //$NON-NLS-1$
         assertTrue(e.toString().contains(errorMsg));
 
-        errorMsg = "A dummy error"; //$NON-NLS-1$
+        errorMsg = "A fake error"; //$NON-NLS-1$
         e = new IOError(new Throwable(errorMsg));
         assertTrue(e.toString().contains(errorMsg));
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOExceptionTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOExceptionTest.java
index d0b26db..3853e39 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOExceptionTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/IOExceptionTest.java
@@ -60,12 +60,12 @@
         // Test for constructor java.io.IOException(java.lang.String, java.lang.Throwable)
 
         IOException ioException = new IOException(
-                "A dummy IOException", new Throwable("A dummy Throwable")); //$NON-NLS-1$//$NON-NLS-2$
-        assertEquals("A dummy IOException", ioException.getMessage()); //$NON-NLS-1$
+                "A fake IOException", new Throwable("A fake Throwable")); //$NON-NLS-1$//$NON-NLS-2$
+        assertEquals("A fake IOException", ioException.getMessage()); //$NON-NLS-1$
 
         try {
             throw new IOException(
-                    "A dummy error", new Throwable("Some error message")); //$NON-NLS-1$ //$NON-NLS-2$
+                    "A fake error", new Throwable("Some error message")); //$NON-NLS-1$ //$NON-NLS-2$
         } catch (IOException e) {
             return;
         } catch (Exception e) {
@@ -80,7 +80,7 @@
      */
     public void test_Constructor_LThrowable() {
         // Test for constructor java.io.IOException(java.lang.Throwable)
-        Throwable cause = new Throwable("A dummy Throwable"); //$NON-NLS-1$
+        Throwable cause = new Throwable("A fake Throwable"); //$NON-NLS-1$
         IOException ioException = new IOException(cause);
         assertEquals(cause.toString(), ioException.getMessage());
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
index 16a7415..637ad9d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamClassTest.java
@@ -43,7 +43,7 @@
     @Rule
     public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
 
-    static class DummyClass implements Serializable {
+    static class FakeClass implements Serializable {
         private static final long serialVersionUID = 999999999999999L;
 
         long bam = 999L;
@@ -61,16 +61,16 @@
     public void test_forClass() {
         // Need to test during serialization to be sure an instance is
         // returned
-        ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookup(FakeClass.class);
         assertEquals("forClass returned an object: " + osc.forClass(),
-                DummyClass.class, osc.forClass());
+                FakeClass.class, osc.forClass());
     }
 
     /**
      * java.io.ObjectStreamClass#getField(java.lang.String)
      */
     public void test_getFieldLjava_lang_String() {
-        ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookup(FakeClass.class);
         assertEquals("getField did not return correct field", 'J', osc
                 .getField("bam").getTypeCode());
         assertNull("getField did not null for non-existent field", osc
@@ -81,7 +81,7 @@
      * java.io.ObjectStreamClass#getFields()
      */
     public void test_getFields() {
-        ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookup(FakeClass.class);
         ObjectStreamField[] osfArray = osc.getFields();
         assertTrue(
                 "Array of fields should be of length 2 but is instead of length: "
@@ -92,10 +92,10 @@
      * java.io.ObjectStreamClass#getName()
      */
     public void test_getName() {
-        ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookup(FakeClass.class);
         assertEquals(
                 "getName returned incorrect name: " + osc.getName(),
-                "org.apache.harmony.tests.java.io.ObjectStreamClassTest$DummyClass",
+                "org.apache.harmony.tests.java.io.ObjectStreamClassTest$FakeClass",
                 osc.getName());
     }
 
@@ -103,10 +103,10 @@
      * java.io.ObjectStreamClass#getSerialVersionUID()
      */
     public void test_getSerialVersionUID() {
-        ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookup(FakeClass.class);
         assertTrue("getSerialversionUID returned incorrect uid: "
                 + osc.getSerialVersionUID() + " instead of "
-                + DummyClass.getUID(), osc.getSerialVersionUID() == DummyClass
+                + FakeClass.getUID(), osc.getSerialVersionUID() == FakeClass
                 .getUID());
     }
 
@@ -128,10 +128,10 @@
      * java.io.ObjectStreamClass#lookup(java.lang.Class)
      */
     public void test_lookupLjava_lang_Class() {
-        ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookup(FakeClass.class);
         assertEquals(
                 "lookup returned wrong class: " + osc.getName(),
-                "org.apache.harmony.tests.java.io.ObjectStreamClassTest$DummyClass",
+                "org.apache.harmony.tests.java.io.ObjectStreamClassTest$FakeClass",
                 osc.getName());
     }
 
@@ -139,7 +139,7 @@
      * java.io.ObjectStreamClass#toString()
      */
     public void test_toString() {
-        ObjectStreamClass osc = ObjectStreamClass.lookup(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookup(FakeClass.class);
         String oscString = osc.toString();
 
         // The previous test was more specific than the spec so it was replaced
@@ -213,9 +213,9 @@
     public void test_lookupAnyLjava_lang_Class() {
         // Test for method java.io.ObjectStreamClass
         // java.io.ObjectStreamClass.lookupAny(java.lang.Class)
-        ObjectStreamClass osc = ObjectStreamClass.lookupAny(DummyClass.class);
+        ObjectStreamClass osc = ObjectStreamClass.lookupAny(FakeClass.class);
         assertEquals("lookup returned wrong class: " + osc.getName(),
-                "org.apache.harmony.tests.java.io.ObjectStreamClassTest$DummyClass", osc
+                "org.apache.harmony.tests.java.io.ObjectStreamClassTest$FakeClass", osc
                 .getName());
 
         osc = ObjectStreamClass.lookupAny(NonSerialzableClass.class);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamFieldTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamFieldTest.java
index 8dd12a4..a5feed1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamFieldTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/io/ObjectStreamFieldTest.java
@@ -33,7 +33,7 @@
 
 public class ObjectStreamFieldTest extends junit.framework.TestCase {
 
-    static class DummyClass implements Serializable {
+    static class FakeClass implements Serializable {
         private static final long serialVersionUID = 999999999999998L;
 
         long bam = 999L;
@@ -245,7 +245,7 @@
      * is called before a test is executed.
      */
     protected void setUp() {
-        osc = ObjectStreamClass.lookup(DummyClass.class);
+        osc = ObjectStreamClass.lookup(FakeClass.class);
         bamField = osc.getField("bam");
         samField = osc.getField("sam");
         hamField = osc.getField("ham");
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ObjectTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ObjectTest.java
index 9dca8e9..f8cff0d 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ObjectTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ObjectTest.java
@@ -16,34 +16,52 @@
  */
 package org.apache.harmony.tests.java.lang;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class ObjectTest extends junit.framework.TestCase {
 
-    /**
-     * Test objects.
-     */
-    Object obj1 = new Object();
+    final Object lock = new Object();
 
-    Object obj2 = new Object();
-
-    /**
-     * Generic state indicator.
-     */
-    int status = 0;
-
+    // Helpers for test_notify() and test_notifyAll(). Access is guarded by {@code lock}'s monitor.
     int ready = 0;
+    int finished = 0;
+    int outstandingNotifications = 0;
+    int spuriousNotifications = 0;
+    Throwable backgroundException;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        synchronized (lock) {
+            backgroundException = null;
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        synchronized (lock) {
+            if (backgroundException != null) {
+                fail("Encountered " + backgroundException);
+            }
+        }
+        super.tearDown();
+    }
 
     /**
      * java.lang.Object#Object()
      */
-    public void test_Constructor() {
+    public void test_constructor() {
         // Test for method java.lang.Object()
-        assertNotNull("Constructor failed !!!", new Object());
+        assertNotNull("Constructor failed", new Object());
     }
 
     /**
      * java.lang.Object#equals(java.lang.Object)
      */
     public void test_equalsLjava_lang_Object() {
+        Object obj1 = new Object();
+        Object obj2 = new Object();
         // Test for method boolean java.lang.Object.equals(java.lang.Object)
         assertTrue("Same object should be equal", obj1.equals(obj1));
         assertTrue("Different objects should not be equal", !obj1.equals(obj2));
@@ -54,20 +72,15 @@
      */
     public void test_getClass() throws Exception {
         // Test for method java.lang.Class java.lang.Object.getClass()
-        String classNames[] = { "java.lang.Object", "java.lang.Throwable",
+        String[] classNames = { "java.lang.Object", "java.lang.Throwable",
                 "java.lang.StringBuffer" };
-        Class<?> classToTest = null;
-        Object instanceToTest = null;
-
-        status = 0;
-        for (int i = 0; i < classNames.length; ++i) {
-            classToTest = Class.forName(classNames[i]);
-            instanceToTest = classToTest.newInstance();
-            assertTrue("Instance didn't match creator class.",
-                    instanceToTest.getClass() == classToTest);
-            assertTrue("Instance didn't match class with matching name.",
-                    instanceToTest.getClass() == Class
-                            .forName(classNames[i]));
+        for (String className : classNames) {
+            final Class<?> classToTest = Class.forName(className);
+            final Object instanceToTest = classToTest.newInstance();
+            assertSame("Instance didn't match creator class.", instanceToTest.getClass(),
+                    classToTest);
+            assertSame("Instance didn't match class with matching name.", instanceToTest.getClass(),
+                    Class.forName(className));
         }
     }
 
@@ -76,10 +89,19 @@
      */
     public void test_hashCode() {
         // Test for method int java.lang.Object.hashCode()
-        assertTrue("Same object should have same hash.",
-                obj1.hashCode() == obj1.hashCode());
-        assertTrue("Same object should have same hash.",
-                obj2.hashCode() == obj2.hashCode());
+        Object obj1 = new Object();
+        Object obj2;
+        int origHashCodeForObj1 = obj1.hashCode();
+        // Force obj1 lock inflation.
+        synchronized (obj1) {
+            obj2 = new Object();
+        }
+        assertEquals("Same object should have same hash.", obj1.hashCode(), obj1.hashCode());
+        assertEquals("Lock inflation shouldn't change hash code.",
+                obj1.hashCode(), origHashCodeForObj1);
+        assertEquals("Same object should have same hash.", obj2.hashCode(), obj2.hashCode());
+        Runtime.getRuntime().gc();
+        assertEquals("Gc shouldn't change hash code.", obj1.hashCode(), origHashCodeForObj1);
     }
 
     /**
@@ -87,67 +109,72 @@
      */
     public void test_notify() {
         // Test for method void java.lang.Object.notify()
+        class TestThread extends Thread {
+            public TestThread(String name) {
+                super(name);
+            }
 
-        // Inner class to run test thread.
-        class TestThread implements Runnable {
+            @Override
             public void run() {
-                synchronized (obj1) {
+                synchronized (lock) {
                     try {
-                        ready += 1;
-                        obj1.wait();// Wait for ever.
-                        status += 1;
+                        ready++;
+                        lock.wait(); // Wait to be notified.
+                        while (outstandingNotifications <= 0) {
+                            spuriousNotifications++;
+                            lock.wait();
+                        }
+                        outstandingNotifications--;
                     } catch (InterruptedException ex) {
-                        status = -1000;
+                        backgroundException = ex;
                     }
                 }
             }
         }
-        ;
-
-        // Start of test code.
 
         // Warning:
         // This code relies on each thread getting serviced within
-        // 200 mSec of when it is notified. Although this
+        // 400 msec of when it is notified. Although this
         // seems reasonable, it could lead to false-failures.
 
         ready = 0;
-        status = 0;
+        outstandingNotifications = 0;
+        spuriousNotifications = 0;
         final int readyWaitSecs = 3;
 
         final int threadCount = 20;
+        List<TestThread> threads = new ArrayList<>();
         for (int i = 0; i < threadCount; ++i) {
-            new Thread(new TestThread()).start();
+            TestThread thread = new TestThread("TestThread " + i);
+            threads.add(thread);
+            thread.start();
         }
-        synchronized (obj1) {
+        synchronized (lock) {
             try {
-
                 // Wait up to readyWaitSeconds for all threads to be waiting on
                 // monitor
-                for (int i = 0; i < readyWaitSecs; i++) {
-                    obj1.wait(1000, 0);
+                for (int i = 0; i < 10 * readyWaitSecs; i++) {
+                    lock.wait(100, 0);
                     if (ready == threadCount) {
                         break;
                     }
                 }
-
-                // Check pre-conditions of testing notifyAll
-                assertTrue("Not all launched threads are waiting. (ready = "
-                        + ready + ")", ready == threadCount);
-                assertTrue("Thread woke too early. (status = " + status + ")",
-                        status == 0);
-
+                assertEquals("Not all launched threads are waiting. (ready=" + ready + ")",
+                        ready, threadCount);
                 for (int i = 1; i <= threadCount; ++i) {
-                    obj1.notify();
-                    obj1.wait(200, 0);
-                    assertTrue("Out of sync. (expected " + i + " but got "
-                            + status + ")", status == i);
+                    outstandingNotifications++;
+                    lock.notify();
+                    for (int j = 0; j < 10 && outstandingNotifications > 0; ++j) {
+                        lock.wait(100);  // Sleep for 100 msecs, releasing lock.
+                    }
+                    assertEquals("Notification #" + i + "  took too long to wake a thread.",
+                            0, outstandingNotifications);
+                    // Spurious notifications are allowed, but should be very rare.
+                    assertTrue("Too many spurious notifications: " + spuriousNotifications,
+                            spuriousNotifications <= 1);
                 }
-
             } catch (InterruptedException ex) {
-                fail(
-                        "Unexpectedly got an InterruptedException. (status = "
-                                + status + ")");
+                fail("Unexpectedly got an InterruptedException.");
             }
         }
     }
@@ -161,13 +188,13 @@
         // Inner class to run test thread.
         class TestThread implements Runnable {
             public void run() {
-                synchronized (obj1) {
+                synchronized (lock) {
                     try {
                         ready += 1;
-                        obj1.wait();// Wait for ever.
-                        status += 1;
+                        lock.wait();// Wait forever.
+                        finished += 1;
                     } catch (InterruptedException ex) {
-                        status = -1000;
+                        backgroundException = ex;
                     }
                 }
             }
@@ -181,45 +208,43 @@
         // 5 seconds of when they are notified. Although this
         // seems reasonable, it could lead to false-failures.
 
-        status = 0;
         ready = 0;
+        finished = 0;
         final int readyWaitSecs = 3;
+        final int finishedWaitSecs = 5;
         final int threadCount = 20;
         for (int i = 0; i < threadCount; ++i) {
             new Thread(new TestThread()).start();
         }
 
-        synchronized (obj1) {
+        synchronized (lock) {
 
             try {
-
                 // Wait up to readyWaitSeconds for all threads to be waiting on
                 // monitor
                 for (int i = 0; i < readyWaitSecs; i++) {
-                    obj1.wait(1000, 0);
+                    lock.wait(1000, 0);
                     if (ready == threadCount) {
                         break;
                     }
                 }
 
                 // Check pre-conditions of testing notifyAll
-                assertTrue("Not all launched threads are waiting. (ready = "
-                        + ready + ")", ready == threadCount);
-                assertTrue("At least one thread woke too early. (status = "
-                        + status + ")", status == 0);
+                assertEquals("Not all launched threads are waiting.", threadCount, ready);
+                // This assumes no spurious wakeups. If we ever see any, we might check for at
+                // most one finished thread instead.
+                assertEquals("At least one thread woke too early.", 0, finished);
 
-                obj1.notifyAll();
+                lock.notifyAll();
 
-                obj1.wait(5000, 0);
+                for (int i = 0; finished < threadCount && i < finishedWaitSecs; ++i) {
+                  lock.wait(1000, 0);
+                }
 
-                assertTrue(
-                        "At least one thread did not get notified. (status = "
-                                + status + ")", status == threadCount);
+                assertEquals("At least one thread did not get notified.", threadCount, finished);
 
             } catch (InterruptedException ex) {
-                fail(
-                        "Unexpectedly got an InterruptedException. (status = "
-                                + status + ")");
+                fail("Unexpectedly got an InterruptedException. (finished = " + finished + ")");
             }
 
         }
@@ -230,7 +255,7 @@
      */
     public void test_toString() {
         // Test for method java.lang.String java.lang.Object.toString()
-        assertNotNull("Object toString returned null.", obj1.toString());
+        assertNotNull("Object toString returned null.", lock.toString());
     }
 
     /**
@@ -240,19 +265,23 @@
         // Test for method void java.lang.Object.wait()
 
         // Inner class to run test thread.
-        class TestThread implements Runnable {
+        class TestThread extends Thread {
+            int status;
+
             public void run() {
-                synchronized (obj1) {
+                synchronized (lock) {
                     try {
-                        obj1.wait();// Wait for ever.
+                        do {
+                            lock.wait(); // Wait to be notified.
+                        } while (outstandingNotifications <= 0);
+                        outstandingNotifications--;
                         status = 1;
                     } catch (InterruptedException ex) {
-                        status = -1;
+                        backgroundException = ex;
                     }
                 }
             }
         }
-        ;
 
         // Start of test code.
 
@@ -261,21 +290,23 @@
         // 1 second of when they are notified. Although this
         // seems reasonable, it could lead to false-failures.
 
-        status = 0;
-        new Thread(new TestThread()).start();
-        synchronized (obj1) {
+        TestThread thread = new TestThread();
+        synchronized (lock) {
+            thread.status = 0;
+        }
+        thread.start();
+        synchronized (lock) {
             try {
-                obj1.wait(1000, 0);
-                assertTrue("Thread woke too early. (status = " + status + ")",
-                        status == 0);
-                obj1.notifyAll();
-                obj1.wait(1000, 0);
-                assertTrue("Thread did not get notified. (status = " + status
-                        + ")", status == 1);
+                lock.wait(1000, 0);
+                assertEquals("Thread woke too early. (status=" + thread.status + ")",
+                        0, thread.status);
+                outstandingNotifications = 1;
+                lock.notifyAll();
+                lock.wait(1000, 0);
+                assertEquals("Thread did not get notified. (status=" + thread.status + ")",
+                        1, thread.status);
             } catch (InterruptedException ex) {
-                fail(
-                        "Unexpectedly got an InterruptedException. (status = "
-                                + status + ")");
+                fail("Unexpectedly got an InterruptedException. (status=" + thread.status + ")");
             }
         }
     }
@@ -289,15 +320,15 @@
         // Start of test code.
 
         final int loopCount = 20;
-        final int allowableError = 100; // millesconds
+        final int allowableError = 100; // milliseconds
         final int delay = 200; // milliseconds
-        synchronized (obj1) {
+        synchronized (lock) {
             try {
                 int count = 0;
                 long[][] toLong = new long[3][3];
                 for (int i = 0; i < loopCount; ++i) {
                     long before = System.currentTimeMillis();
-                    obj1.wait(delay, 0);
+                    lock.wait(delay, 0);
                     long after = System.currentTimeMillis();
                     long error = (after - before - delay);
                     if (error < 0)
@@ -311,10 +342,9 @@
                             count++;
                         }
                         if (error > (1000 + delay) || count == toLong.length) {
-                            StringBuffer sb = new StringBuffer();
+                            StringBuilder sb = new StringBuilder();
                             for (int j = 0; j < count; j++) {
-                                sb
-                                        .append("wakeup time too inaccurate, iteration ");
+                                sb.append("wakeup time too inaccurate, iteration ");
                                 sb.append(toLong[j][0]);
                                 sb.append(", before: ");
                                 sb.append(toLong[j][1]);
@@ -329,9 +359,7 @@
                     }
                 }
             } catch (InterruptedException ex) {
-                fail(
-                        "Unexpectedly got an InterruptedException. (status = "
-                                + status + ")");
+                fail("Unexpectedly got an InterruptedException.");
             }
         }
     }
@@ -343,21 +371,26 @@
         // Test for method void java.lang.Object.wait(long, int)
 
         // Inner class to run test thread.
-        class TestThread implements Runnable {
+        class TestThread extends Thread {
+            int status;
+
+            @Override
             public void run() {
-                synchronized (obj1) {
+                synchronized (lock) {
                     try {
-                        obj1.wait(0, 1); // Don't wait very long.
+                        lock.wait(0, 1); // Don't wait very long.
                         status = 1;
-                        obj1.wait(0, 0); // Wait for ever.
+                        do {
+                            lock.wait(0, 0); // Wait to be notified.
+                        } while (outstandingNotifications <= 0);
+                        outstandingNotifications--;
                         status = 2;
                     } catch (InterruptedException ex) {
-                        status = -1;
+                        backgroundException = ex;
                     }
                 }
             }
         }
-        ;
 
         // Start of test code.
 
@@ -366,23 +399,25 @@
         // 1 second of when they are notified. Although this
         // seems reasonable, it could lead to false-failures.
 
-        status = 0;
-        new Thread(new TestThread()).start();
-        synchronized (obj1) {
+        TestThread thread = new TestThread();
+        synchronized (lock) {
+            thread.status = 0;
+        }
+        thread.start();
+        synchronized (lock) {
             try {
-                obj1.wait(1000, 0);
-                assertTrue("Thread did not wake after 1 ms. (status = "
-                        + status + ")", status == 1);
-                obj1.notifyAll();
-                obj1.wait(1000, 0);
-                assertTrue("Thread did not get notified. (status = " + status
-                        + ")", status == 2);
+                lock.wait(1000, 0);
+                assertEquals("Thread did not wake after 1sec. (status=" + thread.status + ")",
+                        1, thread.status);
+                outstandingNotifications++;
+                lock.notifyAll();
+                lock.wait(1000, 0);
+                assertEquals("Thread did not get notified. (status=" +
+                        thread.status + ")", 2, thread.status);
             } catch (InterruptedException ex) {
-                fail(
-                        "Unexpectedly got an InterruptedException. (status = "
-                                + status + ")");
+                fail("Unexpectedly got an InterruptedException. (status = " +
+                        thread.status + ")");
             }
         }
-
     }
 }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadTest.java
index 5819dd3..2fc03c5 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ThreadTest.java
@@ -707,7 +707,7 @@
         long secondRead = System.currentTimeMillis();
         assertTrue("Did not join by appropriate time: " + secondRead + "-"
                 + firstRead + "=" + (secondRead - firstRead), secondRead
-                - firstRead <= 300);
+                - firstRead <= 400);
         assertTrue("Joined thread is not alive", st.isAlive());
         st.interrupt();
 
@@ -818,8 +818,6 @@
      */
     public void test_sleepJ() {
         // Test for method void java.lang.Thread.sleep(long)
-
-        // TODO : Test needs enhancing.
         long stime = 0, ftime = 0;
         try {
             stime = System.currentTimeMillis();
@@ -828,7 +826,12 @@
         } catch (InterruptedException e) {
             fail("Unexpected interrupt received");
         }
-        assertTrue("Failed to sleep long enough", (ftime - stime) >= 800);
+        long elapsedMillis = ftime - stime;
+        // We shouldn't ever wake up early, but there might be some rounding:
+        assertTrue("Failed to sleep long enough: " + elapsedMillis,
+            elapsedMillis >= 998);
+        // Scheduling delays can make us appear to sleep quite a bit longer:
+        assertTrue("Overslept: " + elapsedMillis, elapsedMillis <= 1300);
     }
 
     /**
@@ -836,8 +839,6 @@
      */
     public void test_sleepJI() {
         // Test for method void java.lang.Thread.sleep(long, int)
-
-        // TODO : Test needs revisiting.
         long stime = 0, ftime = 0;
         try {
             stime = System.currentTimeMillis();
@@ -846,9 +847,10 @@
         } catch (InterruptedException e) {
             fail("Unexpected interrupt received");
         }
-        long result = ftime - stime;
-        assertTrue("Failed to sleep long enough: " + result, result >= 900
-                && result <= 1100);
+        long elapsedMillis = ftime - stime;
+        assertTrue("Failed to sleep long enough: " + elapsedMillis,
+            elapsedMillis >= 999);
+        assertTrue("Overslept: " + elapsedMillis, elapsedMillis <= 1300);
     }
 
     /**
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceTest.java
index 7a3496e..19ccf03 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/lang/ref/ReferenceTest.java
@@ -83,6 +83,63 @@
     }
 
     /**
+     * java.lang.ref.Reference#refersTo()
+     */
+    public void test_refersTo() {
+        tmpA = new Object();
+        tmpB = new Object();
+        tmpC = new Object();
+        final SoftReference sr = new SoftReference(tmpA, new ReferenceQueue());
+        final WeakReference wr = new WeakReference(tmpB, new ReferenceQueue());
+        final PhantomReference pr = new PhantomReference(tmpC, new ReferenceQueue());
+        assertTrue("soft refersTo(referent)", sr.refersTo(tmpA));
+        assertTrue("weak refersTo(referent)", wr.refersTo(tmpB));
+        assertTrue("phantom refersTo(referent)", pr.refersTo(tmpC));
+        assertFalse("soft refersTo(other)", sr.refersTo(tmpC));
+        assertFalse("weak refersTo(other)", wr.refersTo(tmpA));
+        assertFalse("phantom refersTo(other)", pr.refersTo(tmpB));
+        sr.clear();
+        wr.clear();
+        pr.clear();
+        assertTrue("soft refersTo(null)", sr.refersTo(null));
+        assertTrue("weak refersTo(null)", wr.refersTo(null));
+        assertTrue("phantom refersTo(null)", pr.refersTo(null));
+        assertFalse("soft refersTo(nonnull)", sr.refersTo(tmpA));
+        assertFalse("weak refersTo(nonnull)", wr.refersTo(tmpB));
+        assertFalse("phantom refersTo(nonnull)", pr.refersTo(tmpC));
+
+        // Check that refersTo remains consistent during GC.
+        final WeakReference wr2 = new WeakReference(tmpB, new ReferenceQueue());
+        Thread checker = new Thread(() -> {
+            while (wr2.get() == tmpB && wr2.refersTo(tmpB)
+                    && !wr2.refersTo(null) && !wr2.refersTo(tmpA)) {}
+            disappeared = true;
+        });
+        checker.start();
+        for (int i = 0; i < 5; ++i) {
+            Runtime.getRuntime().gc();
+            System.runFinalization();
+            assertFalse("WeakReference prematurely inaccessible", disappeared);
+        }
+        wr2.clear();
+        for (int i = 0; i < 5; ++i) {
+            Runtime.getRuntime().gc();
+            if (disappeared) {
+                break;
+            }
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException e) {
+                fail("InterruptedException : " + e.getMessage());
+            }
+        }
+        assertTrue("WeakReference remained accessible", disappeared);
+        assertTrue("refersTo(null) failed", wr2.refersTo(null) && !wr2.refersTo(tmpB));
+    }
+
+    private volatile boolean disappeared = false;
+
+    /**
      * java.lang.ref.Reference#enqueue()
      */
     public void test_enqueue() {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigIntegerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigIntegerTest.java
index b9ee1c6..cc64946 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigIntegerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/math/BigIntegerTest.java
@@ -81,13 +81,8 @@
 	 * @tests java.math.BigInteger#BigInteger(int, java.util.Random)
 	 */
 	public void test_ConstructorILjava_util_Random() {
-        // regression test for HARMONY-1047
-		try {
-			new BigInteger(Integer.MAX_VALUE, (Random)null);
-			fail("NegativeArraySizeException expected");
-		} catch (NegativeArraySizeException e) {
-            // PASSED
-		}
+        // regression test for HARMONY-1047 removed. We were failing this supposed test for RI
+        // behavior in spite of running their code.
 		
 		bi = new BigInteger(70, rand);
 		bi2 = new BigInteger(70, rand);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
index a24a67b..dab784a 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/MulticastSocketTest.java
@@ -17,6 +17,13 @@
 
 package org.apache.harmony.tests.java.net;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.net.BindException;
 import java.net.DatagramPacket;
@@ -32,12 +39,16 @@
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
-import libcore.junit.junit3.TestCaseWithRules;
+
 import libcore.junit.util.ResourceLeakageDetector;
+
+import org.junit.Assume;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.rules.TestRule;
+import org.junit.Test;
 
-public class MulticastSocketTest extends TestCaseWithRules {
+public class MulticastSocketTest {
     @Rule
     public TestRule guardRule = ResourceLeakageDetector.getRule();
 
@@ -63,10 +74,10 @@
     private NetworkInterface ipv6NetworkInterface;
     private boolean supportsMulticast;
 
-    @Override
-    protected void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         // The loopback interface isn't actually useful for sending/receiving multicast messages
-        // but it can be used as a dummy for tests where that does not matter.
+        // but it can be used as a fake for tests where that does not matter.
         loopbackInterface = NetworkInterface.getByInetAddress(InetAddress.getLoopbackAddress());
         assertNotNull(loopbackInterface);
         assertTrue(loopbackInterface.isLoopback());
@@ -79,9 +90,7 @@
         // set we assume the device has an interface capable of supporting multicast.
         supportsMulticast = Boolean.parseBoolean(
                 System.getProperty("android.cts.device.multicast", "true"));
-        if (!supportsMulticast) {
-            return;
-        }
+        Assume.assumeTrue(supportsMulticast);
 
         while (interfaces.hasMoreElements()
                 && (ipv4NetworkInterface == null || ipv6NetworkInterface == null)) {
@@ -104,10 +113,9 @@
                 ipv4NetworkInterface != null && ipv6NetworkInterface != null);
     }
 
-    public void test_Constructor() throws IOException {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void constructor() throws IOException {
+        Assume.assumeTrue(supportsMulticast);
         // Regression test for 497.
         MulticastSocket s = new MulticastSocket();
         // Regression test for Harmony-1162.
@@ -116,10 +124,9 @@
         s.close();
     }
 
-    public void test_ConstructorI() throws IOException {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void constructorI() throws IOException {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket orig = new MulticastSocket();
         int port = orig.getLocalPort();
         orig.close();
@@ -130,10 +137,9 @@
         dup.close();
     }
 
-    public void test_getInterface() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void getInterface() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         // Validate that we get the expected response when one was not set.
         MulticastSocket mss = new MulticastSocket(0);
         // We expect an ANY address in this case.
@@ -157,10 +163,9 @@
         mss.close();
     }
 
-    public void test_getNetworkInterface() throws IOException {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void getNetworkInterface() throws IOException {
+        Assume.assumeTrue(supportsMulticast);
         // Validate that we get the expected response when one was not set.
         MulticastSocket mss = new MulticastSocket(0);
         NetworkInterface theInterface = mss.getNetworkInterface();
@@ -202,10 +207,9 @@
         mss.close();
     }
 
-    public void test_getTimeToLive() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void getTimeToLive() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket();
         mss.setTimeToLive(120);
         assertEquals("Returned incorrect 1st TTL", 120, mss.getTimeToLive());
@@ -214,27 +218,24 @@
         mss.close();
     }
 
-    public void test_getTTL() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void getTTL() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket();
         mss.setTTL((byte) 120);
         assertEquals("Returned incorrect TTL", 120, mss.getTTL());
         mss.close();
     }
 
-    public void test_joinGroupLjava_net_InetAddress_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void joinGroupLjava_net_InetAddress_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         test_joinGroupLjava_net_InetAddress(GOOD_IPv4);
     }
 
-    public void test_joinGroupLjava_net_InetAddress_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void joinGroupLjava_net_InetAddress_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         test_joinGroupLjava_net_InetAddress(GOOD_IPv6);
     }
 
@@ -258,10 +259,9 @@
         receivingSocket.close();
     }
 
-    public void test_joinGroup_null_null() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void joinGroup_null_null() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.joinGroup(null, null);
@@ -271,10 +271,9 @@
         mss.close();
     }
 
-    public void test_joinGroup_non_multicast_address_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void joinGroup_non_multicast_address_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.joinGroup(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0), null);
@@ -284,10 +283,9 @@
         mss.close();
     }
 
-    public void test_joinGroup_non_multicast_address_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void joinGroup_non_multicast_address_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.joinGroup(new InetSocketAddress(InetAddress.getByName("::1"), 0), null);
@@ -297,35 +295,35 @@
         mss.close();
     }
 
-    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4()
+    @Test
+    public void joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4()
             throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
+        Assume.assumeTrue(supportsMulticast);
+        check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 ipv4NetworkInterface, GOOD_IPv4, BAD_IPv4);
     }
 
-    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6()
+    @Test
+    public void joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6()
             throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
+        Assume.assumeTrue(supportsMulticast);
+        check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 ipv6NetworkInterface, GOOD_IPv6, BAD_IPv6);
     }
 
-    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4_nullInterface()
+    @Test
+    public void joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4_nullInterface()
             throws Exception {
-        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(null, GOOD_IPv4, BAD_IPv4);
+        check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(null, GOOD_IPv4, BAD_IPv4);
     }
 
-    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6_nullInterface()
+    @Test
+    public void joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6_nullInterface()
             throws Exception {
-        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(null, GOOD_IPv6, BAD_IPv6);
+        check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(null, GOOD_IPv6, BAD_IPv6);
     }
 
-    private void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
+    private void check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
             NetworkInterface networkInterface, InetAddress group, InetAddress group2)
             throws Exception {
         // Create the sending socket and specify the interface to use as needed (otherwise use the
@@ -383,10 +381,9 @@
         sendingSocket.close();
     }
 
-    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         // Check that we can join on specific interfaces and that we only receive if data is
         // received on that interface. This test is only really useful on devices with multiple
         // non-loopback interfaces.
@@ -453,25 +450,23 @@
         }
     }
 
-    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv4()
+    @Test
+    public void joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv4()
             throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
+        Assume.assumeTrue(supportsMulticast);
+        check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
                 ipv4NetworkInterface, GOOD_IPv4);
     }
 
-    public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv6()
+    @Test
+    public void joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins_IPv6()
             throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
+        Assume.assumeTrue(supportsMulticast);
+        check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
                 ipv6NetworkInterface, GOOD_IPv6);
     }
 
-    private void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
+    private void check_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface_multiple_joins(
             NetworkInterface networkInterface, InetAddress group) throws Exception {
         // Validate that we can join the same address on two different interfaces but not on the
         // same interface.
@@ -487,21 +482,19 @@
         mss.close();
     }
 
-    public void test_leaveGroupLjava_net_InetAddress_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_leaveGroupLjava_net_InetAddress(GOOD_IPv4);
+    @Test
+    public void leaveGroupLjava_net_InetAddress_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_leaveGroupLjava_net_InetAddress(GOOD_IPv4);
     }
 
-    public void test_leaveGroupLjava_net_InetAddress_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_leaveGroupLjava_net_InetAddress(GOOD_IPv6);
+    @Test
+    public void leaveGroupLjava_net_InetAddress_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_leaveGroupLjava_net_InetAddress(GOOD_IPv6);
     }
 
-    private void test_leaveGroupLjava_net_InetAddress(InetAddress group) throws Exception {
+    private void check_leaveGroupLjava_net_InetAddress(InetAddress group) throws Exception {
         String msg = "Hello World";
         MulticastSocket mss = new MulticastSocket(0);
         InetSocketAddress groupAddress = new InetSocketAddress(group, mss.getLocalPort());
@@ -516,10 +509,9 @@
         mss.close();
     }
 
-    public void test_leaveGroup_null_null() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void leaveGroup_null_null() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.leaveGroup(null, null);
@@ -529,10 +521,9 @@
         mss.close();
     }
 
-    public void test_leaveGroup_non_multicast_address_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void leaveGroup_non_multicast_address_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.leaveGroup(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0), null);
@@ -542,10 +533,9 @@
         mss.close();
     }
 
-    public void test_leaveGroup_non_multicast_address_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void leaveGroup_non_multicast_address_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket(0);
         try {
             mss.leaveGroup(new InetSocketAddress(InetAddress.getByName("::1"), 0), null);
@@ -555,25 +545,23 @@
         mss.close();
     }
 
-    public void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4()
+    @Test
+    public void leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv4()
             throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
+        Assume.assumeTrue(supportsMulticast);
+        check_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 ipv4NetworkInterface, GOOD_IPv4, BAD_IPv4);
     }
 
-    public void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6()
+    @Test
+    public void leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface_IPv6()
             throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
+        Assume.assumeTrue(supportsMulticast);
+        check_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
                 ipv6NetworkInterface, GOOD_IPv6, BAD_IPv6);
     }
 
-    private void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
+    private void check_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface(
             NetworkInterface networkInterface, InetAddress group, InetAddress group2)
             throws Exception {
         SocketAddress groupSockAddr = null;
@@ -609,21 +597,19 @@
         }
     }
 
-    public void test_sendLjava_net_DatagramPacketB_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_sendLjava_net_DatagramPacketB(GOOD_IPv4);
+    @Test
+    public void sendLjava_net_DatagramPacketB_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_sendLjava_net_DatagramPacketB(GOOD_IPv4);
     }
 
-    public void test_sendLjava_net_DatagramPacketB_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_sendLjava_net_DatagramPacketB(GOOD_IPv6);
+    @Test
+    public void sendLjava_net_DatagramPacketB_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_sendLjava_net_DatagramPacketB(GOOD_IPv6);
     }
 
-    private void test_sendLjava_net_DatagramPacketB(InetAddress group) throws Exception {
+    private void check_sendLjava_net_DatagramPacketB(InetAddress group) throws Exception {
         String msg = "Hello World";
         MulticastSocket sendingSocket = new MulticastSocket(0);
         MulticastSocket receivingSocket = createReceivingSocket(sendingSocket.getLocalPort());
@@ -641,10 +627,9 @@
         receivingSocket.close();
     }
 
-    public void test_setInterfaceLjava_net_InetAddress() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setInterfaceLjava_net_InetAddress() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket();
         mss.setInterface(InetAddress.getLocalHost());
         InetAddress theInterface = mss.getInterface();
@@ -662,17 +647,15 @@
         mss.close();
     }
 
-    public void test_setInterface_unbound_address_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setInterface_unbound_address_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         test_setInterface_unbound_address(GOOD_IPv4);
     }
 
-    public void test_setInterface_unbound_address_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setInterface_unbound_address_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         test_setInterface_unbound_address(GOOD_IPv6);
     }
 
@@ -687,10 +670,9 @@
         mss.close();
     }
 
-    public void test_setNetworkInterfaceLjava_net_NetworkInterface_null() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setNetworkInterfaceLjava_net_NetworkInterface_null() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         // Validate that null interface is handled ok.
         MulticastSocket mss = new MulticastSocket();
         try {
@@ -701,10 +683,9 @@
         mss.close();
     }
 
-    public void test_setNetworkInterfaceLjava_net_NetworkInterface_round_trip() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setNetworkInterfaceLjava_net_NetworkInterface_round_trip() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         // Validate that we can get and set the interface.
         MulticastSocket mss = new MulticastSocket();
         mss.setNetworkInterface(ipv4NetworkInterface);
@@ -713,21 +694,19 @@
         mss.close();
     }
 
-    public void test_setNetworkInterfaceLjava_net_NetworkInterface_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_setNetworkInterfaceLjava_net_NetworkInterface(GOOD_IPv4);
+    @Test
+    public void setNetworkInterfaceLjava_net_NetworkInterface_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_setNetworkInterfaceLjava_net_NetworkInterface(GOOD_IPv4);
     }
 
-    public void test_setNetworkInterfaceLjava_net_NetworkInterface_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_setNetworkInterfaceLjava_net_NetworkInterface(GOOD_IPv6);
+    @Test
+    public void setNetworkInterfaceLjava_net_NetworkInterface_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_setNetworkInterfaceLjava_net_NetworkInterface(GOOD_IPv6);
     }
 
-    private void test_setNetworkInterfaceLjava_net_NetworkInterface(InetAddress group)
+    private void check_setNetworkInterfaceLjava_net_NetworkInterface(InetAddress group)
             throws IOException, InterruptedException {
         // Set up the receiving socket and join the group.
         Enumeration theInterfaces = NetworkInterface.getNetworkInterfaces();
@@ -761,10 +740,9 @@
         }
     }
 
-    public void test_setTimeToLiveI() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setTimeToLiveI() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket();
         mss.setTimeToLive(120);
         assertEquals("Returned incorrect 1st TTL", 120, mss.getTimeToLive());
@@ -773,20 +751,18 @@
         mss.close();
     }
 
-    public void test_setTTLB() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setTTLB() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket mss = new MulticastSocket();
         mss.setTTL((byte) 120);
         assertEquals("Failed to set TTL", 120, mss.getTTL());
         mss.close();
     }
 
-    public void test_ConstructorLjava_net_SocketAddress() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void constructorLjava_net_SocketAddress() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket ms = new MulticastSocket((SocketAddress) null);
         assertTrue("should not be bound", !ms.isBound() && !ms.isClosed() && !ms.isConnected());
         ms.bind(null);
@@ -817,10 +793,9 @@
         s.close();
     }
 
-    public void test_getLoopbackMode() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void getLoopbackMode() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket ms = new MulticastSocket(null);
         assertTrue("should not be bound", !ms.isBound() && !ms.isClosed() && !ms.isConnected());
         ms.getLoopbackMode();
@@ -829,10 +804,9 @@
         assertTrue("should be closed", ms.isClosed());
     }
 
-    public void test_setLoopbackModeZ() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setLoopbackModeZ() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         MulticastSocket ms = new MulticastSocket();
         ms.setLoopbackMode(true);
         assertTrue("loopback should be true", ms.getLoopbackMode());
@@ -842,21 +816,19 @@
         assertTrue("should be closed", ms.isClosed());
     }
 
-    public void test_setLoopbackModeSendReceive_IPv4() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_setLoopbackModeSendReceive(GOOD_IPv4);
+    @Test
+    public void setLoopbackModeSendReceive_IPv4() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_setLoopbackModeSendReceive(GOOD_IPv4);
     }
 
-    public void test_setLoopbackModeSendReceive_IPv6() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
-        test_setLoopbackModeSendReceive(GOOD_IPv6);
+    @Test
+    public void setLoopbackModeSendReceive_IPv6() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
+        check_setLoopbackModeSendReceive(GOOD_IPv6);
     }
 
-    private void test_setLoopbackModeSendReceive(InetAddress group) throws IOException {
+    private void check_setLoopbackModeSendReceive(InetAddress group) throws IOException {
         // Test send receive.
         final String message = "Hello, world!";
 
@@ -878,10 +850,9 @@
         socket.close();
     }
 
-    public void test_setReuseAddressZ() throws Exception {
-        if (!supportsMulticast) {
-            return;
-        }
+    @Test
+    public void setReuseAddressZ() throws Exception {
+        Assume.assumeTrue(supportsMulticast);
         // Test case were we to set ReuseAddress to false.
         MulticastSocket theSocket1 = new MulticastSocket(null);
         theSocket1.setReuseAddress(false);
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java
index 68122b2..a7a70d1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/net/SocketTest.java
@@ -1196,7 +1196,7 @@
          * that do not support urgent data
          */
         String platform = System.getProperty("os.name");
-        if (platform.equals("Dummy")) {
+        if (platform.equals("Fake")) {
             return;
         }
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/DoubleBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/DoubleBufferTest.java
index 6ae5eb4..dafcff8 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/DoubleBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/DoubleBufferTest.java
@@ -57,7 +57,7 @@
             long longBitsIn = nans[i];
             double dbl = Double.longBitsToDouble(longBitsIn);
             long longBitsOut = Double.doubleToRawLongBits(dbl);
-            // Sanity check
+            // Confidence check
             assertTrue(longBitsIn == longBitsOut);
 
             // Store the double and retrieve it
@@ -213,7 +213,7 @@
         double positiveZero = Double.parseDouble("+0");
         DoubleBuffer negativeZeroBuffer = DoubleBuffer.wrap(new double[] { negativeZero });
         DoubleBuffer positiveZeroBuffer = DoubleBuffer.wrap(new double[] { positiveZero });
-        assertTrue(Double.compare(negativeZero, positiveZero) < 0); // sanity check
+        assertTrue(Double.compare(negativeZero, positiveZero) < 0); // confidence check
 
         // Unlike Double.compare(), DoubleBuffer.compareTo() considers -0 == +0
         assertEquals(0, negativeZeroBuffer.compareTo(positiveZeroBuffer));
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatTest.java
index 884185a..6a26c55 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DateFormatTest.java
@@ -21,9 +21,12 @@
 import java.text.NumberFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.Locale;
+import java.util.Set;
 
 public class DateFormatTest extends junit.framework.TestCase {
 
@@ -42,18 +45,21 @@
 	 * @tests java.text.DateFormat#getAvailableLocales()
 	 */
 	public void test_getAvailableLocales() {
+		// Locale.getAvailableLocales() and DateFormat.getAvailableLocales() should have
+		// the same set of locales.
+		Set<Locale> expectedLocales = new HashSet<>(Arrays.asList(
+				Locale.getAvailableLocales()));
 		Locale[] locales = DateFormat.getAvailableLocales();
 		assertTrue("No locales", locales.length > 0);
+		assertEquals(expectedLocales.size(), locales.length);
 		boolean english = false, german = false;
-		for (int i = locales.length; --i >= 0;) {
-			if (locales[i].equals(Locale.ENGLISH))
+		for (Locale locale : locales) {
+			assertTrue("Locale.getAvailableLocales() doesn't have the locale " + locale,
+					expectedLocales.contains(locale));
+			if (locale.equals(Locale.ENGLISH))
 				english = true;
-			if (locales[i].equals(Locale.GERMAN))
+			if (locale.equals(Locale.GERMAN))
 				german = true;
-			DateFormat f1 = DateFormat.getDateTimeInstance(DateFormat.SHORT,
-					DateFormat.SHORT, locales[i]);
-			assertTrue("Doesn't work",
-					f1.format(new Date()).getClass() == String.class);
 		}
 		assertTrue("Missing locales", english && german);
 	}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
index 8b36163..15ce7cb 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/text/DecimalFormatTest.java
@@ -1664,11 +1664,11 @@
         // digits. This shows the value actually being tested.
         String testId = "decimalValue: " + new BigDecimal(d);
 
-        // As a sanity check we try out parseDouble() with the string generated by
+        // As a confidence check we try out parseDouble() with the string generated by
         // Double.toString(). Strictly speaking Double.toString() is probably not guaranteed to be
         // lossless, but in reality it probably is, or at least is close enough.
         assertDoubleEqual(
-                testId + " failed parseDouble(toString()) sanity check",
+                testId + " failed parseDouble(toString()) confidence check",
                 d, Double.parseDouble(Double.toString(d)));
 
         // Format the number: If this is lossy it is a problem. We are trying to check that it
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java
index ec065a9..2629c06 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CurrencyTest.java
@@ -235,7 +235,8 @@
         String[] dollar  = new String[] {"USD", "$", "US$", "$US", "$ US"};
         // BEGIN Android-changed
         // Starting CLDR 1.7 release, currency symbol for CAD changed to CA$ in some locales such as ja.
-        String[] cDollar = new String[] {"CA$", "CAD", "$", "Can$", "$CA"};
+        // Starting CLDR 38 release, currency symbol for CAD changed to $ CA in some locales such as ja-JP.
+        String[] cDollar = new String[] {"CA$", "CAD", "$", "Can$", "$CA", "$ CA"};
         // END Android-changed
 
         Currency currE   = Currency.getInstance("EUR");
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java
index 33b17c4..46365e1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java
@@ -711,22 +711,22 @@
             testTask = new TimerTestTask();
             testTask.incrementCount(true);
             d = new Date(System.currentTimeMillis() + 100);
-            t.schedule(testTask, d, 100); // at least 9 times
-            testTask = new TimerTestTask();
-            testTask.incrementCount(true);
-            d = new Date(System.currentTimeMillis() + 200);
-            t.schedule(testTask, d, 100); // at least 7 times
+            t.schedule(testTask, d, 200); // at least 4 times
             testTask = new TimerTestTask();
             testTask.incrementCount(true);
             d = new Date(System.currentTimeMillis() + 300);
             t.schedule(testTask, d, 200); // at least 4 times
             testTask = new TimerTestTask();
             testTask.incrementCount(true);
+            d = new Date(System.currentTimeMillis() + 500);
+            t.schedule(testTask, d, 400); // at least 2 times
+            testTask = new TimerTestTask();
+            testTask.incrementCount(true);
             d = new Date(System.currentTimeMillis() + 100);
-            t.schedule(testTask, d, 200); // at least 4 times
+            t.schedule(testTask, d, 400); // at least 2 times
             Thread.sleep(3000);
-            assertTrue("Multiple tasks should have incremented counter 24 times not "
-                    + timerCounter, timerCounter >= 24);
+            assertTrue("Multiple tasks should have incremented counter 12 times not "
+                    + timerCounter, timerCounter >= 12);
             t.cancel();
         } finally {
             if (t != null)
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeMapTest.java
index 14506fb..f1df0c8 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeMapTest.java
@@ -1902,26 +1902,26 @@
      * Regression test for HARMONY-5788.
      */
     public void test_entrySet_contains() throws Exception {
-        TreeMap master = new TreeMap<String, String>();
+        TreeMap main = new TreeMap<String, String>();
         TreeMap test_map = new TreeMap<String, String>();
 
-        master.put("null", null);
-        Object[] entry = master.entrySet().toArray();
+        main.put("null", null);
+        Object[] entry = main.entrySet().toArray();
         assertFalse("Empty map should not contain the null-valued entry",
                 test_map.entrySet().contains(entry[0]));
 
         Map<String, String> submap = test_map.subMap("a", "z");
-        entry = master.entrySet().toArray();
+        entry = main.entrySet().toArray();
         assertFalse("Empty submap should not contain the null-valued entry",
                 submap.entrySet().contains(entry[0]));
 
         test_map.put("null", null);
         assertTrue("entrySet().containsAll(...) should work with null values",
-                test_map.entrySet().containsAll(master.entrySet()));
+                test_map.entrySet().containsAll(main.entrySet()));
 
-        master.clear();
-        master.put("null", '0');
-        entry = master.entrySet().toArray();
+        main.clear();
+        main.put("null", '0');
+        entry = main.entrySet().toArray();
         assertFalse("Null-valued entry should not equal non-null-valued entry",
                 test_map.entrySet().contains(entry[0]));
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
index c8c5da9..a6e6577 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/jar/JarFileTest.java
@@ -495,7 +495,7 @@
             JarEntry entry = new JarEntry(entryName3);
             InputStream in = jar.getInputStream(entry);
             // BEGIN Android-added
-            byte[] dummy = getAllBytesFromStream(in);
+            byte[] fake = getAllBytesFromStream(in);
             // END Android-added
             assertNull("found certificates", entry.getCertificates());
         } catch (Exception e) {
@@ -507,7 +507,7 @@
             entry.setSize(1076);
             InputStream in = jar.getInputStream(entry);
             // BEGIN Android-added
-            byte[] dummy = getAllBytesFromStream(in);
+            byte[] fake = getAllBytesFromStream(in);
             // END Android-added
             fail("SecurityException should be thrown.");
         } catch (SecurityException e) {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java
index 09e4f66..5db1fcc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/zip/ZipOutputStreamTest.java
@@ -338,7 +338,7 @@
 
         ZipEntry zipEntry;
 
-        // There's no sane way to access ONLY mtime
+        // There's no practical way to access ONLY mtime
         Field mtimeField = ZipEntry.class.getDeclaredField("mtime");
         mtimeField.setAccessible(true);
 
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java
index d820689..305404e 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/ssl/HandshakeCompletedEventTest.java
@@ -548,7 +548,7 @@
 
 
     /**
-     * Implements basically a dummy TrustManager. It stores the certificate
+     * Implements basically a fake TrustManager. It stores the certificate
      * chain it sees, so it can later be queried.
      */
     public static class TestTrustManager implements X509TrustManager {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java
index 37c6086..e8f1bc5 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/org/apache/harmony/kernel/dalvik/ThreadsTest.java
@@ -81,6 +81,7 @@
     public void test_parkFor_3() throws Exception {
         CyclicBarrier barrier = new CyclicBarrier(1);
         Parker parker = new Parker(barrier, false, 1000);
+        parker.disableRetry();
         Thread parkerThread = new Thread(parker);
 
         UNSAFE.unpark(parkerThread);
@@ -123,6 +124,7 @@
     public void test_parkUntil_3() throws Exception {
         CyclicBarrier barrier = new CyclicBarrier(1);
         Parker parker = new Parker(barrier, true, 1000);
+        parker.disableRetry();
         Thread parkerThread = new Thread(parker);
 
         UNSAFE.unpark(parkerThread);
@@ -139,6 +141,8 @@
      */
     private static class Parker implements Runnable {
 
+        private static final long NANOS_PER_MILLI = 1_000_000L;
+
         private final CyclicBarrier barrier;
 
         /** whether {@link #amount} is milliseconds to wait in an
@@ -146,6 +150,8 @@
          * in a relative fashion (<code>false</code>) */
         private final boolean absolute;
 
+        private boolean retryDisabled;
+
         /** amount to wait (see above) */
         private final long amount;
 
@@ -153,10 +159,10 @@
         private boolean completed;
 
         /** recorded start time */
-        private long startMillis;
+        private long startNanos;
 
         /** recorded end time */
-        private long endMillis;
+        private long endNanos;
 
         /**
          * Construct an instance.
@@ -168,9 +174,17 @@
         public Parker(CyclicBarrier barrier, boolean absolute, long parkMillis) {
             this.barrier = barrier;
             this.absolute = absolute;
-
+            // this.retryDisabled = false;
+            if (parkMillis < 10) {
+              // Doesn't work well with our retry logic, and likely to be flakey.
+              throw new AssertionError("Unexpectedly short park timeout.");
+            }
             // Multiply by 1000000 because parkFor() takes nanoseconds.
-            this.amount = absolute ? parkMillis : parkMillis * 1000000;
+            this.amount = absolute ? parkMillis : parkMillis * NANOS_PER_MILLI;
+        }
+
+        public void disableRetry() {
+          retryDisabled = true;
         }
 
         public void run() {
@@ -181,20 +195,29 @@
             }
             boolean absolute = this.absolute;
             long amount = this.amount;
-            long startNanos = System.nanoTime();
+            long startNs = System.nanoTime();
             long start = System.currentTimeMillis();
 
-            if (absolute) {
-                UNSAFE.park(true, start + amount);
-            } else {
-                UNSAFE.park(false, amount);
+            for (int i = 0; i < 2; ++i) {
+                if (absolute) {
+                    UNSAFE.park(true, start + amount);
+                } else {
+                    UNSAFE.park(false, amount);
+                }
+                // park() may wake up spuriously. For our implementation, this
+                // should be unlikely, except there are cases in which the
+                // initial attempt returns immediately. Try a second time, but
+                // only in that case.
+                if (retryDisabled || System.currentTimeMillis() - start > 1L) {
+                  break;
+                }
             }
 
-            long endNanos = System.nanoTime();
+            long endNs = System.nanoTime();
 
             synchronized (this) {
-                startMillis = startNanos / 1000000;
-                endMillis = endNanos / 1000000;
+                startNanos = startNs;
+                endNanos = endNs;
                 completed = true;
                 notifyAll();
             }
@@ -219,25 +242,23 @@
                     }
                 }
 
-                return endMillis - startMillis;
+                return (endNanos - startNanos) / NANOS_PER_MILLI;
             }
         }
 
         /**
-         * Asserts that the actual duration is within 10% of the
+         * Asserts that the actual duration is within 150-200 msecs of the
          * given expected time.
+         * Observe that small errors in either direction are normal here; for a
+         * number of tests the value will be too small if the Parker thread
+         * starts late, e.g. because it doesn't get a time slice soon enough.
          *
          * @param expectedMillis the expected duration, in milliseconds
          */
         public void assertDurationIsInRange(long expectedMillis) {
-            /*
-             * Allow a bit more slop for the maximum on "expected
-             * instantaneous" results.
-             */
-            long minimum = (long) ((double) expectedMillis * 0.80);
-            long maximum =
-                Math.max((long) ((double) expectedMillis * 1.20), 10);
-            long waitMillis = Math.max(expectedMillis * 10, 1000);
+            long minimum = Math.max(expectedMillis - 150L, 0L);
+            long maximum = expectedMillis + 200L;
+            long waitMillis = Math.max(expectedMillis * 10L, 1000L);
             long duration = getDurationMillis(waitMillis);
 
             if (duration < minimum) {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/org/xml/sax/support/MockFilter.java b/harmony-tests/src/test/java/org/apache/harmony/tests/org/xml/sax/support/MockFilter.java
index 2c93189..d09e3a7 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/org/xml/sax/support/MockFilter.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/org/xml/sax/support/MockFilter.java
@@ -29,7 +29,7 @@
 import org.xml.sax.helpers.XMLFilterImpl;
 
 /**
- * A helper class that extends XMLFilterImpl, provides dummy feature/property
+ * A helper class that extends XMLFilterImpl, provides fake feature/property
  * management, and logs some method calls.
  */
 public class MockFilter extends XMLFilterImpl {
diff --git a/json/src/main/java/org/json/JSONObject.java b/json/src/main/java/org/json/JSONObject.java
index 40d15bb..c7a881d 100644
--- a/json/src/main/java/org/json/JSONObject.java
+++ b/json/src/main/java/org/json/JSONObject.java
@@ -16,6 +16,9 @@
 
 package org.json;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.ArrayList;
@@ -674,11 +677,14 @@
      *
      * See {@link #keys()}.
      *
-     * @hide.
+     * @return set of keys in this object
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public Set<String> keySet() {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @NonNull public Set<@NonNull String> keySet() {
         return nameValuePairs.keySet();
     }
 
diff --git a/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
index 44d12b3..71be06c 100644
--- a/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
+++ b/jsr166-tests/src/test/java/jsr166/FutureTaskTest.java
@@ -467,8 +467,8 @@
             shouldThrow();
         } catch (SecurityException expected) {}
 
-        // We failed to deliver the interrupt, but the world retains
-        // its sanity, as if we had done task.cancel(false)
+        // We failed to deliver the interrupt, but the world remains
+        // consistent, as if we had done task.cancel(false)
         assertTrue(task.isCancelled());
         assertTrue(task.isDone());
         assertEquals(1, task.runCount());
diff --git a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
index ea6e576..c073f1d 100644
--- a/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
+++ b/jsr166-tests/src/test/java/jsr166/JSR166TestCase.java
@@ -956,7 +956,7 @@
      * Uninteresting threads are filtered out.
      */
     static void dumpTestThreads() {
-        // Android-change no ThreadMXBean
+        // Android-removed: no ThreadMXBean.
         // ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
         // System.err.println("------ stacktrace dump start ------");
         // for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
@@ -1089,7 +1089,7 @@
      * getPolicy/setPolicy.
      */
     public void runWithPermissions(Runnable r, Permission... permissions) {
-        // Android-changed: no SecurityManager
+        // Android-removed: no SecurityManager.
         // SecurityManager sm = System.getSecurityManager();
         // if (sm == null) {
         //     r.run();
@@ -1107,7 +1107,7 @@
      */
     public void runWithSecurityManagerWithPermissions(Runnable r,
                                                       Permission... permissions) {
-        // Android-changed: no SecurityManager
+        // Android-removed: no SecurityManager.
         // SecurityManager sm = System.getSecurityManager();
         // if (sm == null) {
         //     Policy savedPolicy = Policy.getPolicy();
diff --git a/libart/src/main/java/dalvik/system/AnnotatedStackTraceElement.java b/libart/src/main/java/dalvik/system/AnnotatedStackTraceElement.java
index 0318c83..cce747d 100644
--- a/libart/src/main/java/dalvik/system/AnnotatedStackTraceElement.java
+++ b/libart/src/main/java/dalvik/system/AnnotatedStackTraceElement.java
@@ -16,6 +16,12 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
 /**
  * A class encapsulating a StackTraceElement and lock state. This adds
  * critical thread state to the standard stack trace information, which
@@ -23,8 +29,9 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
-public class AnnotatedStackTraceElement {
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+public final class AnnotatedStackTraceElement {
     /**
      * The traditional StackTraceElement describing the Java stack frame.
      */
@@ -36,7 +43,7 @@
     private Object[] heldLocks;
 
     /**
-     * If this frame denotes the top of stack, <code>blockedOn<code> will hold
+     * If this frame denotes the top of stack, {@code blockedOn} will hold
      * the object this thread is waiting to lock, or waiting on, if any. May be
      * null.
      */
@@ -46,18 +53,45 @@
     private AnnotatedStackTraceElement() {
     }
 
-    @libcore.api.CorePlatformApi
-    public StackTraceElement getStackTraceElement() {
+    /**
+     * Returns the {@link StackTraceElement} describing the Java stack frame.
+     *
+     * @return {@link StackTraceElement} describing the Java stack frame.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @NonNull public StackTraceElement getStackTraceElement() {
         return stackTraceElement;
     }
 
-    @libcore.api.CorePlatformApi
-    public Object[] getHeldLocks() {
+    /**
+     * Returns the objects this stack frame is synchronized on.
+     * May be {@code null}.
+     *
+     * @return array of objects current frame is syncronized on.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @Nullable public Object[] getHeldLocks() {
         return heldLocks;
     }
 
-    @libcore.api.CorePlatformApi
-    public Object getBlockedOn() {
+    /**
+     * Returns the object this stack frame is waiting on for synchronization.
+     * May be {@code null}.
+     *
+     * @return object this thread is waiting to lock, or waiting on, if any,
+     *         or {@code null}, if none.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @Nullable public Object getBlockedOn() {
         return blockedOn;
     }
 }
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 321f4e3..2b5d8cd 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -16,11 +16,18 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import dalvik.annotation.compat.VersionCodes;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.api.CorePlatformApi;
 
 import java.lang.ref.FinalizerReference;
 import java.util.HashMap;
@@ -28,9 +35,6 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
 /**
  * Provides an interface to VM-global, Dalvik-specific features.
  * An application cannot create its own Runtime instance, and must obtain
@@ -38,7 +42,9 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.IntraCoreApi
 public final class VMRuntime {
 
     /**
@@ -65,32 +71,44 @@
 
     /**
      * Remove meta-reflection workaround for hidden api usage for apps targeting R+. This allowed
-     * apps to obtain references to blacklisted fields and methods through an extra layer of
+     * apps to obtain references to blocklist fields and methods through an extra layer of
      * reflection.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+    @EnabledSince(targetSdkVersion = VersionCodes.R)
     private static final long
-        PREVENT_META_REFLECTION_BLACKLIST_ACCESS = 142365358; // This is a bug id.
+        PREVENT_META_REFLECTION_BLOCKLIST_ACCESS = 142365358; // This is a bug id.
 
     /**
      * Gating access to greylist-max-p APIs.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = VersionCodes.P)
+    @EnabledSince(targetSdkVersion = VersionCodes.Q)
     private static final long HIDE_MAXTARGETSDK_P_HIDDEN_APIS = 149997251; // This is a bug id.
 
     /**
      * Gating access to greylist-max-q APIs.
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+    @EnabledSince(targetSdkVersion = VersionCodes.R)
     private static final long HIDE_MAXTARGETSDK_Q_HIDDEN_APIS = 149994052; // This is a bug id.
 
     /**
-     * Interface for logging hidden API usage events.
+     * Allow apps accessing @TestApi APIs.
+     *
+     * <p>This will always be disabled by default and should only be used by platform test code.
      */
-    @libcore.api.CorePlatformApi
+    @ChangeId
+    @Disabled
+    private static final long ALLOW_TEST_API_ACCESS = 166236554; // This is a bug id.
+
+    /**
+     * Interface for logging hidden API usage events.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public interface HiddenApiUsageLogger {
 
         // The following ACCESS_METHOD_ constants must match the values in
@@ -98,23 +116,39 @@
         /**
          * Internal test value that does not correspond to an actual access by the
          * application. Never logged, added for completeness.
+         *
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         public static final int ACCESS_METHOD_NONE = 0;
 
         /**
          *  Used when a method has been accessed via reflection.
+         *
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         public static final int ACCESS_METHOD_REFLECTION = 1;
 
         /**
          *  Used when a method has been accessed via JNI.
+         *
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         public static final int ACCESS_METHOD_JNI = 2;
 
         /**
          * Used when a method is accessed at link time. Never logged, added only
          * for completeness.
+         *
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         public static final int ACCESS_METHOD_LINKING = 3;
 
         /**
@@ -130,7 +164,11 @@
          *      {@code com.android.app.Activity->finish(I)V})
          * @param accessType how the accessed was done
          * @param accessDenied whether the access was allowed or not
+         *
+         * @hide
          */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
         public void hiddenApiUsed(int sampledValue, String appPackageName,
             String signature, int accessType, boolean accessDenied);
     }
@@ -141,8 +179,14 @@
      * Sets the hidden API usage logger {@link #hiddenApiUsageLogger}.
      * It should only be called if {@link #setHiddenApiAccessLogSamplingRate(int)}
      * is called with a value > 0
+     *
+     * @param hiddenApiUsageLogger an object implement {@code HiddenApiUsageLogger} that the runtime
+     *          will call for logging hidden API checks.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setHiddenApiUsageLogger(HiddenApiUsageLogger hiddenApiUsageLogger) {
         VMRuntime.hiddenApiUsageLogger = hiddenApiUsageLogger;
     }
@@ -166,7 +210,9 @@
      * any released version in {@code android.os.Build.VERSION_CODES}.
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    // Must match android.os.Build.VERSION_CODES.CUR_DEVELOPMENT.
     public static final int SDK_VERSION_CUR_DEVELOPMENT = 10000;
 
     private static Consumer<String> nonSdkApiUsageConsumer = null;
@@ -189,13 +235,15 @@
     }
 
     /**
-     * Returns the object that represents the VM instance's Dalvik-specific
-     * runtime environment.
-     *
+     * Returns the object that represents the current runtime.
      * @return the runtime object
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @libcore.api.IntraCoreApi
     public static VMRuntime getRuntime() {
         return THE_ONE;
     }
@@ -203,50 +251,79 @@
     /**
      * Returns a copy of the VM's command-line property settings.
      * These are in the form "name=value" rather than "-Dname=value".
+     *
+     * @hide
      */
     public native String[] properties();
 
     /**
      * Returns the VM's boot class path.
+     *
+     * @hide
      */
     public native String bootClassPath();
 
     /**
      * Returns the VM's class path.
+     *
+     * @hide
      */
     public native String classPath();
 
     /**
      * Returns the VM's version.
+     *
+     * @hide
      */
     public native String vmVersion();
 
     /**
      * Returns the name of the shared library providing the VM implementation.
+     *
+     * @return the name of the shared library providing the VM implementation.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native String vmLibrary();
 
     /**
      * Returns the VM's instruction set.
+     *
+     * @return the VM's instruction set.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native String vmInstructionSet();
 
     /**
      * Returns whether the VM is running in 64-bit mode.
+     *
+     * @return true if VM is running in 64-bit mode, false otherwise.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     @FastNative
     public native boolean is64Bit();
 
     /**
      * Returns whether the VM is running with JNI checking enabled.
+     *
+     * @return true if the VM is running with JNI checking enabled,
+     *         and false otherwise.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public native boolean isCheckJniEnabled();
 
@@ -257,14 +334,17 @@
      * equal to this number.
      *
      * @return the current ideal heap utilization
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
     public native float getTargetHeapUtilization();
 
     /**
      * Retrieves the finalizer timeout in milliseconds.
      * Finalizers that fail to terminate in this amount of time cause the
      * runtime to abort.
+     *
+     * @hide
      */
     public native long getFinalizerTimeoutMs();
 
@@ -280,9 +360,10 @@
      *                  This value may be adjusted internally.
      * @return the previous ideal heap utilization
      * @throws IllegalArgumentException if newTarget is &lt;= 0.0 or &gt;= 1.0
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public float setTargetHeapUtilization(float newTarget) {
         if (newTarget <= 0.0f || newTarget >= 1.0f) {
             throw new IllegalArgumentException(newTarget + " out of range (0,1)");
@@ -305,10 +386,15 @@
      * Sets the target SDK version. Should only be called before the
      * app starts to run, because it may change the VM's behavior in
      * dangerous ways. Defaults to {@link #SDK_VERSION_CUR_DEVELOPMENT}.
+     *
+     * @param targetSdkVersion the SDK version the app wants to run with.
+     *
+     * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk=0, publicAlternatives="Use the {@code targetSdkVersion}"
         +" attribute in the {@code uses-sdk} manifest tag instead.")
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public synchronized void setTargetSdkVersion(int targetSdkVersion) {
         this.targetSdkVersion = targetSdkVersion;
         setTargetSdkVersionNative(this.targetSdkVersion);
@@ -319,8 +405,13 @@
      * Sets the disabled compat changes. Should only be called before the
      * app starts to run, because it may change the VM's behavior in
      * dangerous ways. Defaults to empty.
+     *
+     * @param disabledCompatChanges An array of ChangeIds that we want to disable.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public synchronized void setDisabledCompatChanges(long[] disabledCompatChanges) {
         this.disabledCompatChanges = disabledCompatChanges;
         setDisabledCompatChangesNative(this.disabledCompatChanges);
@@ -329,8 +420,13 @@
     /**
      * Gets the target SDK version. See {@link #setTargetSdkVersion} for
      * special values.
+     *
+     * @return the target SDK version.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public synchronized int getTargetSdkVersion() {
         return targetSdkVersion;
     }
@@ -341,6 +437,8 @@
     /**
      * This method exists for binary compatibility.  It was part of a
      * heap sizing API which was removed in Android 3.0 (Honeycomb).
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
@@ -351,6 +449,8 @@
     /**
      * This method exists for binary compatibility.  It was part of a
      * heap sizing API which was removed in Android 3.0 (Honeycomb).
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
@@ -361,6 +461,8 @@
     /**
      * This method exists for binary compatibility.  It used to
      * perform a garbage collection that cleared SoftReferences.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
@@ -369,6 +471,8 @@
     /**
      * This method exists for binary compatibility.  It is equivalent
      * to {@link System#runFinalization}.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
@@ -387,6 +491,8 @@
     /**
      * This method exists for binary compatibility.  It was part of
      * the external allocation API which was removed in Android 3.0 (Honeycomb).
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
@@ -397,6 +503,8 @@
     /**
      * This method exists for binary compatibility.  It was part of
      * the external allocation API which was removed in Android 3.0 (Honeycomb).
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
@@ -405,6 +513,8 @@
     /**
      * This method exists for binary compatibility.  It was part of
      * the external allocation API which was removed in Android 3.0 (Honeycomb).
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
@@ -413,28 +523,17 @@
     }
 
     /**
-     * Tells the VM to enable the JIT compiler. If the VM does not have a JIT
-     * implementation, calling this method should have no effect.
-     */
-    @libcore.api.CorePlatformApi
-    public native void startJitCompilation();
-
-    /**
-     * Tells the VM to disable the JIT compiler. If the VM does not have a JIT
-     * implementation, calling this method should have no effect.
-     */
-    @libcore.api.CorePlatformApi
-    public native void disableJitCompilation();
-
-    /**
      * Sets the list of exemptions from hidden API access enforcement.
      *
      * @param signaturePrefixes
      *         A list of signature prefixes. Each item in the list is a prefix match on the type
      *         signature of a blacklisted API. All matching APIs are treated as if they were on
      *         the whitelist: access permitted, and no logging..
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void setHiddenApiExemptions(String[] signaturePrefixes);
 
     /**
@@ -442,62 +541,101 @@
      *
      * @param rate Proportion of hidden API accesses that will be logged; an integer between
      *                0 and 0x10000 inclusive.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void setHiddenApiAccessLogSamplingRate(int rate);
 
     /**
      * Returns an array allocated in an area of the Java heap where it will never be moved.
      * This is used to implement native allocations on the Java heap, such as DirectByteBuffers
      * and Bitmaps.
+     *
+     * @param componentType the component type of the returned array.
+     * @param length the length of the returned array.
+     * @return array allocated in an area of the heap where it will never be moved.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @libcore.api.IntraCoreApi
     @FastNative
     public native Object newNonMovableArray(Class<?> componentType, int length);
 
     /**
-     * Returns an array of at least minLength, but potentially larger. The increased size comes from
-     * avoiding any padding after the array. The amount of padding varies depending on the
-     * componentType and the memory allocator implementation.
+     * Returns an array of at least {@code minLength}, but potentially larger. The increased size
+     * comes from avoiding any padding after the array. The amount of padding varies depending on
+     * the componentType and the memory allocator implementation.
+     *
+     * @param componentType the component type of the returned array.
+     * @param minLength     the minimum length of the returned array. The actual length could
+     *                      be greater.
+     * @return              array of at least of {@code minLength}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public native Object newUnpaddedArray(Class<?> componentType, int minLength);
 
     /**
-     * Returns the address of array[0]. This differs from using JNI in that JNI might lie and
-     * give you the address of a copy of the array when in forcecopy mode.
+     * Returns the address of {@code array[0]}. This differs from using JNI in that JNI
+     * might lie and give you the address of a copy of the array when in forcecopy mode.
+     *
+     * @param array the object we want the native address of.
+     * @return native address of {@code array[0]}.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @libcore.api.IntraCoreApi
     @FastNative
     public native long addressOf(Object array);
 
     /**
      * Removes any growth limits, allowing the application to allocate
      * up to the maximum heap size.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void clearGrowthLimit();
 
     /**
      * Make the current growth limit the new non growth limit capacity by releasing pages which
      * are after the growth limit but before the non growth limit capacity.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void clampGrowthLimit();
 
     /**
      * Returns true if native debugging is on.
+     *
+     * @return true if native debugging is on, false otherwise.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @FastNative
     public native boolean isNativeDebuggable();
 
     /**
      * Returns true if Java debugging is enabled.
+     *
+     * @hide
      */
     public native boolean isJavaDebuggable();
 
@@ -510,37 +648,57 @@
      * from excessively increasing. Memory allocated via system malloc() should not be included
      * in this count. The argument must be the same as that later passed to registerNativeFree(),
      * but may otherwise be approximate.
+     *
+     * @param bytes the number of bytes of the native object.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void registerNativeAllocation(long bytes);
 
     /**
-     * Backward compatibility version of registerNativeAllocation. We used to pass an int instead
-     * of a long. The RenderScript support library looks it up via reflection.
-     * @deprecated Use long argument instead.
+     * Backward compatibility version of {@link #registerNativeAllocation(long)}. We used to pass
+     * an int instead of a long. The RenderScript support library looks it up via reflection.
+     * @deprecated Use {@link #registerNativeAllocation(long)} instead.
+     *
+     * @param bytes the number of bytes of the native object.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public void registerNativeAllocation(int bytes) {
         registerNativeAllocation((long) bytes);
     }
 
     /**
      * Registers a native free by reducing the number of native bytes accounted for.
+     *
+     * @param bytes the number of bytes of the freed object.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void registerNativeFree(long bytes);
 
     /**
-     * Backward compatibility version of registerNativeFree.
-     * @deprecated Use long argument instead.
+     * Backward compatibility version of {@link #registerNativeFree(long)}.
+     * @deprecated Use {@link #registerNativeFree(long)} instead.
+     *
+     * @param bytes the number of bytes of the freed object.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @Deprecated
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public void registerNativeFree(int bytes) {
         registerNativeFree((long) bytes);
     }
@@ -553,6 +711,8 @@
 
     /**
      * Report a native malloc()-only allocation to the GC.
+     *
+     * @hide
      */
     public void notifyNativeAllocation() {
         // Minimize JNI calls by notifying once every notifyNativeInterval allocations.
@@ -576,6 +736,8 @@
      * Report to the GC that roughly notifyNativeInterval native malloc()-based
      * allocations have occurred since the last call to notifyNativeAllocationsInternal().
      * Hints that we should check whether a GC is required.
+     *
+     * @hide
      */
     public native void notifyNativeAllocationsInternal();
 
@@ -592,6 +754,8 @@
      *
      * @see #Runtime.runFinalization()
      * @see #wait(long,int)
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public static void runFinalization(long timeout) {
@@ -604,43 +768,133 @@
         }
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Request that a garbage collection gets started on a different thread.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void requestConcurrentGC();
-    public native void concurrentGC();
+
+    /**
+     *
+     * @hide
+     */
     public native void requestHeapTrim();
+
+    /**
+     *
+     * @hide
+     */
     public native void trimHeap();
+
+    /**
+     *
+     * @hide
+     */
     public native void startHeapTaskProcessor();
+
+    /**
+     *
+     * @hide
+     */
     public native void stopHeapTaskProcessor();
+
+    /**
+     *
+     * @hide
+     */
     public native void runHeapTasks();
 
     /**
      * Let the heap know of the new process state. This can change allocation and garbage collection
      * behavior regarding trimming and compaction.
+     *
+     * @param state The state of the process, as defined in art/runtime/process_state.h.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void updateProcessState(int state);
 
     /**
      * Let the runtime know that the application startup is completed. This may affect behavior
      * related to profiling and startup caches.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void notifyStartupCompleted();
 
     /**
      * Fill in dex caches with classes, fields, and methods that are
      * already loaded. Typically used after Zygote preloading.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public native void preloadDexCaches();
 
     /**
-     * Register application info.
-     * @param profileFile the path of the file where the profile information should be stored.
-     * @param codePaths the code paths that should be profiled.
+     * Flag denoting that the code paths passed to
+     * {@link #registerAppInfo(String, String, String, String[], int, boolean)}
+     * contains the app primary APK.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static native void registerAppInfo(String profileFile, String[] codePaths);
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static final int CODE_PATH_TYPE_PRIMARY_APK = 1 << 0;
+    /**
+     * Flag denoting that the code paths passed to
+     * {@link #registerAppInfo(String, String, String, String[], int, boolean)}
+     * contains the a split APK.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static final int CODE_PATH_TYPE_SPLIT_APK = 1 << 1;
+    /**
+     * Flag denoting that the code paths passed to
+     * {@link #registerAppInfo(String, String, String, String[], int, boolean)}
+     * contains a secondary dex file (dynamically loaded by the app).
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static final int CODE_PATH_TYPE_SECONDARY_DEX = 1 << 2;
+
+    /**
+     * Register application info to ART.
+     * This enables ART to support certain low level features (such as profiling) and provide
+     * better debug information. The method should be called after the application loads its
+     * apks or dex files.
+     *
+     * @param packageName the name of the package being ran.
+     * @param currentProfileFile the path of the file where the profile information for the current
+     *        execution should be stored.
+     * @param referenceProfileFile the path of the file where the reference profile information
+     *        (for past executions) is stored.
+     * @param appCodePaths the code paths (apk/dex files) of the applications that were loaded.
+     *        These paths will also be profiled.
+     * @param codePathsTypes the type of the code paths.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static native void registerAppInfo(
+            String packageName,
+            String currentProfileFile,
+            String referenceProfileFile,
+            String[] appCodePaths,
+            int codePathsType);
 
     /**
      * Returns the runtime instruction set corresponding to a given ABI. Multiple
@@ -648,9 +902,14 @@
      * {@code armeabi-v7a} and {@code armeabi} might map to the instruction set {@code arm}.
      *
      * This influences the compilation of the applications classes.
+     *
+     * @param abi The ABI we want the instruction set from.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static String getInstructionSet(String abi) {
         final String instructionSet = ABI_TO_INSTRUCTION_SET_MAP.get(abi);
         if (instructionSet == null) {
@@ -660,114 +919,143 @@
         return instructionSet;
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Returns whether the given {@code instructionSet} is 64 bits.
+     *
+     * @param instructionSet a string representing an instruction set.
+     *
+     * @return true if given {@code instructionSet} is 64 bits, false otherwise.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean is64BitInstructionSet(String instructionSet) {
         return "arm64".equals(instructionSet) ||
                 "x86_64".equals(instructionSet) ||
                 "mips64".equals(instructionSet);
     }
 
+    /**
+     * Returns whether the given {@code abi} is 64 bits.
+     *
+     * @param abi a string representing an ABI.
+     *
+     * @return true if given {@code abi} is 64 bits, false otherwise.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean is64BitAbi(String abi) {
         return is64BitInstructionSet(getInstructionSet(abi));
     }
 
     /**
-     * Prevent initialization of the caller's class if they are calling
-     * from their clinit method. This works because calling a JNI method
-     * from clinit causes the transactional runtime to abort the current
-     * transaction.
-     * @hide
-     */
-    @CriticalNative
-    public static native void doNotInitializeInAot();
-
-    /**
      * Return false if the boot class path for the given instruction
      * set mapped from disk storage, versus being interpretted from
      * dirty pages in memory.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
     public static native boolean isBootClassPathOnDisk(String instructionSet);
 
     /**
-     * Returns whether the runtime is using a boot image.
-     *
-     * <p>While isBootClassPathOnDisk checks for the existence of an image file on disk,
-     * this method queries the runtime whether it is <em>using</em> an image.
-     */
-    @libcore.api.CorePlatformApi
-    @FastNative
-    public static native boolean hasBootImageSpaces();
-
-    /**
      * Used to notify the runtime that boot completed.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void bootCompleted();
 
     /**
      * Used to notify the runtime to reset Jit counters. This is done for the boot image
      * profiling configuration to avoid samples during class preloading. This helps avoid
      * the regression from disabling class profiling.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void resetJitCounters();
 
     /**
      * Returns the instruction set of the current runtime.
+     *
+     * @return instruction set of the current runtime.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native String getCurrentInstructionSet();
 
     /**
-     * Return true if the dalvik cache was pruned when booting. This may have happened for
-     * various reasons, e.g., after an OTA. The return value is for the current instruction
-     * set.
-     */
-    @libcore.api.CorePlatformApi
-    public static native boolean didPruneDalvikCache();
-
-    /**
      * Register the current execution thread to the runtime as sensitive thread.
      * Should be called just once. Subsequent calls are ignored.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void registerSensitiveThread();
 
     /**
      * Sets up the priority of the system daemon thread (caller).
+     *
+     * @hide
      */
     public static native void setSystemDaemonThreadPriority();
 
     /**
      * Sets a callback that the runtime can call whenever a usage of a non SDK API is detected.
+     *
+     * @param consumer an object implementing the {@code java.util.function.Consumer} interface that
+     *      the runtime will call whenever a usage of a non SDK API is detected.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setNonSdkApiUsageConsumer(Consumer<String> consumer) {
         nonSdkApiUsageConsumer = consumer;
     }
 
     /**
      * Sets whether or not the runtime should dedupe detection and warnings for hidden API usage.
-     * If deduping is enabled, only the first usage of each API will be detected. The default
-     * behaviour is to dedupe.
+     *
+     * @param dedupe if set, only the first usage of each API will be detected. The default
+     *      behaviour is to dedupe.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void setDedupeHiddenApiWarnings(boolean dedupe);
 
     /**
      * Sets the package name of the app running in this process.
+     *
+     * @param packageName the value being set
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void setProcessPackageName(String packageName);
 
     /**
      * Sets the full path to data directory of the app running in this process.
+     *
+     * @param dataDir the value being set
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void setProcessDataDirectory(String dataDir);
 
     /**
@@ -775,9 +1063,14 @@
      * A class loader context is an internal opaque format used by the runtime to encode the
      * class loader hierarchy (including each ClassLoader's classpath) used to load a dex file.
      *
-     * @return True if encodedClassLoaderContext is a non-null valid encoded class loader context.
-     *   Throws NullPointerException if encodedClassLoaderContext is null.
+     * @param encodedClassLoaderContext the class loader context to analyze
+     * @throws NullPointerException if {@code encodedClassLoaderContext is null.
+     * @return {@code true} if {@code encodedClassLoaderContext} is a non-null valid encoded class
+     *         loader context.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native boolean isValidClassLoaderContext(String encodedClassLoaderContext);
 }
diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java
index 89ce215..9a84f0f 100644
--- a/libart/src/main/java/dalvik/system/VMStack.java
+++ b/libart/src/main/java/dalvik/system/VMStack.java
@@ -16,17 +16,23 @@
 
 package dalvik.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import dalvik.annotation.optimization.FastNative;
 
+import libcore.util.Nullable;
+
 /**
  * Provides a limited interface to the Dalvik VM stack. This class is mostly
  * used for implementing security checks.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = MODULE_LIBRARIES)
 public final class VMStack {
 
     private VMStack() {
@@ -40,6 +46,8 @@
      * @deprecated Use {@code ClassLoader.getClassLoader(sun.reflect.Reflection.getCallerClass())}.
      *         Note that that can return {@link BootClassLoader} on Android where the RI
      *         would have returned null.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @FastNative
@@ -51,6 +59,8 @@
      *
      * @return the requested class, or {@code null}.
      * @deprecated Use {@link sun.reflect.Reflection#getCallerClass()}.
+     *
+     * @hide
      */
     @Deprecated
     public static Class<?> getStackClass1() {
@@ -61,6 +71,8 @@
      * Returns the class of the caller's caller's caller.
      *
      * @return the requested class, or {@code null}.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @FastNative
@@ -69,6 +81,8 @@
     /**
      * Returns the first ClassLoader on the call stack that isn't the
      * bootstrap class loader.
+     *
+     * @hide
      */
     @FastNative
     public native static ClassLoader getClosestUserClassLoader();
@@ -80,6 +94,8 @@
      *      thread of interest
      * @return an array of stack trace elements, or null if the thread
      *      doesn't have a stack trace (e.g. because it exited)
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @FastNative
@@ -92,10 +108,13 @@
      *      thread of interest
      * @return an array of annotated stack frames, or null if the thread
      *      doesn't have a stack trace (e.g. because it exited)
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
     @FastNative
-    native public static AnnotatedStackTraceElement[]
+    native public static @Nullable AnnotatedStackTraceElement[]
             getAnnotatedThreadStackTrace(Thread t);
 
     /**
@@ -108,6 +127,8 @@
      *      preallocated array for use when only the top of stack is
      *      desired. Unused elements will be filled with null values.
      * @return the number of elements filled
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     @FastNative
diff --git a/libart/src/main/java/java/lang/StringFactory.java b/libart/src/main/java/java/lang/StringFactory.java
index 6ef664b..ea80b9f 100644
--- a/libart/src/main/java/java/lang/StringFactory.java
+++ b/libart/src/main/java/java/lang/StringFactory.java
@@ -65,14 +65,6 @@
         return newStringFromBytes(data, 0, data.length, Charset.forNameUEE(charsetName));
     }
 
-    private static final int[] TABLE_UTF8_NEEDED = new int[] {
-    //      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
-            0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
-            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
-            2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
-            3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0 - 0xff
-    };
-
     // TODO: Implement this method natively.
     public static String newStringFromBytes(byte[] data, int offset, int byteCount, Charset charset) {
         if ((offset | byteCount) < 0 || byteCount > data.length - offset) {
@@ -85,147 +77,7 @@
         // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed.
         String canonicalCharsetName = charset.name();
         if (canonicalCharsetName.equals("UTF-8")) {
-            /*
-            This code converts a UTF-8 byte sequence to a Java String (UTF-16).
-            It implements the W3C recommended UTF-8 decoder.
-            https://www.w3.org/TR/encoding/#utf-8-decoder
-
-            Unicode 3.2 Well-Formed UTF-8 Byte Sequences
-            Code Points        First  Second Third Fourth
-            U+0000..U+007F     00..7F
-            U+0080..U+07FF     C2..DF 80..BF
-            U+0800..U+0FFF     E0     A0..BF 80..BF
-            U+1000..U+CFFF     E1..EC 80..BF 80..BF
-            U+D000..U+D7FF     ED     80..9F 80..BF
-            U+E000..U+FFFF     EE..EF 80..BF 80..BF
-            U+10000..U+3FFFF   F0     90..BF 80..BF 80..BF
-            U+40000..U+FFFFF   F1..F3 80..BF 80..BF 80..BF
-            U+100000..U+10FFFF F4     80..8F 80..BF 80..BF
-
-            Please refer to Unicode as the authority.
-            p.126 Table 3-7 in http://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
-
-            Handling Malformed Input
-            The maximal subpart should be replaced by a single U+FFFD. Maximal subpart is
-            the longest code unit subsequence starting at an unconvertible offset that is either
-            1) the initial subsequence of a well-formed code unit sequence, or
-            2) a subsequence of length one:
-            One U+FFFD should be emitted for every sequence of bytes that is an incomplete prefix
-            of a valid sequence, and with the conversion to restart after the incomplete sequence.
-
-            For example, in byte sequence "41 C0 AF 41 F4 80 80 41", the maximal subparts are
-            "C0", "AF", and "F4 80 80". "F4 80 80" can be the initial subsequence of "F4 80 80 80",
-            but "C0" can't be the initial subsequence of any well-formed code unit sequence.
-            Thus, the output should be "A\ufffd\ufffdA\ufffdA".
-
-            Please refer to section "Best Practices for Using U+FFFD." in
-            http://www.unicode.org/versions/Unicode10.0.0/ch03.pdf
-            */
-            byte[] d = data;
-            char[] v = new char[byteCount];
-
-            int idx = offset;
-            int last = offset + byteCount;
-            int s = 0;
-
-            int codePoint = 0;
-            int utf8BytesSeen = 0;
-            int utf8BytesNeeded = 0;
-            int lowerBound = 0x80;
-            int upperBound = 0xbf;
-
-            while (idx < last) {
-                int b = d[idx++] & 0xff;
-                if (utf8BytesNeeded == 0) {
-                    if ((b & 0x80) == 0) { // ASCII char. 0xxxxxxx
-                        v[s++] = (char) b;
-                        continue;
-                    }
-
-                    if ((b & 0x40) == 0) { // 10xxxxxx is illegal as first byte
-                        v[s++] = REPLACEMENT_CHAR;
-                        continue;
-                    }
-
-                    // 11xxxxxx
-                    int tableLookupIndex = b & 0x3f;
-                    utf8BytesNeeded = TABLE_UTF8_NEEDED[tableLookupIndex];
-                    if (utf8BytesNeeded == 0) {
-                        v[s++] = REPLACEMENT_CHAR;
-                        continue;
-                    }
-
-                    // utf8BytesNeeded
-                    // 1: b & 0x1f
-                    // 2: b & 0x0f
-                    // 3: b & 0x07
-                    codePoint = b & (0x3f >> utf8BytesNeeded);
-                    if (b == 0xe0) {
-                        lowerBound = 0xa0;
-                    } else if (b == 0xed) {
-                        upperBound = 0x9f;
-                    } else if (b == 0xf0) {
-                        lowerBound = 0x90;
-                    } else if (b == 0xf4) {
-                        upperBound = 0x8f;
-                    }
-                } else {
-                    if (b < lowerBound || b > upperBound) {
-                        // The bytes seen are ill-formed. Substitute them with U+FFFD
-                        v[s++] = REPLACEMENT_CHAR;
-                        codePoint = 0;
-                        utf8BytesNeeded = 0;
-                        utf8BytesSeen = 0;
-                        lowerBound = 0x80;
-                        upperBound = 0xbf;
-                        /*
-                         * According to the Unicode Standard,
-                         * "a UTF-8 conversion process is required to never consume well-formed
-                         * subsequences as part of its error handling for ill-formed subsequences"
-                         * The current byte could be part of well-formed subsequences. Reduce the
-                         * index by 1 to parse it in next loop.
-                         */
-                        idx--;
-                        continue;
-                    }
-
-                    lowerBound = 0x80;
-                    upperBound = 0xbf;
-                    codePoint = (codePoint << 6) | (b & 0x3f);
-                    utf8BytesSeen++;
-                    if (utf8BytesNeeded != utf8BytesSeen) {
-                        continue;
-                    }
-
-                    // Encode chars from U+10000 up as surrogate pairs
-                    if (codePoint < 0x10000) {
-                        v[s++] = (char) codePoint;
-                    } else {
-                        v[s++] = (char) ((codePoint >> 10) + 0xd7c0);
-                        v[s++] = (char) ((codePoint & 0x3ff) + 0xdc00);
-                    }
-
-                    utf8BytesSeen = 0;
-                    utf8BytesNeeded = 0;
-                    codePoint = 0;
-                }
-            }
-
-            // The bytes seen are ill-formed. Substitute them by U+FFFD
-            if (utf8BytesNeeded != 0) {
-                v[s++] = REPLACEMENT_CHAR;
-            }
-
-            if (s == byteCount) {
-                // We guessed right, so we can use our temporary array as-is.
-                value = v;
-                length = s;
-            } else {
-                // Our temporary array was too big, so reallocate and copy.
-                value = new char[s];
-                length = s;
-                System.arraycopy(v, 0, value, 0, s);
-            }
+            return newStringFromUtf8Bytes(data, offset, byteCount);
         } else if (canonicalCharsetName.equals("ISO-8859-1")) {
             value = new char[byteCount];
             length = byteCount;
@@ -267,6 +119,9 @@
     @FastNative
     public static native String newStringFromString(String toCopy);
 
+    @FastNative
+    public static native String newStringFromUtf8Bytes(byte[] data, int offset, int byteCount);
+
     public static String newStringFromStringBuffer(StringBuffer stringBuffer) {
         synchronized (stringBuffer) {
             return newStringFromChars(stringBuffer.getValue(), 0, stringBuffer.length());
diff --git a/luni/Android.bp b/luni/Android.bp
new file mode 100644
index 0000000..ef9ab60
--- /dev/null
+++ b/luni/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2021 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 {
+    default_applicable_licenses: ["libcore_luni_license"],
+}
+
+// Added automatically by a large-scale-change
+// http://go/android-license-faq
+license {
+    name: "libcore_luni_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "license.html",
+    ],
+}
diff --git a/luni/src/main/java/android/compat/Compatibility.java b/luni/src/main/java/android/compat/Compatibility.java
index 9b4b1af..28147dd 100644
--- a/luni/src/main/java/android/compat/Compatibility.java
+++ b/luni/src/main/java/android/compat/Compatibility.java
@@ -16,6 +16,9 @@
 
 package android.compat;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.ChangeId;
 
 import libcore.api.CorePlatformApi;
@@ -25,6 +28,7 @@
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
+import libcore.util.NonNull;
 
 /**
  * Internal APIs for logging and gating compatibility changes.
@@ -33,7 +37,8 @@
  *
  * @hide
  */
-@CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@CorePlatformApi(status = CorePlatformApi.Status.STABLE)
 @IntraCoreApi
 public final class Compatibility {
 
@@ -52,11 +57,14 @@
      * {@link #isChangeEnabled(long)} returns {@code true}.
      *
      * @param changeId The ID of the compatibility change taking effect.
+     *
+     * @hide
      */
-    @CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     @IntraCoreApi
-    public static void reportChange(@ChangeId long changeId) {
-        sCallbacks.reportChange(changeId);
+    public static void reportUnconditionalChange(@ChangeId long changeId) {
+        sCallbacks.onChangeReported(changeId);
     }
 
     /**
@@ -68,25 +76,57 @@
      * {@code false}, the calling code should behave as it did in earlier releases.
      *
      * <p>When this method returns {@code true}, it will also report the change as
-     * {@link #reportChange(long)} would, so there is no need to call that method directly.
+     * {@link #reportUnconditionalChange(long)} would, so there is no need to call that method
+     * directly.
      *
      * @param changeId The ID of the compatibility change in question.
      * @return {@code true} if the change is enabled for the current app.
+     *
+     * @hide
      */
-    @CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     @IntraCoreApi
     public static boolean isChangeEnabled(@ChangeId long changeId) {
         return sCallbacks.isChangeEnabled(changeId);
     }
 
-    private volatile static Callbacks sCallbacks = new Callbacks();
+    private static final BehaviorChangeDelegate DEFAULT_CALLBACKS = new BehaviorChangeDelegate(){};
 
-    @CorePlatformApi
-    public static void setCallbacks(Callbacks callbacks) {
+    private volatile static BehaviorChangeDelegate sCallbacks = DEFAULT_CALLBACKS;
+
+    /**
+     * Sets the behavior change delegate.
+     *
+     * All changes reported via the {@link Compatibility} class will be forwarded to this class.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public static void setBehaviorChangeDelegate(BehaviorChangeDelegate callbacks) {
         sCallbacks = Objects.requireNonNull(callbacks);
     }
 
-    @CorePlatformApi
+    /**
+     * Removes a behavior change delegate previously set via {@link #setBehaviorChangeDelegate}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public static void clearBehaviorChangeDelegate() {
+        sCallbacks = DEFAULT_CALLBACKS;
+    }
+
+    /**
+     * For use by tests only. Causes values from {@code overrides} to be returned instead of the
+     * real value.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public static void setOverrides(ChangeConfig overrides) {
         // Setting overrides twice in a row does not need to be supported because
         // this method is only for enabling/disabling changes for the duration of
@@ -99,7 +139,13 @@
         sCallbacks = new OverrideCallbacks(sCallbacks, overrides);
     }
 
-    @CorePlatformApi
+    /**
+     * For use by tests only. Removes overrides set by {@link #setOverrides}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public static void clearOverrides() {
         if (!(sCallbacks instanceof OverrideCallbacks)) {
             throw new IllegalStateException("No overrides set");
@@ -113,32 +159,55 @@
      *
      * This is provided as a class rather than an interface to allow new methods to be added without
      * breaking @CorePlatformApi binary compatibility.
+     *
+     * @hide
      */
-    @CorePlatformApi
-    public static class Callbacks {
-        @CorePlatformApi
-        protected Callbacks() {
-        }
-        @CorePlatformApi
-        protected void reportChange(long changeId) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public interface BehaviorChangeDelegate {
+        /**
+         * Called when a change is reported via {@link Compatibility#reportUnconditionalChange}
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        default void onChangeReported(long changeId) {
             // Do not use String.format here (b/160912695)
             System.logW("No Compatibility callbacks set! Reporting change " + changeId);
         }
-        @CorePlatformApi
-        protected boolean isChangeEnabled(long changeId) {
+
+        /**
+         * Called when a change is queried via {@link Compatibility#isChangeEnabled}
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        default boolean isChangeEnabled(long changeId) {
             // Do not use String.format here (b/160912695)
             System.logW("No Compatibility callbacks set! Querying change " + changeId);
             return true;
         }
     }
 
-    @CorePlatformApi
+    /**
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     @IntraCoreApi
     public static final class ChangeConfig {
         private final Set<Long> enabled;
         private final Set<Long> disabled;
 
-        public ChangeConfig(Set<Long> enabled, Set<Long> disabled) {
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
+        public ChangeConfig(@NonNull Set<@NonNull Long> enabled, @NonNull Set<@NonNull Long> disabled) {
             this.enabled = Objects.requireNonNull(enabled);
             this.disabled = Objects.requireNonNull(disabled);
             if (enabled.contains(null)) {
@@ -155,6 +224,12 @@
             }
         }
 
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
         public boolean isEmpty() {
             return enabled.isEmpty() && disabled.isEmpty();
         }
@@ -168,30 +243,75 @@
             return result;
         }
 
-        public long[] forceEnabledChangesArray() {
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
+        public @NonNull long[] getEnabledChangesArray() {
             return toLongArray(enabled);
         }
 
-        public long[] forceDisabledChangesArray() {
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
+        public @NonNull long[] getDisabledChangesArray() {
             return toLongArray(disabled);
         }
 
-        public Set<Long> forceEnabledSet() {
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
+        public @NonNull Set<@NonNull Long> getEnabledSet() {
             return Collections.unmodifiableSet(enabled);
         }
 
-        public Set<Long> forceDisabledSet() {
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
+        public @NonNull Set<@NonNull Long> getDisabledSet() {
             return Collections.unmodifiableSet(disabled);
         }
 
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
         public boolean isForceEnabled(long changeId) {
             return enabled.contains(changeId);
         }
 
+
+        /**
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        @IntraCoreApi
         public boolean isForceDisabled(long changeId) {
             return disabled.contains(changeId);
         }
 
+
+        /**
+         * @hide
+         */
         @Override
         public boolean equals(Object o) {
             if (this == o) return true;
@@ -203,27 +323,34 @@
                     disabled.equals(that.disabled);
         }
 
+        /**
+         * @hide
+         */
         @Override
         public int hashCode() {
             return Objects.hash(enabled, disabled);
         }
 
+
+        /**
+         * @hide
+         */
         @Override
         public String toString() {
             return "ChangeConfig{enabled=" + enabled + ", disabled=" + disabled + '}';
         }
     }
 
-    private static class OverrideCallbacks extends Callbacks {
-        private final Callbacks delegate;
+    private static class OverrideCallbacks implements BehaviorChangeDelegate {
+        private final BehaviorChangeDelegate delegate;
         private final ChangeConfig changeConfig;
 
-        private OverrideCallbacks(Callbacks delegate, ChangeConfig changeConfig) {
+        private OverrideCallbacks(BehaviorChangeDelegate delegate, ChangeConfig changeConfig) {
             this.delegate = Objects.requireNonNull(delegate);
             this.changeConfig = Objects.requireNonNull(changeConfig);
         }
         @Override
-        protected boolean isChangeEnabled(long changeId) {
+        public boolean isChangeEnabled(long changeId) {
            if (changeConfig.isForceEnabled(changeId)) {
                return true;
            }
diff --git a/luni/src/main/java/android/system/Int32Ref.java b/luni/src/main/java/android/system/Int32Ref.java
index c43c796..70c4876 100644
--- a/luni/src/main/java/android/system/Int32Ref.java
+++ b/luni/src/main/java/android/system/Int32Ref.java
@@ -24,13 +24,10 @@
  * @hide
  * A signed 32bit integer reference suitable for passing to lower-level system calls.
  */
-@libcore.api.CorePlatformApi
 public class Int32Ref {
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public int value;
 
-    @libcore.api.CorePlatformApi
     public Int32Ref(int value) {
         this.value = value;
     }
diff --git a/luni/src/main/java/android/system/NetlinkSocketAddress.java b/luni/src/main/java/android/system/NetlinkSocketAddress.java
index b4f6e74..ecccfa3 100644
--- a/luni/src/main/java/android/system/NetlinkSocketAddress.java
+++ b/luni/src/main/java/android/system/NetlinkSocketAddress.java
@@ -16,6 +16,9 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.net.SocketAddress;
@@ -27,41 +30,104 @@
  * Corresponds to Linux's {@code struct sockaddr_nl} from
  * <a href="https://github.com/torvalds/linux/blob/master/include/uapi/linux/netlink.h">&lt;linux/netlink.h&gt;</a>.
  *
+ * Netlink socket address descirbes a netlink client is user space or in kernel.
+ * A {@link NetlinkSocketAddress} can be either unicast (only sent to one peer)
+ * or sent to netlink multicast groups ({@code nlGroupsMask} not equal 0).
+ *
+ * Any {@link NetlinkSocketAddress} is described by {@code nlPortId} and {@code nlGroupsMask}.
+ *
+ * {@code nlPortId} is the unicast address of netlink socket. It's always 0
+ * if the destination is in the kernel. For a user-space process,
+ * {@nlPortId} is usually the PID of the process owning the destination
+ * socket. However, {@nlPortId} identifies a netlink socket, not a
+ * process. If a process owns several netlink sockets, then {@nlPortId}
+ * can be equal to the process ID only for at most one socket.
+ *
+ * {@code nlGroupsMask} is a bit mask with every bit representing a netlink
+ * group number. The default value for this field is zero which means that
+ * no multicasts will be received. A socket may multicast messages to any
+ * of the multicast groups by setting {@code nlGroupsMask} to a bit mask of
+ * the groups it wishes to send to.
+ *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class NetlinkSocketAddress extends SocketAddress {
-    /** port ID */
+    /**
+     * port ID
+     *
+     * @hide
+     */
     private final int nlPortId;
 
-    /** multicast groups mask */
+    /**
+     * multicast groups mask
+     *
+     * @hide
+     */
     private final int nlGroupsMask;
 
+    /**
+     * @hide
+     */
+    // VisibleForTesting
     public NetlinkSocketAddress() {
         this(0, 0);
     }
-
+    /**
+     * @hide
+     */
+    // VisibleForTesting
     public NetlinkSocketAddress(int nlPortId) {
         this(nlPortId, 0);
     }
 
+    /**
+     * Constructs an instance with the given port id and groups mask.
+     *
+     * @param nlPortId     port id
+     * @param nlGroupsMask groups mask
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public NetlinkSocketAddress(int nlPortId, int nlGroupsMask) {
         this.nlPortId = nlPortId;
         this.nlGroupsMask = nlGroupsMask;
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Returns this address's port id.
+     *
+     * @return port id
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public int getPortId() {
         return nlPortId;
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Returns this address's groups multicast mask.
+     *
+     * @return groups mask
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public int getGroupsMask() {
         return nlGroupsMask;
     }
 
+    /**
+     * @hide
+     */
     @Override public String toString() {
       return Objects.toString(this);
     }
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
old mode 100644
new mode 100755
index 70fdce5..9beb8786
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -16,8 +16,15 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
+import libcore.io.Libcore;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
 import java.net.InetAddress;
@@ -25,9 +32,6 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
-import libcore.io.Libcore;
-import libcore.util.NonNull;
-import libcore.util.Nullable;
 
 /**
  * Access to low-level system functionality. Most of these are system calls. Most users will want
@@ -73,20 +77,40 @@
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
      *
+     * @param hdr capabilities header, containing version and pid
+     * @return list of capabilities data structures, each containing effective, permitted,
+     *         and inheritable fields are bit masks of the capabilities
+     * @throws ErrnoException if {@code hdr} structure contains invalid data
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @Nullable StructCapUserData[] capget(@NonNull StructCapUserHeader hdr) throws ErrnoException {
         return Libcore.os.capget(hdr);
     }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/capset.2.html">capset(2)</a>.
      *
+     * @param hdr capabilities header, containing version and pid
+     * @param data capabilities data list, containing effective, permitted,
+     *             and inheritable fields. Must be the same length as returned value
+     * @throws ErrnoException if {@code hdr} structure contains invalid data; or
+     *                        an attempt was made to add a capability to the permitted
+     *                        set, or to set a capability in the effective set that is
+     *                        not in the permitted set; or
+     *                        the caller attempted to use
+     *                        {@link capset(StructCapUserHeader, StructCapUserData[])}
+     *                        to modify the capabilities of a thread other than itself,
+     *                        but lacked sufficient privilege;
+     *                        or there is no such thread.
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void capset(StructCapUserHeader hdr, StructCapUserData[] data)
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void capset(@NonNull StructCapUserHeader hdr, @NonNull StructCapUserData[] data)
             throws ErrnoException {
         Libcore.os.capset(hdr, data);
     }
@@ -224,10 +248,18 @@
     public static SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return Libcore.os.getpeername(fd); }
 
     /**
+     * Gets process's pgid (process group ID).
+     *
      * See <a href="http://man7.org/linux/man-pages/man2/getpgid.2.html">getpgid(2)</a>.
+     *
+     * @param pid process id to get the pgid of
+     * @return process's pgid
+     * @throws ErrnoException if {@code pid} does not match any process
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int getpgid(int pid) throws ErrnoException { return Libcore.os.getpgid(pid); }
 
     /**
@@ -246,9 +278,20 @@
     /** @hide */
     public static StructPasswd getpwuid(int uid) throws ErrnoException { return Libcore.os.getpwuid(uid); }
 
-    /** @hide */
-    @libcore.api.CorePlatformApi
-    public static StructRlimit getrlimit(int resource) throws ErrnoException { return Libcore.os.getrlimit(resource); }
+    /**
+     * Gets the resource limit.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man3/vlimit.3.html">getrlimit(2)</a>.
+     *
+     * @param resource resource id
+     * @return         the limit of the given resource
+     * @throws ErrnoException the value specified in {@code resource} is not valid
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @Nullable StructRlimit getrlimit(int resource) throws ErrnoException { return Libcore.os.getrlimit(resource); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/getsockname.2.html">getsockname(2)</a>.
@@ -261,13 +304,47 @@
     /** @hide */
     public static InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptInAddr(fd, level, option); }
 
-    /** @hide */
-    @libcore.api.CorePlatformApi
-    public static int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptInt(fd, level, option); }
+    /**
+     * Gets socket options for the socket referred to by the file descriptor {@code fd}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/getsockopt.2.html">getsockopt(2)</a>.
+     * For the list of available options see <a href="https://man7.org/linux/man-pages/man7/socket.7.html">socket(7)</a>.
+     *
+     * @param fd    file descriptor of the socket to get options of
+     * @param level level at which the {@code option} resides. For example,
+     *              to indicate that an option is to be interpreted by the TCP protocol,
+     *              level should be set to the protocol number of TCP
+     * @param option name of the option to get
+     * @return socket options for file descriptor {@code fd}
+     * @throws ErrnoException if {@code fd} is invalid; or
+     *                        {@code option} is unknown at given {@code level}
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static int getsockoptInt(@NonNull FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptInt(fd, level, option); }
 
-    /** @hide */
-    @libcore.api.CorePlatformApi
-    public static StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptLinger(fd, level, option); }
+    /**
+     * Gets {@link OsConstants#SO_LINGER} option for the socket referred to by the file descriptor {@code fd}.
+     * When enabled, a {@link close(FileDescriptor) or {@link shutdown(FileDescriptor, int)} will
+     * not return until all queued messages for the socket have been successfully sent or the
+     * linger timeout has been reached. Otherwise, the call returns immediately and the closing is
+     * done in the background.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man7/socket.7.html">socket(7)</a>.
+     *
+     * @param fd     file descriptor of the socket to get {@code OsConstants.SO_LINGER} option of
+     * @param level  level at which the {@code option} resides
+     * @param option name of the option to get
+     * @return       {@link StructLinger} associated with given {@code fd}
+     * @throws ErrnoException
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @Nullable StructLinger getsockoptLinger(@NonNull FileDescriptor fd, int level, int option) throws ErrnoException { return Libcore.os.getsockoptLinger(fd, level, option); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/setsockopt.2.html">getsockopt(2)</a>.
@@ -314,11 +391,23 @@
     /** @hide */
     public static InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException { return Libcore.os.ioctlInetAddress(fd, cmd, interfaceName); }
 
-
-    /** @hide */
-    @libcore.api.CorePlatformApi
-    public static int ioctlInt(FileDescriptor fd, int cmd, Int32Ref arg) throws ErrnoException {
-        return Libcore.os.ioctlInt(fd, cmd, arg);
+    /**
+     * See <a href="https://man7.org/linux/man-pages/man2/ioctl.2.html">ioctl(3)</a>.
+     * System call manipulates the underlying device parameters of special files. In particular,
+     * many operating characteristics of character special files.
+     *
+     * @param fd    an open file descriptor
+     * @param cmd   encoded in it whether the argument is an "in" parameter or "out" parameter
+     * @return      returns a nonnegative value on success
+     * @throws ErrnoException A checked exception thrown when {@link Os} methods fail.
+     *                        {@see android.system.ErrnoException}
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static int ioctlInt(@NonNull FileDescriptor fd, int cmd) throws ErrnoException {
+        return Libcore.os.ioctlInt(fd, cmd);
     }
 
     /**
@@ -416,9 +505,29 @@
      */
     public static FileDescriptor[] pipe() throws ErrnoException { return Libcore.os.pipe2(0); }
 
-    /** @hide */
-    @libcore.api.CorePlatformApi
-    public static FileDescriptor[] pipe2(int flags) throws ErrnoException { return Libcore.os.pipe2(flags); }
+    /**
+     * Creates a pipe, a unidirectional data channel that can be used for interprocess communication.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/pipe.2.html">pipe(2)</a>.
+     *
+     * @param flags bitmask of options, e.g. {@link OsConstants#O_CLOEXEC}, {@link OsConstants#O_DIRECT}
+     *              or {@link OsConstants#O_NONBLOCK}.
+     *              If {@code flags} is {@code 0}, then {@link pipe2(int)} is the same as {@link pipe()}.
+     * @return array of two file descriptors referring to the ends of the pipe, where
+     *         first file descriptor is the read end of the pipe, and second is a write end
+     * @throws ErrnoException if {@code flags} contains invalid value; or
+     *                        the per-process limit on the number of open file
+     *                        descriptors has been reached; or
+     *                        the system-wide limit on the total number of open files
+     *                        has been reached; or
+     *                        the user hard limit on memory that can be allocated for
+     *                        pipes has been reached and the caller is not privileged
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @Nullable FileDescriptor[] pipe2(int flags) throws ErrnoException { return Libcore.os.pipe2(flags); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/poll.2.html">poll(2)</a>.
@@ -475,11 +584,28 @@
     public static String readlink(String path) throws ErrnoException { return Libcore.os.readlink(path); }
 
     /**
+     * Eexpands all symbolic links and resolves references to {@code /./},
+     * {@code /../} and extra {@code /} characters string named by path
+     * to produce a canonicalized absolute pathname.
+     *
      * See <a href="http://man7.org/linux/man-pages/man3/realpath.3.html">realpath(3)</a>.
+     *
+     * @param path string to resolve
+     * @return     resolved path if no error occurred. Returns {@code null} if {@code path}
+     *             is {@code null}
+     * @throws ErrnoException read or search permission was denied for a component of
+     *                        the path prefix; or an I/O error occurred while reading
+     *                        from the filesystem; or too many symbolic links were
+     *                        encountered in translating the pathname; or
+     *                        the named file does not exist; or a component of the path
+     *                        prefix is not a directory
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static String realpath(String path) throws ErrnoException { return Libcore.os.realpath(path); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @libcore.api.IntraCoreApi
+    public static @Nullable String realpath(@Nullable String path) throws ErrnoException { return Libcore.os.realpath(path); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/readv.2.html">readv(2)</a>.
@@ -497,6 +623,11 @@
     public static int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
 
     /**
+     * See <a href="http://man7.org/linux/man-pages/man2/recvmsg.2.html">recvmsg(2)</a>.
+     */
+    public static int recvmsg(@NonNull FileDescriptor fd, @NonNull StructMsghdr msg, int flags) throws ErrnoException, SocketException { return Libcore.os.recvmsg(fd, msg, flags); }
+
+    /**
      * See <a href="http://man7.org/linux/man-pages/man3/remove.3.html">remove(3)</a>.
      */
     public static void remove(String path) throws ErrnoException { Libcore.os.remove(path); }
@@ -519,6 +650,13 @@
     }
 
     /**
+     * See <a href="http://man7.org/linux/man-pages/man2/sendmsg.2.html">sendmsg(2)</a>.
+     */
+    public static int sendmsg(@NonNull FileDescriptor fd, @NonNull StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+        return Libcore.os.sendmsg(fd, msg, flags);
+    }
+
+    /**
      * See <a href="http://man7.org/linux/man-pages/man2/sendto.2.html">sendto(2)</a>.
      */
     public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, buffer, flags, inetAddress, port); }
@@ -560,24 +698,62 @@
     public static void setgid(int gid) throws ErrnoException { Libcore.os.setgid(gid); }
 
     /**
+     * Sets process's pgid (process group ID).
+     *
      * See <a href="http://man7.org/linux/man-pages/man2/setpgid.2.html">setpgid(2)</a>.
+     *
+     * @param pid  process id to set the pgid of
+     * @param pgid new pgid for process {@code pid}
+     * @throws ErrnoException an attempt was made to change the process group ID of one
+     *                        of the children of the calling process and the child had
+     *                        already performed an {@link execve(String, String[], String[])}; or
+     *                        {@code pgid} is less than {@code 0}; or
+     *                        an attempt was made to move a process into a process group
+     *                        in a different session, or to change the process group ID
+     *                        of one of the children of the calling process and the
+     *                        child was in a different session, or to change the process
+     *                        group ID of a session leader; or
+     *                        {@code pid} is not the calling process and not a child
+     *                        of the calling process
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }
 
     /**
+     * Set real and/or effective group ID of the calling process.
+     *
      * See <a href="http://man7.org/linux/man-pages/man2/setregid.2.html">setregid(2)</a>.
+     *
+     * @param rgid real group ID
+     * @param egid effective group ID
+     * @throws ErrnoException one or more of the target group IDs is not valid
+     *                        in this user namespace; or the calling process is
+     *                        not privileged
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setregid(int rgid, int egid) throws ErrnoException { Libcore.os.setregid(rgid, egid); }
 
     /**
+     * Set real and/or effective user ID of the calling process.
+     *
      * See <a href="http://man7.org/linux/man-pages/man2/setreuid.2.html">setreuid(2)</a>.
+     *
+     * @param ruid real user ID
+     * @param euid effective user ID
+     * @throws ErrnoException one or more of the target user IDs is not valid
+     *                        in this user namespace; or the calling process is
+     *                        not privileged
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setreuid(int ruid, int euid) throws ErrnoException { Libcore.os.setreuid(ruid, euid); }
 
     /**
@@ -588,10 +764,29 @@
     /** @hide */
     public static void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptByte(fd, level, option, value); }
 
-    /** @hide */
+    /**
+     * Sets a supplied socket {@code option} to {@code value}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/getsockopt.2.html">getsockopt(2)</a>.
+     * For the list of available options see <a href="https://man7.org/linux/man-pages/man7/socket.7.html">socket(7)</a>.
+     * Corresponding socket options constants reside in {@link OsCosntants}, e.g. {@link OsConstants#SO_REUSEADDR}.
+     *
+     * @param fd    file descriptor of the socket to set options of
+     * @param level level at which the {@code option} resides. For example,
+     *              to indicate that an option is to be interpreted by the TCP protocol,
+     *              level should be set to the protocol number of TCP
+     * @param option name of the option to set
+     * @param value  interface name
+     * @return socket options for file descriptor {@code fd}
+     * @throws ErrnoException if {@code fd} is invalid; or
+     *                        {@code option} is unknown at given {@code level}
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { Libcore.os.setsockoptIfreq(fd, level, option, value); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void setsockoptIfreq(@NonNull FileDescriptor fd, int level, int option, @Nullable String value) throws ErrnoException { Libcore.os.setsockoptIfreq(fd, level, option, value); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/setsockopt.2.html">setsockopt(2)</a>.
@@ -604,9 +799,22 @@
     /** @hide */
     public static void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException { Libcore.os.setsockoptGroupReq(fd, level, option, value); }
 
-    /** @hide */
-    @libcore.api.CorePlatformApi
-    public static void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException { Libcore.os.setsockoptLinger(fd, level, option, value); }
+    /**
+     * Sets {@link OsConstants#SO_LINGER} option for the socket referred to by the file descriptor
+     * {@code fd}.
+     *
+     * @param fd     file descriptor
+     * @param level  level at which the {@code option} resides
+     * @param option name of the option to set
+     * @param value  {@link StructLinger} to set for {@code fd}
+     * @throws ErrnoException if {@code fd} is invalid; or
+     *                        {@code option} is unknown at given {@code level}
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void setsockoptLinger(@NonNull FileDescriptor fd, int level, int option, @NonNull StructLinger value) throws ErrnoException { Libcore.os.setsockoptLinger(fd, level, option, value); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/setsockopt.2.html">setsockopt(2)</a>.
@@ -645,11 +853,65 @@
     public static void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { Libcore.os.socketpair(domain, type, protocol, fd1, fd2); }
 
     /**
+     * Moves data between two file descriptors without copying
+     * between kernel address space and user address space. It
+     * transfers up to {@code len} bytes of data from the file descriptor {@code fdIn}
+     * to the file descriptor {@code fdOut}, where one of the file descriptors
+     * must refer to a pipe.
+     *
+     * The following semantics apply for {@code fdIn} and {@code offIn}:
+     * <ul>
+     *   <li>If {@code fdIn} refers to a pipe, then {@code offIn} must be {@code null}.</li>
+     *   <li>If {@code fdIn} does not refer to a pipe and {@code offIn} is {@code null}, then
+     *       bytes are read from {@code fdIn} starting from the file offset, and
+     *       the file offset is adjusted appropriately.</li>
+     *   <li>If {@code fdIn} does not refer to a pipe and {@code offIn} is not {@code null}, then
+     *       {@code offIn} must point to a buffer which specifies the starting
+     *       offset from which bytes will be read from {@code fdIn}; in this case,
+     *       the file offset of {@code fdIn} is not changed.</li>
+     * </ul>
+     *
+     * Analogous statements apply for {@code fdOut} and {@code offOut}.
+     *
+     * The flags argument is a bit mask that is composed by ORing
+     * together zero or more of the following values:
+     * <ul>
+     *   <li>{@link OsConstants#SPLICE_F_MOVE}
+     *       Attempt to move pages instead of copying. This is only a
+     *       hint to the kernel: pages may still be copied if the
+     *       kernel cannot move the pages from the pipe, or if the pipe
+     *       buffers don't refer to full pages.</li>
+     *   <li>{@link OsConstants#SPLICE_F_NONBLOCK}
+     *       Do not block on I/O. This makes the splice pipe
+     *       operations nonblocking, but
+     *       {@link splice(FileDescriptor, Int64Ref, FileDescriptor, Int64Ref, long, int)}
+     *       may nevertheless block because the file descriptors that are spliced
+     *       to/from may block (unless they have the {@link OsConstants#O_NONBLOCK} flag set).</li>
+     *   <li>{@link OsConstants#SPLICE_F_MORE}
+     *       More data will be coming in a subsequent splice.</li>
+     *   <li>{@link OsConstants#SPLICE_F_GIFT} Unused</li>
+     * </ul>
+     *
      * See <a href="http://man7.org/linux/man-pages/man2/splice.2.html">splice(2)</a>.
+     *
+     * @param fdIn   file descriptor to read from
+     * @param offIn  {@code null} for pipe; file offset; or pointer to a buffer that specifies starting offset
+     * @param fdOut  file descriptor to write to
+     * @param offOut {@code null} for pipe; file offset; or pointer to a buffer that specifies starting offset
+     * @param len    number of bytes to read/write
+     * @param flags  bitmask of options
+     * @return       number of bytes spliced on success. A return value of {@code 0} means end of input.
+     * @throws ErrnoException if target fs does not support splicing; or
+     *                        target file opened in append mode; or
+     *                        one or both file descriptors are invalid; or
+     *                        neither of file descriptors refer to a pipe; or
+     *                        {@code fdIn} and {@code fdOut} refer to a same pipe
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static long splice(FileDescriptor fdIn, Int64Ref offIn, FileDescriptor fdOut, Int64Ref offOut, long len, int flags) throws ErrnoException { return Libcore.os.splice(fdIn, offIn, fdOut, offOut, len, flags); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static long splice(@NonNull FileDescriptor fdIn, @Nullable Int64Ref offIn, @NonNull FileDescriptor fdOut, @Nullable Int64Ref offOut, long len, int flags) throws ErrnoException { return Libcore.os.splice(fdIn, offIn, fdOut, offOut, len, flags); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man2/stat.2.html">stat(2)</a>.
@@ -702,10 +964,24 @@
     public static StructUtsname uname() { return Libcore.os.uname(); }
 
     /**
-     * @hide See <a href="http://man7.org/linux/man-pages/man2/unlink.2.html">unlink(2)</a>.
+     * Deletes a name from the filesystem. If that name was the last link to a file
+     * and no processes have the file open, the file is deleted and the space it was
+     * using is made available for reuse.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/unlink.2.html">unlink(2)</a>.
+     *
+     * @param pathname name in the filesystem to delete
+     * @throws ErrnoException write access to {@code pathname} is not allowed; or
+     *                        I/O error occurred; or
+     *                        {@code pathname} refers to directory; or
+     *                        too many symbolic links were encountered in translating {@code pathname}; or
+     *                        {@code pathname} is used by the system or another process
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void unlink(String pathname) throws ErrnoException { Libcore.os.unlink(pathname); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void unlink(@Nullable String pathname) throws ErrnoException { Libcore.os.unlink(pathname); }
 
     /**
      * See <a href="http://man7.org/linux/man-pages/man3/unsetenv.3.html">unsetenv(3)</a>.
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 1b0dc16..2c09edb 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -16,6 +16,9 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
@@ -27,21 +30,32 @@
     }
 
     /**
-     * Returns the index of the element in the cap_user_data array that this capability is stored
-     * in.
+     * Returns the index of the element in the {@link StructCapUserData} (cap_user_data)
+     * array that this capability is stored in.
+     *
+     * @param x capability
+     * @return index of the element in the {@link StructCapUserData} array storing this capability
+     *
      * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int CAP_TO_INDEX(int x) { return x >>> 5; }
 
     /**
-     * Returns the mask for the given capability. This is relative to the capability's cap_user_data
-     * element, the index of which can be retrieved with CAP_TO_INDEX.
+     * Returns the mask for the given capability. This is relative to the capability's
+     * {@link StructCapUserData} (cap_user_data) element, the index of which can be
+     * retrieved with {@link CAP_TO_INDEX}.
+     *
+     * @param x capability
+     * @return mask for given capability
+     *
      * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int CAP_TO_MASK(int x) { return 1 << (x & 31); }
 
     /**
@@ -119,6 +133,15 @@
     public static final int AF_NETLINK = placeholder();
     public static final int AF_PACKET = placeholder();
     public static final int AF_UNIX = placeholder();
+
+    /**
+     * The virt-vsock address family, linux specific.
+     * It is used with {@code struct sockaddr_vm} from uapi/linux/vm_sockets.h.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+     * @see VmSocketAddress
+     */
+    public static final int AF_VSOCK = placeholder();
     public static final int AF_UNSPEC = placeholder();
     public static final int AI_ADDRCONFIG = placeholder();
     public static final int AI_ALL = placeholder();
@@ -128,9 +151,47 @@
     public static final int AI_PASSIVE = placeholder();
     public static final int AI_V4MAPPED = placeholder();
     public static final int ARPHRD_ETHER = placeholder();
-    /** @hide */
+
+    /**
+      * The virtio-vsock {@code svmPort} value to bind for any available port.
+      *
+      * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+      * @see VmSocketAddress
+      */
+    public static final int VMADDR_PORT_ANY = placeholder();
+
+    /**
+      * The virtio-vsock {@code svmCid} value to listens for all CIDs.
+      *
+      * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+      * @see VmSocketAddress
+      */
+    public static final int VMADDR_CID_ANY = placeholder();
+
+    /**
+      * The virtio-vsock {@code svmCid} value for host communication.
+      *
+      * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+      * @see VmSocketAddress
+      */
+    public static final int VMADDR_CID_LOCAL = placeholder();
+
+    /**
+      * The virtio-vsock {@code svmCid} value for loopback communication.
+      *
+      * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+      * @see VmSocketAddress
+      */
+    public static final int VMADDR_CID_HOST = placeholder();
+
+    /**
+     * ARP protocol loopback device identifier.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int ARPHRD_LOOPBACK = placeholder();
     public static final int CAP_AUDIT_CONTROL = placeholder();
     public static final int CAP_AUDIT_WRITE = placeholder();
@@ -231,9 +292,6 @@
     public static final int ENOLINK = placeholder();
     public static final int ENOMEM = placeholder();
     public static final int ENOMSG = placeholder();
-    /** @hide */
-    @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public static final int ENONET = placeholder();
     public static final int ENOPROTOOPT = placeholder();
     public static final int ENOSPC = placeholder();
@@ -266,9 +324,15 @@
     public static final int ETIME = placeholder();
     public static final int ETIMEDOUT = placeholder();
     public static final int ETXTBSY = placeholder();
-    /** @hide */
+    /**
+     * "Too many users" error.
+     * See <a href="https://man7.org/linux/man-pages/man3/errno.3.html">errno(3)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int EUSERS = placeholder();
     // On Linux, EWOULDBLOCK == EAGAIN. Use EAGAIN instead, to reduce confusion.
     public static final int EXDEV = placeholder();
@@ -354,15 +418,31 @@
     public static final int IP_RECVTOS = placeholder();
     public static final int IP_TOS = placeholder();
     public static final int IP_TTL = placeholder();
-    /** @hide */
+    /**
+     * Version constant to be used in {@link StructCapUserHeader} with
+     * {@link Os#capset(StructCapUserHeader, StructCapUserData[])} and
+     * {@link Os#capget(StructCapUserHeader)}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int _LINUX_CAPABILITY_VERSION_3 = placeholder();
     public static final int MAP_FIXED = placeholder();
     public static final int MAP_ANONYMOUS = placeholder();
-    /** @hide */
+    /**
+     * Flag argument for {@code mmap(long, long, int, int, FileDescriptor, long)}.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/mmap.2.html">mmap(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int MAP_POPULATE = placeholder();
     public static final int MAP_PRIVATE = placeholder();
     public static final int MAP_SHARED = placeholder();
@@ -400,9 +480,26 @@
     public static final int O_APPEND = placeholder();
     public static final int O_CLOEXEC = placeholder();
     public static final int O_CREAT = placeholder();
-    /** @hide */
+    /**
+     * Flag for {@code Os#open(String, int, int)}.
+     *
+     * When enabled, tries to minimize cache effects of the I/O to and from this
+     * file. In general this will degrade performance, but it is
+     * useful in special situations, such as when applications do
+     * their own caching. File I/O is done directly to/from
+     * user-space buffers. The {@link O_DIRECT} flag on its own makes an
+     * effort to transfer data synchronously, but does not give
+     * the guarantees of the {@link O_SYNC} flag that data and necessary
+     * metadata are transferred. To guarantee synchronous I/O,
+     * {@link O_SYNC} must be used in addition to {@link O_DIRECT}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int O_DIRECT = placeholder();
     public static final int O_EXCL = placeholder();
     public static final int O_NOCTTY = placeholder();
@@ -424,13 +521,30 @@
     public static final int POLLRDNORM = placeholder();
     public static final int POLLWRBAND = placeholder();
     public static final int POLLWRNORM = placeholder();
-    /** @hide */
+    /**
+     * Reads or changes the ambient capability set of the calling thread.
+     * Has to be used as a first argument for {@link Os#prctl(int, long, long, long, long)}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int PR_CAP_AMBIENT = placeholder();
-    /** @hide */
+    /**
+     * The capability specified in {@code arg3} of {@link Os#prctl(int, long, long, long, long)}
+     * is added to the ambient set. The specified capability must already
+     * be present in both the permitted and the inheritable sets of the process.
+     * Has to be used as a second argument for {@link Os#prctl(int, long, long, long, long)}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl(2)</a>.
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int PR_CAP_AMBIENT_RAISE = placeholder();
     public static final int PR_GET_DUMPABLE = placeholder();
     public static final int PR_SET_DUMPABLE = placeholder();
@@ -440,18 +554,38 @@
     public static final int PROT_READ = placeholder();
     public static final int PROT_WRITE = placeholder();
     public static final int R_OK = placeholder();
-    /** @hide */
+    /**
+     * Specifies a value one greater than the maximum file
+     * descriptor number that can be opened by this process.
+     *
+     * <p>Attempts ({@link Os#open(String, int, int)}, {@link Os#pipe()},
+     * {@link Os#dup(java.io.FileDescriptor)}, etc.) to exceed this
+     * limit yield the error {@link EMFILE}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man3/vlimit.3.html">getrlimit(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int RLIMIT_NOFILE = placeholder();
     public static final int RT_SCOPE_HOST = placeholder();
     public static final int RT_SCOPE_LINK = placeholder();
     public static final int RT_SCOPE_NOWHERE = placeholder();
     public static final int RT_SCOPE_SITE = placeholder();
     public static final int RT_SCOPE_UNIVERSE = placeholder();
-    /** @hide */
+    /**
+     * Bitmask for IPv4 addresses add/delete events multicast groups mask.
+     * Used in {@link NetlinkSocketAddress}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man7/netlink.7.html">netlink(7)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int RTMGRP_IPV4_IFADDR = placeholder();
     /** @hide */
     @UnsupportedAppUsage
@@ -560,6 +694,7 @@
     public static final int SOCK_SEQPACKET = placeholder();
     public static final int SOCK_STREAM = placeholder();
     public static final int SOL_SOCKET = placeholder();
+    public static final int SOL_UDP = placeholder();
     public static final int SO_BINDTODEVICE = placeholder();
     public static final int SO_BROADCAST = placeholder();
     public static final int SO_DEBUG = placeholder();
@@ -584,16 +719,40 @@
     public static final int SO_SNDLOWAT = placeholder();
     public static final int SO_SNDTIMEO = placeholder();
     public static final int SO_TYPE = placeholder();
-    /** @hide */
+    /**
+     * Bitmask for flags argument of
+     * {@link splice(java.io.FileDescriptor, Int64Ref, FileDescriptor, Int64Ref, long, int)}.
+     *
+     * Attempt to move pages instead of copying.  This is only a
+     * hint to the kernel: pages may still be copied if the
+     * kernel cannot move the pages from the pipe, or if the pipe
+     * buffers don't refer to full pages.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/splice.2.html">splice(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int SPLICE_F_MOVE = placeholder();
     /** @hide */
     @UnsupportedAppUsage
     public static final int SPLICE_F_NONBLOCK = placeholder();
-    /** @hide */
+    /**
+     * Bitmask for flags argument of
+     * {@link splice(java.io.FileDescriptor, Int64Ref, FileDescriptor, Int64Ref, long, int)}.
+     *
+     * <p>Indicates that more data will be coming in a subsequent splice. This is
+     * a helpful hint when the {@code fdOut} refers to a socket.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/splice.2.html">splice(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int SPLICE_F_MORE = placeholder();
     public static final int STDERR_FILENO = placeholder();
     public static final int STDIN_FILENO = placeholder();
@@ -632,20 +791,35 @@
     public static final int S_IXUSR = placeholder();
     public static final int TCP_NODELAY = placeholder();
     public static final int TCP_USER_TIMEOUT = placeholder();
-    /** @hide */
+    public static final int UDP_GRO = placeholder();
+    public static final int UDP_SEGMENT = placeholder();
+    /**
+     * Get the number of bytes in the output buffer.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/ioctl.2.html">ioctl(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int TIOCOUTQ = placeholder();
-    /** @hide */
+    /**
+     * Sockopt option to encapsulate ESP packets in UDP.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int UDP_ENCAP = placeholder();
     /** @hide */
     @UnsupportedAppUsage
     public static final int UDP_ENCAP_ESPINUDP_NON_IKE = placeholder();
     /** @hide */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int UDP_ENCAP_ESPINUDP = placeholder();
     /** @hide */
     @UnsupportedAppUsage
@@ -657,12 +831,32 @@
     public static final int WSTOPPED = placeholder();
     public static final int WUNTRACED = placeholder();
     public static final int W_OK = placeholder();
-    /** @hide */
+    /**
+     * {@code flags} option for {@link Os#setxattr(String, String, byte[], int)}.
+     *
+     * <p>Performs a pure create, which fails if the named attribute exists already.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/setxattr.2.html">setxattr(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int XATTR_CREATE = placeholder();
-    /** @hide */
+    /**
+     * {@code flags} option for {@link Os#setxattr(String, String, byte[], int)}.
+     *
+     * <p>Perform a pure replace operation, which fails if the named attribute
+     * does not already exist.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/setxattr.2.html">setxattr(2)</a>.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int XATTR_REPLACE = placeholder();
     public static final int X_OK = placeholder();
     public static final int _SC_2_CHAR_TERM = placeholder();
diff --git a/luni/src/main/java/android/system/PacketSocketAddress.java b/luni/src/main/java/android/system/PacketSocketAddress.java
index c3f15a6..dca8d06 100644
--- a/luni/src/main/java/android/system/PacketSocketAddress.java
+++ b/luni/src/main/java/android/system/PacketSocketAddress.java
@@ -16,6 +16,9 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.net.SocketAddress;
@@ -26,31 +29,54 @@
  *
  * Corresponds to Linux's {@code struct sockaddr_ll}.
  *
+ * See <a href="https://man7.org/linux/man-pages/man7/packet.7.html">packet(7)</a>.
+ *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class PacketSocketAddress extends SocketAddress {
-    /** Protocol. An Ethernet protocol type, e.g., {@link OsConstants#ETH_P_IPV6}. */
+    /**
+     * Protocol. An Ethernet protocol type, e.g., {@link OsConstants#ETH_P_IPV6}.
+     *
+     * @hide
+     */
     public final int sll_protocol;
 
-    /** Interface index. */
+    /**
+     * Interface index.
+     *
+     * @hide
+     */
     public final int sll_ifindex;
 
     /**
      * ARP hardware type. One of the {@code ARPHRD_*} constants, such as
      * {@link OsConstants#ARPHRD_ETHER}.
+     *
+     * @hide
      */
     public final int sll_hatype;
 
     /**
      * Packet type.
+     *
+     * @hide
      */
     public final int sll_pkttype;
 
-    /** Hardware address. */
+    /**
+     * Hardware address.
+     *
+     * @hide
+     */
     public final byte[] sll_addr;
 
-    /** Constructs a new PacketSocketAddress. Used from native code. */
+    /**
+     * Constructs a new PacketSocketAddress. Used from native code.
+     *
+     * @hide
+     */
     public PacketSocketAddress(int sll_protocol, int sll_ifindex, int sll_hatype, int sll_pkttype,
             byte[] sll_addr) {
         this.sll_protocol = sll_protocol;
@@ -60,8 +86,20 @@
         this.sll_addr = sll_addr;
     }
 
-    /** Constructs a new PacketSocketAddress with all the "in" parameters. */
-    @libcore.api.CorePlatformApi
+    /**
+     * Constructs a new PacketSocketAddress with all the "in" parameters which
+     * correspond to Linux's {@code struct sockaddr_ll}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man7/packet.7.html">packet(7)</a>.
+     *
+     * @param sll_protocol protocol field in {@code struct sockaddr_ll}
+     * @param sll_ifindex  interface index number field in {@code struct sockaddr_ll}
+     * @param sll_addr     physical-layer address field in {@code struct sockaddr_ll}
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public PacketSocketAddress(int sll_protocol, int sll_ifindex, byte[] sll_addr) {
         this.sll_protocol = sll_protocol;
         this.sll_ifindex = sll_ifindex;
@@ -70,7 +108,11 @@
         this.sll_addr = sll_addr;
     }
 
-    /** Legacy constructor. Kept for @UnsupportedAppUsage only. */
+    /**
+     * Legacy constructor. Kept for @UnsupportedAppUsage only.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
     public PacketSocketAddress(short sll_protocol, int sll_ifindex) {
         this.sll_protocol = sll_protocol;
@@ -80,7 +122,11 @@
         this.sll_addr = null;
     }
 
-    /** Legacy constructor. Kept for @UnsupportedAppUsage only. */
+    /**
+     * Legacy constructor. Kept for @UnsupportedAppUsage only.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
     public PacketSocketAddress(int sll_ifindex, byte[] sll_addr) {
         this.sll_protocol = 0;
@@ -90,6 +136,9 @@
         this.sll_addr = sll_addr;
     }
 
+    /**
+     * @hide
+     */
     @Override
     public String toString() {
         return Objects.toString(this);
diff --git a/luni/src/main/java/android/system/StructCapUserData.java b/luni/src/main/java/android/system/StructCapUserData.java
index c175898..a4aa87c 100644
--- a/luni/src/main/java/android/system/StructCapUserData.java
+++ b/luni/src/main/java/android/system/StructCapUserData.java
@@ -16,37 +16,71 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import libcore.util.Objects;
 
 /**
  * Corresponds to Linux' __user_cap_data_struct for capget and capset.
+ * Used in {@link Os.capget(StructCapUserHeader)} and
+ * {@link Os.capset(StructCapUserHeader, StructCapUserData[])}.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class StructCapUserData {
-    /** Effective capability mask. */
-    @libcore.api.CorePlatformApi
+    /**
+     * Effective capability mask.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final int effective; /* __u32 */
 
-    /** Permitted capability mask. */
-    @libcore.api.CorePlatformApi
+    /**
+     * Permitted capability mask.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final int permitted; /* __u32 */
 
-    /** Inheritable capability mask. */
-    @libcore.api.CorePlatformApi
+    /**
+     * Inheritable capability mask.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final int inheritable; /* __u32 */
 
     /**
      * Constructs an instance with the given field values.
+     *
+     * @param effective   effective capability mask
+     * @param permitted   permitted capability mask
+     * @param inheritable inheritable capability mask
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public StructCapUserData(int effective, int permitted, int inheritable) {
         this.effective = effective;
         this.permitted = permitted;
         this.inheritable = inheritable;
     }
 
+    /**
+     * @hide
+     */
     @Override public String toString() {
         return Objects.toString(this);
     }
diff --git a/luni/src/main/java/android/system/StructCapUserHeader.java b/luni/src/main/java/android/system/StructCapUserHeader.java
index aa1f876..249313a 100644
--- a/luni/src/main/java/android/system/StructCapUserHeader.java
+++ b/luni/src/main/java/android/system/StructCapUserHeader.java
@@ -16,34 +16,60 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import libcore.util.Objects;
 
 /**
  * Corresponds to Linux' __user_cap_header_struct for capget and capset.
+ * Used in {@link Os.capget(StructCapUserHeader)} and
+ * {@link Os.capset(StructCapUserHeader, StructCapUserData[])}.
  *
+ * Capabilities defined in <a href="https://man7.org/linux/man-pages/man7/capabilities.7.html">capabilities(7)</a>
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class StructCapUserHeader {
     /**
      * Version of the header. Note this is not final as capget() may mutate the field when an
-     * invalid version is provided. See
-     * <a href="http://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
+     * invalid version is provided.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
+     *
+     * @see {@link OsConstants._LINUX_CAPABILITY_VERSION_3}.
+     *
+     * @hide
      */
     public int version; /* __u32 */
 
-    /** Pid of the header. The pid a call applies to. */
+    /**
+     * Pid of the header. The pid a call applies to.
+     *
+     * @hide
+     */
     public final int pid;
 
     /**
      * Constructs an instance with the given field values.
+     *
+     * @param version linux capability version
+     * @param pid     process id
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public StructCapUserHeader(int version, int pid) {
         this.version = version;
         this.pid = pid;
     }
 
+    /**
+     * @hide
+     */
     @Override public String toString() {
         return Objects.toString(this);
     }
diff --git a/luni/src/main/java/android/system/StructCmsghdr.java b/luni/src/main/java/android/system/StructCmsghdr.java
new file mode 100755
index 0000000..1f2f3ed
--- /dev/null
+++ b/luni/src/main/java/android/system/StructCmsghdr.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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 android.system;
+
+import libcore.util.NonNull;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Corresponds to C's {@code struct cmsghdr}.
+ *
+ */
+public final class StructCmsghdr {
+    /** Originating protocol */
+    public final int cmsg_level;
+
+    /** Protocol-specific type */
+    public final int cmsg_type;
+
+    /** message data sent/received */
+    @NonNull public final byte[] cmsg_data;
+
+    public StructCmsghdr(int cmsg_level, int cmsg_type, short value) {
+        // Short.Size unit is bits, ByteBuffer data unit is bytes
+        ByteBuffer buf = ByteBuffer.allocate(Short.SIZE / 8);
+        buf.order(ByteOrder.nativeOrder());
+        buf.putShort(value);
+
+        this.cmsg_level = cmsg_level;
+        this.cmsg_type = cmsg_type;
+        this.cmsg_data = buf.array();
+    }
+
+    public StructCmsghdr(int cmsg_level, int cmsg_type, @NonNull byte[] value) {
+        this.cmsg_level = cmsg_level;
+        this.cmsg_type = cmsg_type;
+        this.cmsg_data = value;
+    }
+
+}
diff --git a/luni/src/main/java/android/system/StructLinger.java b/luni/src/main/java/android/system/StructLinger.java
index bf030a8..52f4f8e 100644
--- a/luni/src/main/java/android/system/StructLinger.java
+++ b/luni/src/main/java/android/system/StructLinger.java
@@ -16,35 +16,82 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import libcore.util.Objects;
 
 /**
  * Corresponds to C's {@code struct linger} from
  * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html">&lt;sys/socket.h&gt;</a>
  *
+ * When enabled, a {@link Os.close(java.io.FileDescriptor) or
+ * {@link Os.shutdown(java.io.FileDescriptor, int)} will
+ * not return until all queued messages for the socket have been successfully sent or the
+ * linger timeout has been reached. Otherwise, the call returns immediately and the closing is
+ * done in the background.
+ *
+ * See <a href="https://man7.org/linux/man-pages/man7/socket.7.html">socket(7)</a>
+ * for linger struct description.
+ *
+ * @see Os#getsockoptLinger(java.io.FileDescriptor, int, int).
+ * @see OsConstants#SO_LINGER
+ *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class StructLinger {
-    /** Whether or not linger is enabled. Non-zero is on. */
+    /**
+     * Whether or not linger is enabled. Non-zero is on.
+     *
+     * @hide
+     */
     public final int l_onoff;
 
-    /** Linger time in seconds. */
-    @libcore.api.CorePlatformApi
+    /**
+     * Linger time in seconds.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final int l_linger;
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Constructs linger structure.
+     *
+     * @param l_onoff  whether or not linger is enabled, non-zero is on
+     * @param l_linger linger time, in seconds
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public StructLinger(int l_onoff, int l_linger) {
         this.l_onoff = l_onoff;
         this.l_linger = l_linger;
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Returns whether linger is on or not.
+     *
+     * @return {@code true} if linger is enabled, and {@code false} otherwise
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public boolean isOn() {
         return l_onoff != 0;
     }
 
-    @Override public String toString() {
+    /**
+     * @hide
+     */
+    @Override
+    public String toString() {
         return Objects.toString(this);
     }
 }
diff --git a/luni/src/main/java/android/system/StructMsghdr.java b/luni/src/main/java/android/system/StructMsghdr.java
new file mode 100755
index 0000000..9b5dcce
--- /dev/null
+++ b/luni/src/main/java/android/system/StructMsghdr.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 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 android.system;
+
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * Corresponds to C's {@code struct msghdr}
+ *
+ */
+public final class StructMsghdr{
+    /**
+     * Optional address.
+     * <p>Sendmsg: Caller must populate to specify the target address for a datagram, or pass
+     * {@code null} to send to the destination of an already-connected socket.
+     * Recvmsg: Populated by the system to specify the source address.
+     */
+    @Nullable public SocketAddress msg_name;
+
+    /** Scatter/gather array */
+    @NonNull public final ByteBuffer[] msg_iov;
+
+    /** Ancillary data */
+    @Nullable public StructCmsghdr[] msg_control;
+
+    /** Flags on received message. */
+    public int msg_flags;
+
+    /**
+     * Constructs an instance with the given field values
+     */
+    public StructMsghdr(@Nullable SocketAddress msg_name, @NonNull ByteBuffer[] msg_iov,
+                        @Nullable StructCmsghdr[] msg_control, int msg_flags) {
+        this.msg_name = msg_name;
+        this.msg_iov = msg_iov;
+        this.msg_control = msg_control;
+        this.msg_flags = msg_flags;
+    }
+}
diff --git a/luni/src/main/java/android/system/StructRlimit.java b/luni/src/main/java/android/system/StructRlimit.java
index e1f3437..b35c075 100644
--- a/luni/src/main/java/android/system/StructRlimit.java
+++ b/luni/src/main/java/android/system/StructRlimit.java
@@ -16,25 +16,49 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import libcore.util.Objects;
 
 /**
  * Information returned by {@link Os#getrlimit}. Corresponds to C's {@code struct rlimit} from
  * {@code <sys/resource.h>}.
  *
+ * See <a href="https://man7.org/linux/man-pages/man3/vlimit.3.html">getrlimit(2)</a>.
+ *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class StructRlimit {
-    @libcore.api.CorePlatformApi
+
+    /**
+     * Soft limit
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public final long rlim_cur;
+    /**
+     * Hard limit (ceiling for rlim_cur)
+     *
+     * @hide
+     */
     public final long rlim_max;
 
+    /**
+     * @hide
+     */
     public StructRlimit(long rlim_cur, long rlim_max) {
         this.rlim_cur = rlim_cur;
         this.rlim_max = rlim_max;
     }
-
+    /**
+     * @hide
+     */
     @Override public String toString() {
         return Objects.toString(this);
     }
diff --git a/luni/src/main/java/android/system/StructTimeval.java b/luni/src/main/java/android/system/StructTimeval.java
index 559056a..7b39462 100644
--- a/luni/src/main/java/android/system/StructTimeval.java
+++ b/luni/src/main/java/android/system/StructTimeval.java
@@ -43,11 +43,10 @@
         // represented by rounding down to the nearest whole second <= the one we need
         // (i.e. floor()) and adding the necessary micro seconds.
         long tv_sec = millis / 1000;
-        long tv_usec = (millis - (tv_sec * 1000)) * 1000;
-        if (millis < 0) {
-            tv_sec -= 1;
-            tv_usec += 1_000_000;
+        if (tv_sec * 1000 > millis) {
+            --tv_sec;
         }
+        long tv_usec = (millis - (tv_sec * 1000)) * 1000;
         return new StructTimeval(tv_sec, tv_usec);
     }
 
diff --git a/luni/src/main/java/android/system/UnixSocketAddress.java b/luni/src/main/java/android/system/UnixSocketAddress.java
index 993cc5b..3a7c822 100644
--- a/luni/src/main/java/android/system/UnixSocketAddress.java
+++ b/luni/src/main/java/android/system/UnixSocketAddress.java
@@ -16,6 +16,10 @@
 
 package android.system;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import java.net.SocketAddress;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
@@ -23,9 +27,14 @@
 /**
  * A UNIX-domain (AF_UNIX / AF_LOCAL) socket address.
  *
+ * See <a href="https://man7.org/linux/man-pages/man7/unix.7.html">unix(7)</a>.
+ *
+ * @see OsConstants#AF_UNIX
+ *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class UnixSocketAddress extends SocketAddress {
 
     private static final int NAMED_PATH_LENGTH = OsConstants.UNIX_PATH_MAX;
@@ -60,6 +69,8 @@
      *
      * @throws NullPointerException if {@code name} is null
      * @throws IllegalArgumentException if {@code name} is invalid, e.g. too long
+     *
+     * @hide
      */
     public static UnixSocketAddress createAbstract(String name) {
         byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
@@ -70,12 +81,18 @@
     }
 
     /**
-     * Creates a named, filesystem AF_UNIX socket address.
+     * Creates a named, filesystem {@link OsConstants#AF_UNIX} socket address.
      *
-     * @throws NullPointerException if {@code name} is null
+     * See <a href="https://man7.org/linux/man-pages/man7/unix.7.html">unix(7)</a>.
+     *
+     * @param pathname filename for named unix socket
+     * @throws NullPointerException if {@code name} is {@code null}
      * @throws IllegalArgumentException if {@code name} is invalid, e.g. too long
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static UnixSocketAddress createFileSystem(String pathName) {
         byte[] pathNameBytes = pathName.getBytes(StandardCharsets.UTF_8);
         // File system sockets have a path that ends with (byte) 0.
@@ -86,12 +103,18 @@
 
     /**
      * Creates an unnamed, filesystem AF_UNIX socket address.
+     *
+     * @hide
      */
     public static UnixSocketAddress createUnnamed() {
         return new UnixSocketAddress(UNNAMED_PATH);
     }
 
-    /** Used for testing. */
+    /**
+     * Used for testing.
+     *
+     * @hide
+     */
     public byte[] getSunPath() {
         if (sun_path.length == 0) {
             return sun_path;
@@ -101,6 +124,9 @@
         return sunPathCopy;
     }
 
+    /**
+     * @hide
+     */
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -114,11 +140,17 @@
         return Arrays.equals(sun_path, that.sun_path);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public int hashCode() {
         return Arrays.hashCode(sun_path);
     }
 
+    /**
+     * @hide
+     */
     @Override
     public String toString() {
         return "UnixSocketAddress[" +
diff --git a/luni/src/main/java/android/system/VmSocketAddress.java b/luni/src/main/java/android/system/VmSocketAddress.java
new file mode 100644
index 0000000..1ff85bd
--- /dev/null
+++ b/luni/src/main/java/android/system/VmSocketAddress.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 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 android.system;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
+import java.net.SocketAddress;
+import libcore.api.CorePlatformApi;
+import libcore.util.Objects;
+
+/**
+ * A virtio-vsock address {@link VmSocketAddress}.
+ *
+ * <p>
+ * virtio-vsock socket address, linux specific.
+ *
+ * <p>
+ * {@link VmSocketAddress} corresponds to {@code struct sockaddr_vm} in
+ * bionic/libc/kernel/uapi/linux/vm_sockets.h.
+ *
+ * <p>
+ * Currently virtio-vsock is used as a generic purpose pipe in emulators
+ * to talk to the host.
+ *
+ * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+public final class VmSocketAddress extends SocketAddress {
+    /**
+      * sockaddr_vm::svmPort, see {@code struct sockaddr_vm} in
+      * bionic/libc/kernel/uapi/linux/vm_sockets.h for more details.
+      */
+    private int svmPort;
+
+    /**
+      * sockaddr_vm::svmCid, see {@code struct sockaddr_vm} in
+      * bionic/libc/kernel/uapi/linux/vm_sockets.h for more details.
+      */
+    private int svmCid;
+
+    /**
+     * Creates a new instance of VmSocketAddress.
+     *
+     * @param svmPort      The svmPort field value,
+     *                     see {@link OsConstants.VMADDR_PORT_ANY}.
+     * @param svmCid       The svmCid field value,
+     *                     see OsConstants.VMADDR_CID_* for VMADDR_CID_* values.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public VmSocketAddress(int svmPort, int svmCid) {
+        this.svmPort = svmPort;
+        this.svmCid = svmCid;
+    }
+
+    /**
+     * Returns the value of the svmPort field
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public int getSvmPort() {
+        return svmPort;
+    }
+
+    /**
+     * Returns the value of the svmCid field
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public int getSvmCid() {
+        return svmCid;
+    }
+}
diff --git a/luni/src/main/java/java/lang/ref/FinalizerReference.java b/luni/src/main/java/java/lang/ref/FinalizerReference.java
index 037af4a..a71044c 100644
--- a/luni/src/main/java/java/lang/ref/FinalizerReference.java
+++ b/luni/src/main/java/java/lang/ref/FinalizerReference.java
@@ -160,16 +160,15 @@
 
         synchronized void awaitFinalization(long timeout) throws InterruptedException {
             final long startTime = System.nanoTime();
-            final long endTime = startTime + timeout;
+            final long endTime = startTime + timeout;  // May wrap.
             while (!finalized) {
                 // 0 signifies no timeout.
                 if (timeout != 0) {
-                    final long currentTime = System.nanoTime();
-                    if (currentTime >= endTime) {
+                    final long deltaTime = endTime - System.nanoTime();
+                    if (deltaTime <= 0) {
                         break;
                     } else {
-                        final long deltaTime = endTime - currentTime;
-                        wait(deltaTime / 1000000, (int)(deltaTime % 1000000));
+                        wait(deltaTime / 1_000_000, (int)(deltaTime % 1_000_000));
                     }
                 } else {
                     wait();
diff --git a/luni/src/main/java/java/math/BigDecimal.java b/luni/src/main/java/java/math/BigDecimal.java
deleted file mode 100644
index 6ba251b..0000000
--- a/luni/src/main/java/java/math/BigDecimal.java
+++ /dev/null
@@ -1,2973 +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 java.math;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.Arrays;
-import libcore.math.MathUtils;
-
-/**
- * An immutable arbitrary-precision signed decimal.
- *
- * <p>A value is represented by an arbitrary-precision "unscaled value" and a signed 32-bit "scale",
- * combined thus: {@code unscaled * 10<sup>-scale</sup>}. See {@link #unscaledValue} and {@link #scale}.
- *
- * <p>Most operations allow you to supply a {@link MathContext} to specify a desired rounding mode.
- */
-public class BigDecimal extends Number implements Comparable<BigDecimal>, Serializable {
-
-    /**
-     * Rounding mode where positive values are rounded towards positive infinity
-     * and negative values towards negative infinity.
-     *
-     * @see RoundingMode#UP
-     */
-    public static final int ROUND_UP = 0;
-
-    /**
-     * Rounding mode where the values are rounded towards zero.
-     *
-     * @see RoundingMode#DOWN
-     */
-    public static final int ROUND_DOWN = 1;
-
-    /**
-     * Rounding mode to round towards positive infinity. For positive values
-     * this rounding mode behaves as {@link #ROUND_UP}, for negative values as
-     * {@link #ROUND_DOWN}.
-     *
-     * @see RoundingMode#CEILING
-     */
-    public static final int ROUND_CEILING = 2;
-
-    /**
-     * Rounding mode to round towards negative infinity. For positive values
-     * this rounding mode behaves as {@link #ROUND_DOWN}, for negative values as
-     * {@link #ROUND_UP}.
-     *
-     * @see RoundingMode#FLOOR
-     */
-    public static final int ROUND_FLOOR = 3;
-
-    /**
-     * Rounding mode where values are rounded towards the nearest neighbor.
-     * Ties are broken by rounding up.
-     *
-     * @see RoundingMode#HALF_UP
-     */
-    public static final int ROUND_HALF_UP = 4;
-
-    /**
-     * Rounding mode where values are rounded towards the nearest neighbor.
-     * Ties are broken by rounding down.
-     *
-     * @see RoundingMode#HALF_DOWN
-     */
-    public static final int ROUND_HALF_DOWN = 5;
-
-    /**
-     * Rounding mode where values are rounded towards the nearest neighbor.
-     * Ties are broken by rounding to the even neighbor.
-     *
-     * @see RoundingMode#HALF_EVEN
-     */
-    public static final int ROUND_HALF_EVEN = 6;
-
-    /**
-     * Rounding mode where the rounding operations throws an {@code
-     * ArithmeticException} for the case that rounding is necessary, i.e. for
-     * the case that the value cannot be represented exactly.
-     *
-     * @see RoundingMode#UNNECESSARY
-     */
-    public static final int ROUND_UNNECESSARY = 7;
-
-    /** This is the serialVersionUID used by the sun implementation. */
-    private static final long serialVersionUID = 6108874887143696463L;
-
-    /** The double closest to {@code Log10(2)}. */
-    private static final double LOG10_2 = 0.3010299956639812;
-
-    /** The <code>String</code> representation is cached. */
-    private transient String toStringImage = null;
-
-    /** Cache for the hash code. */
-    private transient int hashCode = 0;
-
-    /**
-     * An array with powers of five that fit in the type <code>long</code>
-     * (<code>5^0,5^1,...,5^27</code>).
-     */
-    private static final BigInteger[] FIVE_POW;
-
-    /**
-     * An array with powers of ten that fit in the type <code>long</code>
-     * (<code>10^0,10^1,...,10^18</code>).
-     */
-    private static final BigInteger[] TEN_POW;
-
-    private static final long[] LONG_FIVE_POW = new long[]
-    {   1L,
-        5L,
-        25L,
-        125L,
-        625L,
-        3125L,
-        15625L,
-        78125L,
-        390625L,
-        1953125L,
-        9765625L,
-        48828125L,
-        244140625L,
-        1220703125L,
-        6103515625L,
-        30517578125L,
-        152587890625L,
-        762939453125L,
-        3814697265625L,
-        19073486328125L,
-        95367431640625L,
-        476837158203125L,
-        2384185791015625L,
-        11920928955078125L,
-        59604644775390625L,
-        298023223876953125L,
-        1490116119384765625L,
-        7450580596923828125L, };
-
-    private static final int[] LONG_FIVE_POW_BIT_LENGTH = new int[LONG_FIVE_POW.length];
-    private static final int[] LONG_POWERS_OF_TEN_BIT_LENGTH = new int[MathUtils.LONG_POWERS_OF_TEN.length];
-
-    private static final int BI_SCALED_BY_ZERO_LENGTH = 11;
-
-    /**
-     * An array with the first <code>BigInteger</code> scaled by zero.
-     * (<code>[0,0],[1,0],...,[10,0]</code>).
-     */
-    private static final BigDecimal[] BI_SCALED_BY_ZERO = new BigDecimal[BI_SCALED_BY_ZERO_LENGTH];
-
-    /**
-     * An array with the zero number scaled by the first positive scales.
-     * (<code>0*10^0, 0*10^1, ..., 0*10^10</code>).
-     */
-    private static final BigDecimal[] ZERO_SCALED_BY = new BigDecimal[11];
-
-    /** An array filled with characters <code>'0'</code>. */
-    private static final char[] CH_ZEROS = new char[100];
-
-    static {
-        Arrays.fill(CH_ZEROS, '0');
-
-        for (int i = 0; i < ZERO_SCALED_BY.length; ++i) {
-            BI_SCALED_BY_ZERO[i] = new BigDecimal(i, 0);
-            ZERO_SCALED_BY[i] = new BigDecimal(0, i);
-        }
-        for (int i = 0; i < LONG_FIVE_POW_BIT_LENGTH.length; ++i) {
-            LONG_FIVE_POW_BIT_LENGTH[i] = bitLength(LONG_FIVE_POW[i]);
-        }
-        for (int i = 0; i < LONG_POWERS_OF_TEN_BIT_LENGTH.length; ++i) {
-            LONG_POWERS_OF_TEN_BIT_LENGTH[i] = bitLength(MathUtils.LONG_POWERS_OF_TEN[i]);
-        }
-
-        // Taking the references of useful powers.
-        TEN_POW = Multiplication.bigTenPows;
-        FIVE_POW = Multiplication.bigFivePows;
-    }
-
-    /**
-     * The constant zero as a {@code BigDecimal}.
-     */
-    public static final BigDecimal ZERO = new BigDecimal(0, 0);
-
-    /**
-     * The constant one as a {@code BigDecimal}.
-     */
-    public static final BigDecimal ONE = new BigDecimal(1, 0);
-
-    /**
-     * The constant ten as a {@code BigDecimal}.
-     */
-    public static final BigDecimal TEN = new BigDecimal(10, 0);
-
-    /**
-     * The arbitrary precision integer (unscaled value) in the internal
-     * representation of {@code BigDecimal}.
-     */
-    private BigInteger intVal;
-
-    private transient int bitLength;
-
-    private transient long smallValue;
-
-    /**
-     * The 32-bit integer scale in the internal representation of {@code BigDecimal}.
-     */
-    private int scale;
-
-    /**
-     * Represent the number of decimal digits in the unscaled value. This
-     * precision is calculated the first time, and used in the following calls
-     * of method <code>precision()</code>. Note that some call to the private
-     * method <code>inplaceRound()</code> could update this field.
-     *
-     * @see #precision()
-     * @see #inplaceRound(MathContext)
-     */
-    private transient int precision = 0;
-
-    private BigDecimal(long smallValue, int scale){
-        this.smallValue = smallValue;
-        this.scale = scale;
-        this.bitLength = bitLength(smallValue);
-    }
-
-    private BigDecimal(int smallValue, int scale){
-        this.smallValue = smallValue;
-        this.scale = scale;
-        this.bitLength = bitLength(smallValue);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a string representation
-     * given as a character array.
-     *
-     * @param in
-     *            array of characters containing the string representation of
-     *            this {@code BigDecimal}.
-     * @param offset
-     *            first index to be copied.
-     * @param len
-     *            number of characters to be used.
-     * @throws NumberFormatException
-     *             if {@code offset < 0 || len <= 0 || offset+len-1 < 0 ||
-     *             offset+len-1 >= in.length}, or if {@code in} does not
-     *             contain a valid string representation of a big decimal.
-     */
-    public BigDecimal(char[] in, int offset, int len) {
-        int begin = offset; // first index to be copied
-        int last = offset + (len - 1); // last index to be copied
-        String scaleString; // buffer for scale
-        StringBuilder unscaledBuffer; // buffer for unscaled value
-        long newScale; // the new scale
-
-        if (in == null) {
-            throw new NullPointerException("in == null");
-        }
-        if ((last >= in.length) || (offset < 0) || (len <= 0) || (last < 0)) {
-            throw new NumberFormatException("Bad offset/length: offset=" + offset +
-                    " len=" + len + " in.length=" + in.length);
-        }
-        unscaledBuffer = new StringBuilder(len);
-        int bufLength = 0;
-        // To skip a possible '+' symbol
-        if ((offset <= last) && (in[offset] == '+')) {
-            offset++;
-            begin++;
-        }
-        int counter = 0;
-        boolean wasNonZero = false;
-        // Accumulating all digits until a possible decimal point
-        for (; (offset <= last) && (in[offset] != '.') && (in[offset] != 'e') && (in[offset] != 'E'); offset++) {
-            if (!wasNonZero) {
-                if (in[offset] == '0') {
-                    counter++;
-                } else {
-                    wasNonZero = true;
-                }
-            }
-
-        }
-        unscaledBuffer.append(in, begin, offset - begin);
-        bufLength += offset - begin;
-        // A decimal point was found
-        if ((offset <= last) && (in[offset] == '.')) {
-            offset++;
-            // Accumulating all digits until a possible exponent
-            begin = offset;
-            for (; (offset <= last) && (in[offset] != 'e')
-            && (in[offset] != 'E'); offset++) {
-                if (!wasNonZero) {
-                    if (in[offset] == '0') {
-                        counter++;
-                    } else {
-                        wasNonZero = true;
-                    }
-                }
-            }
-            scale = offset - begin;
-            bufLength +=scale;
-            unscaledBuffer.append(in, begin, scale);
-        } else {
-            scale = 0;
-        }
-        // An exponent was found
-        if ((offset <= last) && ((in[offset] == 'e') || (in[offset] == 'E'))) {
-            offset++;
-            // Checking for a possible sign of scale
-            begin = offset;
-            if ((offset <= last) && (in[offset] == '+')) {
-                offset++;
-                if ((offset <= last) && (in[offset] != '-')) {
-                    begin++;
-                }
-            }
-            // Accumulating all remaining digits
-            scaleString = String.valueOf(in, begin, last + 1 - begin);
-            // Checking if the scale is defined
-            newScale = (long)scale - Integer.parseInt(scaleString);
-            scale = (int)newScale;
-            if (newScale != scale) {
-                throw new NumberFormatException("Scale out of range");
-            }
-        }
-        // Parsing the unscaled value
-        if (bufLength < 19) {
-            smallValue = Long.parseLong(unscaledBuffer.toString());
-            bitLength = bitLength(smallValue);
-        } else {
-            setUnscaledValue(new BigInteger(unscaledBuffer.toString()));
-        }
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a string representation
-     * given as a character array.
-     *
-     * @param in
-     *            array of characters containing the string representation of
-     *            this {@code BigDecimal}.
-     * @param offset
-     *            first index to be copied.
-     * @param len
-     *            number of characters to be used.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws NumberFormatException
-     *             if {@code offset < 0 || len <= 0 || offset+len-1 < 0 ||
-     *             offset+len-1 >= in.length}, or if {@code in} does not
-     *             contain a valid string representation of a big decimal.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     */
-    public BigDecimal(char[] in, int offset, int len, MathContext mc) {
-        this(in, offset, len);
-        inplaceRound(mc);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a string representation
-     * given as a character array.
-     *
-     * @param in
-     *            array of characters containing the string representation of
-     *            this {@code BigDecimal}.
-     * @throws NumberFormatException
-     *             if {@code in} does not contain a valid string representation
-     *             of a big decimal.
-     */
-    public BigDecimal(char[] in) {
-        this(in, 0, in.length);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a string representation
-     * given as a character array. The result is rounded according to the
-     * specified math context.
-     *
-     * @param in
-     *            array of characters containing the string representation of
-     *            this {@code BigDecimal}.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws NumberFormatException
-     *             if {@code in} does not contain a valid string representation
-     *             of a big decimal.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     */
-    public BigDecimal(char[] in, MathContext mc) {
-        this(in, 0, in.length);
-        inplaceRound(mc);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a string
-     * representation.
-     *
-     * @throws NumberFormatException
-     *             if {@code val} does not contain a valid string representation
-     *             of a big decimal.
-     */
-    public BigDecimal(String val) {
-        this(val.toCharArray(), 0, val.length());
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a string
-     * representation. The result is rounded according to the specified math
-     * context.
-     *
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws NumberFormatException
-     *             if {@code val} does not contain a valid string representation
-     *             of a big decimal.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     */
-    public BigDecimal(String val, MathContext mc) {
-        this(val.toCharArray(), 0, val.length());
-        inplaceRound(mc);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the 64bit double
-     * {@code val}. The constructed big decimal is equivalent to the given
-     * double. For example, {@code new BigDecimal(0.1)} is equal to {@code
-     * 0.1000000000000000055511151231257827021181583404541015625}. This happens
-     * as {@code 0.1} cannot be represented exactly in binary.
-     * <p>
-     * To generate a big decimal instance which is equivalent to {@code 0.1} use
-     * the {@code BigDecimal(String)} constructor.
-     *
-     * @param val
-     *            double value to be converted to a {@code BigDecimal} instance.
-     * @throws NumberFormatException
-     *             if {@code val} is infinity or not a number.
-     */
-    public BigDecimal(double val) {
-        if (Double.isInfinite(val) || Double.isNaN(val)) {
-            throw new NumberFormatException("Infinity or NaN: " + val);
-        }
-        long bits = Double.doubleToLongBits(val); // IEEE-754
-        long mantissa;
-        int trailingZeros;
-        // Extracting the exponent, note that the bias is 1023
-        scale = 1075 - (int)((bits >> 52) & 0x7FFL);
-        // Extracting the 52 bits of the mantissa.
-        mantissa = (scale == 1075) ? (bits & 0xFFFFFFFFFFFFFL) << 1
-                : (bits & 0xFFFFFFFFFFFFFL) | 0x10000000000000L;
-        if (mantissa == 0) {
-            scale = 0;
-            precision = 1;
-        }
-        // To simplify all factors '2' in the mantissa
-        if (scale > 0) {
-            trailingZeros = Math.min(scale, Long.numberOfTrailingZeros(mantissa));
-            mantissa >>>= trailingZeros;
-            scale -= trailingZeros;
-        }
-        // Calculating the new unscaled value and the new scale
-        if((bits >> 63) != 0) {
-            mantissa = -mantissa;
-        }
-        int mantissaBits = bitLength(mantissa);
-        if (scale < 0) {
-            bitLength = mantissaBits == 0 ? 0 : mantissaBits - scale;
-            if(bitLength < 64) {
-                smallValue = mantissa << (-scale);
-            } else {
-                BigInt bi = new BigInt();
-                bi.putLongInt(mantissa);
-                bi.shift(-scale);
-                intVal = new BigInteger(bi);
-            }
-            scale = 0;
-        } else if (scale > 0) {
-            // m * 2^e =  (m * 5^(-e)) * 10^e
-            if(scale < LONG_FIVE_POW.length
-                    && mantissaBits+LONG_FIVE_POW_BIT_LENGTH[scale] < 64) {
-                smallValue = mantissa * LONG_FIVE_POW[scale];
-                bitLength = bitLength(smallValue);
-            } else {
-                setUnscaledValue(Multiplication.multiplyByFivePow(BigInteger.valueOf(mantissa), scale));
-            }
-        } else { // scale == 0
-            smallValue = mantissa;
-            bitLength = mantissaBits;
-        }
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the 64bit double
-     * {@code val}. The constructed big decimal is equivalent to the given
-     * double. For example, {@code new BigDecimal(0.1)} is equal to {@code
-     * 0.1000000000000000055511151231257827021181583404541015625}. This happens
-     * as {@code 0.1} cannot be represented exactly in binary.
-     * <p>
-     * To generate a big decimal instance which is equivalent to {@code 0.1} use
-     * the {@code BigDecimal(String)} constructor.
-     *
-     * @param val
-     *            double value to be converted to a {@code BigDecimal} instance.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws NumberFormatException
-     *             if {@code val} is infinity or not a number.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     */
-    public BigDecimal(double val, MathContext mc) {
-        this(val);
-        inplaceRound(mc);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the given big integer
-     * {@code val}. The scale of the result is {@code 0}.
-     */
-    public BigDecimal(BigInteger val) {
-        this(val, 0);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the given big integer
-     * {@code val}. The scale of the result is {@code 0}.
-     *
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     */
-    public BigDecimal(BigInteger val, MathContext mc) {
-        this(val);
-        inplaceRound(mc);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a given unscaled value
-     * {@code unscaledVal} and a given scale. The value of this instance is
-     * {@code unscaledVal * 10<sup>-scale</sup>}).
-     *
-     * @throws NullPointerException
-     *             if {@code unscaledVal == null}.
-     */
-    public BigDecimal(BigInteger unscaledVal, int scale) {
-        if (unscaledVal == null) {
-            throw new NullPointerException("unscaledVal == null");
-        }
-        this.scale = scale;
-        setUnscaledValue(unscaledVal);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from a given unscaled value
-     * {@code unscaledVal} and a given scale. The value of this instance is
-     * {@code unscaledVal * 10<sup>-scale</sup>). The result is rounded according
-     * to the specified math context.
-     *
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     * @throws NullPointerException
-     *             if {@code unscaledVal == null}.
-     */
-    public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) {
-        this(unscaledVal, scale);
-        inplaceRound(mc);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the given int
-     * {@code val}. The scale of the result is 0.
-     *
-     * @param val
-     *            int value to be converted to a {@code BigDecimal} instance.
-     */
-    public BigDecimal(int val) {
-        this(val,0);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the given int {@code
-     * val}. The scale of the result is {@code 0}. The result is rounded
-     * according to the specified math context.
-     *
-     * @param val
-     *            int value to be converted to a {@code BigDecimal} instance.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code c.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     */
-    public BigDecimal(int val, MathContext mc) {
-        this(val,0);
-        inplaceRound(mc);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the given long {@code
-     * val}. The scale of the result is {@code 0}.
-     *
-     * @param val
-     *            long value to be converted to a {@code BigDecimal} instance.
-     */
-    public BigDecimal(long val) {
-        this(val,0);
-    }
-
-    /**
-     * Constructs a new {@code BigDecimal} instance from the given long {@code
-     * val}. The scale of the result is {@code 0}. The result is rounded
-     * according to the specified math context.
-     *
-     * @param val
-     *            long value to be converted to a {@code BigDecimal} instance.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and the new big decimal cannot be represented
-     *             within the given precision without rounding.
-     */
-    public BigDecimal(long val, MathContext mc) {
-        this(val);
-        inplaceRound(mc);
-    }
-
-    /* Public Methods */
-
-    /**
-     * Returns a new {@code BigDecimal} instance whose value is equal to {@code
-     * unscaledVal * 10<sup>-scale</sup>}). The scale of the result is {@code
-     * scale}, and its unscaled value is {@code unscaledVal}.
-     */
-    public static BigDecimal valueOf(long unscaledVal, int scale) {
-        if (scale == 0) {
-            return valueOf(unscaledVal);
-        }
-        if ((unscaledVal == 0) && (scale >= 0)
-                && (scale < ZERO_SCALED_BY.length)) {
-            return ZERO_SCALED_BY[scale];
-        }
-        return new BigDecimal(unscaledVal, scale);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance whose value is equal to {@code
-     * unscaledVal}. The scale of the result is {@code 0}, and its unscaled
-     * value is {@code unscaledVal}.
-     *
-     * @param unscaledVal
-     *            value to be converted to a {@code BigDecimal}.
-     * @return {@code BigDecimal} instance with the value {@code unscaledVal}.
-     */
-    public static BigDecimal valueOf(long unscaledVal) {
-        if ((unscaledVal >= 0) && (unscaledVal < BI_SCALED_BY_ZERO_LENGTH)) {
-            return BI_SCALED_BY_ZERO[(int)unscaledVal];
-        }
-        return new BigDecimal(unscaledVal,0);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance whose value is equal to {@code
-     * val}. The new decimal is constructed as if the {@code BigDecimal(String)}
-     * constructor is called with an argument which is equal to {@code
-     * Double.toString(val)}. For example, {@code valueOf("0.1")} is converted to
-     * (unscaled=1, scale=1), although the double {@code 0.1} cannot be
-     * represented exactly as a double value. In contrast to that, a new {@code
-     * BigDecimal(0.1)} instance has the value {@code
-     * 0.1000000000000000055511151231257827021181583404541015625} with an
-     * unscaled value {@code 1000000000000000055511151231257827021181583404541015625}
-     * and the scale {@code 55}.
-     *
-     * @param val
-     *            double value to be converted to a {@code BigDecimal}.
-     * @return {@code BigDecimal} instance with the value {@code val}.
-     * @throws NumberFormatException
-     *             if {@code val} is infinite or {@code val} is not a number
-     */
-    public static BigDecimal valueOf(double val) {
-        if (Double.isInfinite(val) || Double.isNaN(val)) {
-            throw new NumberFormatException("Infinity or NaN: " + val);
-        }
-        return new BigDecimal(Double.toString(val));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this + augend}.
-     * The scale of the result is the maximum of the scales of the two
-     * arguments.
-     *
-     * @param augend
-     *            value to be added to {@code this}.
-     * @return {@code this + augend}.
-     * @throws NullPointerException
-     *             if {@code augend == null}.
-     */
-    public BigDecimal add(BigDecimal augend) {
-        int diffScale = this.scale - augend.scale;
-        // Fast return when some operand is zero
-        if (this.isZero()) {
-            if (diffScale <= 0) {
-                return augend;
-            }
-            if (augend.isZero()) {
-                return this;
-            }
-        } else if (augend.isZero()) {
-            if (diffScale >= 0) {
-                return this;
-            }
-        }
-        // Let be:  this = [u1,s1]  and  augend = [u2,s2]
-        if (diffScale == 0) {
-            // case s1 == s2: [u1 + u2 , s1]
-            if (Math.max(this.bitLength, augend.bitLength) + 1 < 64) {
-                return valueOf(this.smallValue + augend.smallValue, this.scale);
-            }
-            return new BigDecimal(this.getUnscaledValue().add(augend.getUnscaledValue()), this.scale);
-        } else if (diffScale > 0) {
-            // case s1 > s2 : [(u1 + u2) * 10 ^ (s1 - s2) , s1]
-            return addAndMult10(this, augend, diffScale);
-        } else {// case s2 > s1 : [(u2 + u1) * 10 ^ (s2 - s1) , s2]
-            return addAndMult10(augend, this, -diffScale);
-        }
-    }
-
-    private static BigDecimal addAndMult10(BigDecimal thisValue,BigDecimal augend, int diffScale) {
-        if(diffScale < MathUtils.LONG_POWERS_OF_TEN.length &&
-                Math.max(thisValue.bitLength,augend.bitLength+LONG_POWERS_OF_TEN_BIT_LENGTH[diffScale])+1<64) {
-            return valueOf(thisValue.smallValue+augend.smallValue*MathUtils.LONG_POWERS_OF_TEN[diffScale],thisValue.scale);
-        } else {
-            BigInt bi = Multiplication.multiplyByTenPow(augend.getUnscaledValue(),diffScale).getBigInt();
-            bi.add(thisValue.getUnscaledValue().getBigInt());
-            return new BigDecimal(new BigInteger(bi), thisValue.scale);
-        }
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this + augend}.
-     * The result is rounded according to the passed context {@code mc}.
-     *
-     * @param augend
-     *            value to be added to {@code this}.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @return {@code this + augend}.
-     * @throws NullPointerException
-     *             if {@code augend == null} or {@code mc == null}.
-     */
-    public BigDecimal add(BigDecimal augend, MathContext mc) {
-        BigDecimal larger; // operand with the largest unscaled value
-        BigDecimal smaller; // operand with the smallest unscaled value
-        BigInteger tempBI;
-        long diffScale = (long)this.scale - augend.scale;
-        int largerSignum;
-        // Some operand is zero or the precision is infinity
-        if ((augend.isZero()) || (this.isZero())
-                || (mc.getPrecision() == 0)) {
-            return add(augend).round(mc);
-        }
-        // Cases where there is room for optimizations
-        if (this.approxPrecision() < diffScale - 1) {
-            larger = augend;
-            smaller = this;
-        } else if (augend.approxPrecision() < -diffScale - 1) {
-            larger = this;
-            smaller = augend;
-        } else {// No optimization is done
-            return add(augend).round(mc);
-        }
-        if (mc.getPrecision() >= larger.approxPrecision()) {
-            // No optimization is done
-            return add(augend).round(mc);
-        }
-        // Cases where it's unnecessary to add two numbers with very different scales
-        largerSignum = larger.signum();
-        if (largerSignum == smaller.signum()) {
-            tempBI = Multiplication.multiplyByPositiveInt(larger.getUnscaledValue(),10)
-            .add(BigInteger.valueOf(largerSignum));
-        } else {
-            tempBI = larger.getUnscaledValue().subtract(
-                    BigInteger.valueOf(largerSignum));
-            tempBI = Multiplication.multiplyByPositiveInt(tempBI,10)
-            .add(BigInteger.valueOf(largerSignum * 9));
-        }
-        // Rounding the improved adding
-        larger = new BigDecimal(tempBI, larger.scale + 1);
-        return larger.round(mc);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this - subtrahend}.
-     * The scale of the result is the maximum of the scales of the two arguments.
-     *
-     * @param subtrahend
-     *            value to be subtracted from {@code this}.
-     * @return {@code this - subtrahend}.
-     * @throws NullPointerException
-     *             if {@code subtrahend == null}.
-     */
-    public BigDecimal subtract(BigDecimal subtrahend) {
-        int diffScale = this.scale - subtrahend.scale;
-        // Fast return when some operand is zero
-        if (this.isZero()) {
-            if (diffScale <= 0) {
-                return subtrahend.negate();
-            }
-            if (subtrahend.isZero()) {
-                return this;
-            }
-        } else if (subtrahend.isZero()) {
-            if (diffScale >= 0) {
-                return this;
-            }
-        }
-        // Let be: this = [u1,s1] and subtrahend = [u2,s2] so:
-        if (diffScale == 0) {
-            // case s1 = s2 : [u1 - u2 , s1]
-            if (Math.max(this.bitLength, subtrahend.bitLength) + 1 < 64) {
-                return valueOf(this.smallValue - subtrahend.smallValue,this.scale);
-            }
-            return new BigDecimal(this.getUnscaledValue().subtract(subtrahend.getUnscaledValue()), this.scale);
-        } else if (diffScale > 0) {
-            // case s1 > s2 : [ u1 - u2 * 10 ^ (s1 - s2) , s1 ]
-            if(diffScale < MathUtils.LONG_POWERS_OF_TEN.length &&
-                    Math.max(this.bitLength,subtrahend.bitLength+LONG_POWERS_OF_TEN_BIT_LENGTH[diffScale])+1<64) {
-                return valueOf(this.smallValue-subtrahend.smallValue*MathUtils.LONG_POWERS_OF_TEN[diffScale],this.scale);
-            }
-            return new BigDecimal(this.getUnscaledValue().subtract(
-                    Multiplication.multiplyByTenPow(subtrahend.getUnscaledValue(),diffScale)), this.scale);
-        } else {// case s2 > s1 : [ u1 * 10 ^ (s2 - s1) - u2 , s2 ]
-            diffScale = -diffScale;
-            if(diffScale < MathUtils.LONG_POWERS_OF_TEN.length &&
-                    Math.max(this.bitLength+LONG_POWERS_OF_TEN_BIT_LENGTH[diffScale],subtrahend.bitLength)+1<64) {
-                return valueOf(this.smallValue*MathUtils.LONG_POWERS_OF_TEN[diffScale]-subtrahend.smallValue,subtrahend.scale);
-            }
-            return new BigDecimal(Multiplication.multiplyByTenPow(this.getUnscaledValue(),diffScale)
-            .subtract(subtrahend.getUnscaledValue()), subtrahend.scale);
-        }
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this - subtrahend}.
-     * The result is rounded according to the passed context {@code mc}.
-     *
-     * @param subtrahend
-     *            value to be subtracted from {@code this}.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @return {@code this - subtrahend}.
-     * @throws NullPointerException
-     *             if {@code subtrahend == null} or {@code mc == null}.
-     */
-    public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) {
-        long diffScale = subtrahend.scale - (long)this.scale;
-        int thisSignum;
-        BigDecimal leftOperand; // it will be only the left operand (this)
-        BigInteger tempBI;
-        // Some operand is zero or the precision is infinity
-        if ((subtrahend.isZero()) || (this.isZero())
-                || (mc.getPrecision() == 0)) {
-            return subtract(subtrahend).round(mc);
-        }
-        // Now:   this != 0   and   subtrahend != 0
-        if (subtrahend.approxPrecision() < diffScale - 1) {
-            // Cases where it is unnecessary to subtract two numbers with very different scales
-            if (mc.getPrecision() < this.approxPrecision()) {
-                thisSignum = this.signum();
-                if (thisSignum != subtrahend.signum()) {
-                    tempBI = Multiplication.multiplyByPositiveInt(this.getUnscaledValue(), 10)
-                    .add(BigInteger.valueOf(thisSignum));
-                } else {
-                    tempBI = this.getUnscaledValue().subtract(BigInteger.valueOf(thisSignum));
-                    tempBI = Multiplication.multiplyByPositiveInt(tempBI, 10)
-                    .add(BigInteger.valueOf(thisSignum * 9));
-                }
-                // Rounding the improved subtracting
-                leftOperand = new BigDecimal(tempBI, this.scale + 1);
-                return leftOperand.round(mc);
-            }
-        }
-        // No optimization is done
-        return subtract(subtrahend).round(mc);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this *
-     * multiplicand}. The scale of the result is the sum of the scales of the
-     * two arguments.
-     *
-     * @param multiplicand
-     *            value to be multiplied with {@code this}.
-     * @return {@code this * multiplicand}.
-     * @throws NullPointerException
-     *             if {@code multiplicand == null}.
-     */
-    public BigDecimal multiply(BigDecimal multiplicand) {
-        long newScale = (long)this.scale + multiplicand.scale;
-
-        if ((this.isZero()) || (multiplicand.isZero())) {
-            return zeroScaledBy(newScale);
-        }
-        /* Let be: this = [u1,s1] and multiplicand = [u2,s2] so:
-         * this x multiplicand = [ s1 * s2 , s1 + s2 ] */
-        if (this.bitLength + multiplicand.bitLength < 64) {
-            long unscaledValue = this.smallValue * multiplicand.smallValue;
-            // b/19185440 Case where result should be +2^63 but unscaledValue overflowed to -2^63
-            boolean longMultiplicationOverflowed = (unscaledValue == Long.MIN_VALUE) &&
-                    (Math.signum(smallValue) * Math.signum(multiplicand.smallValue) > 0);
-            if (!longMultiplicationOverflowed) {
-                return valueOf(unscaledValue, safeLongToInt(newScale));
-            }
-        }
-        return new BigDecimal(this.getUnscaledValue().multiply(
-                multiplicand.getUnscaledValue()), safeLongToInt(newScale));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this *
-     * multiplicand}. The result is rounded according to the passed context
-     * {@code mc}.
-     *
-     * @param multiplicand
-     *            value to be multiplied with {@code this}.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @return {@code this * multiplicand}.
-     * @throws NullPointerException
-     *             if {@code multiplicand == null} or {@code mc == null}.
-     */
-    public BigDecimal multiply(BigDecimal multiplicand, MathContext mc) {
-        BigDecimal result = multiply(multiplicand);
-
-        result.inplaceRound(mc);
-        return result;
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
-     * As scale of the result the parameter {@code scale} is used. If rounding
-     * is required to meet the specified scale, then the specified rounding mode
-     * {@code roundingMode} is applied.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param scale
-     *            the scale of the result returned.
-     * @param roundingMode
-     *            rounding mode to be used to round the result.
-     * @return {@code this / divisor} rounded according to the given rounding
-     *         mode.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws IllegalArgumentException
-     *             if {@code roundingMode} is not a valid rounding mode.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
-     *             necessary according to the given scale.
-     */
-    public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
-        return divide(divisor, scale, RoundingMode.valueOf(roundingMode));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
-     * As scale of the result the parameter {@code scale} is used. If rounding
-     * is required to meet the specified scale, then the specified rounding mode
-     * {@code roundingMode} is applied.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param scale
-     *            the scale of the result returned.
-     * @param roundingMode
-     *            rounding mode to be used to round the result.
-     * @return {@code this / divisor} rounded according to the given rounding
-     *         mode.
-     * @throws NullPointerException
-     *             if {@code divisor == null} or {@code roundingMode == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if {@code roundingMode == RoundingMode.UNNECESSAR}Y and
-     *             rounding is necessary according to the given scale and given
-     *             precision.
-     */
-    public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
-        // Let be: this = [u1,s1]  and  divisor = [u2,s2]
-        if (roundingMode == null) {
-            throw new NullPointerException("roundingMode == null");
-        }
-        if (divisor.isZero()) {
-            throw new ArithmeticException("Division by zero");
-        }
-
-        long diffScale = ((long)this.scale - divisor.scale) - scale;
-
-        // Check whether the diffScale will fit into an int. See http://b/17393664.
-        if (bitLength(diffScale) > 32) {
-            throw new ArithmeticException(
-                    "Unable to perform divisor / dividend scaling: the difference in scale is too" +
-                            " big (" + diffScale + ")");
-        }
-
-        if(this.bitLength < 64 && divisor.bitLength < 64 ) {
-            if(diffScale == 0) {
-                // http://b/26105053 - corner case: Long.MIN_VALUE / (-1) overflows a long
-                if (this.smallValue != Long.MIN_VALUE || divisor.smallValue != -1) {
-                    return dividePrimitiveLongs(this.smallValue,
-                            divisor.smallValue,
-                            scale,
-                            roundingMode);
-                }
-            } else if(diffScale > 0) {
-                if(diffScale < MathUtils.LONG_POWERS_OF_TEN.length &&
-                        divisor.bitLength + LONG_POWERS_OF_TEN_BIT_LENGTH[(int)diffScale] < 64) {
-                    return dividePrimitiveLongs(this.smallValue,
-                            divisor.smallValue*MathUtils.LONG_POWERS_OF_TEN[(int)diffScale],
-                            scale,
-                            roundingMode);
-                }
-            } else { // diffScale < 0
-                if(-diffScale < MathUtils.LONG_POWERS_OF_TEN.length &&
-                        this.bitLength + LONG_POWERS_OF_TEN_BIT_LENGTH[(int)-diffScale] < 64) {
-                    return dividePrimitiveLongs(this.smallValue*MathUtils.LONG_POWERS_OF_TEN[(int)-diffScale],
-                            divisor.smallValue,
-                            scale,
-                            roundingMode);
-                }
-
-            }
-        }
-        BigInteger scaledDividend = this.getUnscaledValue();
-        BigInteger scaledDivisor = divisor.getUnscaledValue(); // for scaling of 'u2'
-
-        if (diffScale > 0) {
-            // Multiply 'u2'  by:  10^((s1 - s2) - scale)
-            scaledDivisor = Multiplication.multiplyByTenPow(scaledDivisor, (int)diffScale);
-        } else if (diffScale < 0) {
-            // Multiply 'u1'  by:  10^(scale - (s1 - s2))
-            scaledDividend  = Multiplication.multiplyByTenPow(scaledDividend, (int)-diffScale);
-        }
-        return divideBigIntegers(scaledDividend, scaledDivisor, scale, roundingMode);
-        }
-
-    private static BigDecimal divideBigIntegers(BigInteger scaledDividend, BigInteger scaledDivisor, int scale, RoundingMode roundingMode) {
-
-        BigInteger[] quotAndRem = scaledDividend.divideAndRemainder(scaledDivisor);  // quotient and remainder
-        // If after division there is a remainder...
-        BigInteger quotient = quotAndRem[0];
-        BigInteger remainder = quotAndRem[1];
-        if (remainder.signum() == 0) {
-            return new BigDecimal(quotient, scale);
-        }
-        int sign = scaledDividend.signum() * scaledDivisor.signum();
-        int compRem;                                      // 'compare to remainder'
-        if(scaledDivisor.bitLength() < 63) { // 63 in order to avoid out of long after *2
-            long rem = remainder.longValue();
-            long divisor = scaledDivisor.longValue();
-            compRem = compareForRounding(rem, divisor);
-            // To look if there is a carry
-            compRem = roundingBehavior(quotient.testBit(0) ? 1 : 0,
-                    sign * (5 + compRem), roundingMode);
-
-        } else {
-            // Checking if:  remainder * 2 >= scaledDivisor
-            compRem = remainder.abs().shiftLeftOneBit().compareTo(scaledDivisor.abs());
-            compRem = roundingBehavior(quotient.testBit(0) ? 1 : 0,
-                    sign * (5 + compRem), roundingMode);
-        }
-            if (compRem != 0) {
-            if(quotient.bitLength() < 63) {
-                return valueOf(quotient.longValue() + compRem,scale);
-            }
-            quotient = quotient.add(BigInteger.valueOf(compRem));
-            return new BigDecimal(quotient, scale);
-        }
-        // Constructing the result with the appropriate unscaled value
-        return new BigDecimal(quotient, scale);
-    }
-
-    private static BigDecimal dividePrimitiveLongs(long scaledDividend, long scaledDivisor, int scale, RoundingMode roundingMode) {
-        long quotient = scaledDividend / scaledDivisor;
-        long remainder = scaledDividend % scaledDivisor;
-        int sign = Long.signum( scaledDividend ) * Long.signum( scaledDivisor );
-        if (remainder != 0) {
-            // Checking if:  remainder * 2 >= scaledDivisor
-            int compRem = compareForRounding(remainder, scaledDivisor); // 'compare to remainder'
-            // To look if there is a carry
-            quotient += roundingBehavior(((int)quotient) & 1,
-                    sign * (5 + compRem),
-                    roundingMode);
-        }
-        // Constructing the result with the appropriate unscaled value
-        return valueOf(quotient, scale);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
-     * The scale of the result is the scale of {@code this}. If rounding is
-     * required to meet the specified scale, then the specified rounding mode
-     * {@code roundingMode} is applied.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param roundingMode
-     *            rounding mode to be used to round the result.
-     * @return {@code this / divisor} rounded according to the given rounding
-     *         mode.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws IllegalArgumentException
-     *             if {@code roundingMode} is not a valid rounding mode.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
-     *             necessary according to the scale of this.
-     */
-    public BigDecimal divide(BigDecimal divisor, int roundingMode) {
-        return divide(divisor, scale, RoundingMode.valueOf(roundingMode));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
-     * The scale of the result is the scale of {@code this}. If rounding is
-     * required to meet the specified scale, then the specified rounding mode
-     * {@code roundingMode} is applied.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param roundingMode
-     *            rounding mode to be used to round the result.
-     * @return {@code this / divisor} rounded according to the given rounding
-     *         mode.
-     * @throws NullPointerException
-     *             if {@code divisor == null} or {@code roundingMode == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if {@code roundingMode == RoundingMode.UNNECESSARY} and
-     *             rounding is necessary according to the scale of this.
-     */
-    public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) {
-        return divide(divisor, scale, roundingMode);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
-     * The scale of the result is the difference of the scales of {@code this}
-     * and {@code divisor}. If the exact result requires more digits, then the
-     * scale is adjusted accordingly. For example, {@code 1/128 = 0.0078125}
-     * which has a scale of {@code 7} and precision {@code 5}.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @return {@code this / divisor}.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if the result cannot be represented exactly.
-     */
-    public BigDecimal divide(BigDecimal divisor) {
-        BigInteger p = this.getUnscaledValue();
-        BigInteger q = divisor.getUnscaledValue();
-        BigInteger gcd; // greatest common divisor between 'p' and 'q'
-        BigInteger quotAndRem[];
-        long diffScale = (long)scale - divisor.scale;
-        int newScale; // the new scale for final quotient
-        int k; // number of factors "2" in 'q'
-        int l = 0; // number of factors "5" in 'q'
-        int i = 1;
-        int lastPow = FIVE_POW.length - 1;
-
-        if (divisor.isZero()) {
-            throw new ArithmeticException("Division by zero");
-        }
-        if (p.signum() == 0) {
-            return zeroScaledBy(diffScale);
-        }
-        // To divide both by the GCD
-        gcd = p.gcd(q);
-        p = p.divide(gcd);
-        q = q.divide(gcd);
-        // To simplify all "2" factors of q, dividing by 2^k
-        k = q.getLowestSetBit();
-        q = q.shiftRight(k);
-        // To simplify all "5" factors of q, dividing by 5^l
-        do {
-            quotAndRem = q.divideAndRemainder(FIVE_POW[i]);
-            if (quotAndRem[1].signum() == 0) {
-                l += i;
-                if (i < lastPow) {
-                    i++;
-                }
-                q = quotAndRem[0];
-            } else {
-                if (i == 1) {
-                    break;
-                }
-                i = 1;
-            }
-        } while (true);
-        // If  abs(q) != 1  then the quotient is periodic
-        if (!q.abs().equals(BigInteger.ONE)) {
-            throw new ArithmeticException("Non-terminating decimal expansion; no exact representable decimal result");
-        }
-        // The sign of the is fixed and the quotient will be saved in 'p'
-        if (q.signum() < 0) {
-            p = p.negate();
-        }
-        // Checking if the new scale is out of range
-        newScale = safeLongToInt(diffScale + Math.max(k, l));
-        // k >= 0  and  l >= 0  implies that  k - l  is in the 32-bit range
-        i = k - l;
-
-        p = (i > 0) ? Multiplication.multiplyByFivePow(p, i)
-        : p.shiftLeft(-i);
-        return new BigDecimal(p, newScale);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
-     * The result is rounded according to the passed context {@code mc}. If the
-     * passed math context specifies precision {@code 0}, then this call is
-     * equivalent to {@code this.divide(divisor)}.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @return {@code this / divisor}.
-     * @throws NullPointerException
-     *             if {@code divisor == null} or {@code mc == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if {@code mc.getRoundingMode() == UNNECESSARY} and rounding
-     *             is necessary according {@code mc.getPrecision()}.
-     */
-    public BigDecimal divide(BigDecimal divisor, MathContext mc) {
-        /* Calculating how many zeros must be append to 'dividend'
-         * to obtain a  quotient with at least 'mc.precision()' digits */
-        long trailingZeros = mc.getPrecision() + 2L
-                + divisor.approxPrecision() - approxPrecision();
-        long diffScale = (long)scale - divisor.scale;
-        long newScale = diffScale; // scale of the final quotient
-        int compRem; // to compare the remainder
-        int i = 1; // index
-        int lastPow = TEN_POW.length - 1; // last power of ten
-        BigInteger integerQuot; // for temporal results
-        BigInteger quotAndRem[] = {getUnscaledValue()};
-        // In special cases it reduces the problem to call the dual method
-        if ((mc.getPrecision() == 0) || (this.isZero())
-        || (divisor.isZero())) {
-            return this.divide(divisor);
-        }
-        if (trailingZeros > 0) {
-            // To append trailing zeros at end of dividend
-            quotAndRem[0] = getUnscaledValue().multiply( Multiplication.powerOf10(trailingZeros) );
-            newScale += trailingZeros;
-        }
-        quotAndRem = quotAndRem[0].divideAndRemainder( divisor.getUnscaledValue() );
-        integerQuot = quotAndRem[0];
-        // Calculating the exact quotient with at least 'mc.precision()' digits
-        if (quotAndRem[1].signum() != 0) {
-            // Checking if:   2 * remainder >= divisor ?
-            compRem = quotAndRem[1].shiftLeftOneBit().compareTo( divisor.getUnscaledValue() );
-            // quot := quot * 10 + r;     with 'r' in {-6,-5,-4, 0,+4,+5,+6}
-            integerQuot = integerQuot.multiply(BigInteger.TEN)
-            .add(BigInteger.valueOf(quotAndRem[0].signum() * (5 + compRem)));
-            newScale++;
-        } else {
-            // To strip trailing zeros until the preferred scale is reached
-            while (!integerQuot.testBit(0)) {
-                quotAndRem = integerQuot.divideAndRemainder(TEN_POW[i]);
-                if ((quotAndRem[1].signum() == 0)
-                        && (newScale - i >= diffScale)) {
-                    newScale -= i;
-                    if (i < lastPow) {
-                        i++;
-                    }
-                    integerQuot = quotAndRem[0];
-                } else {
-                    if (i == 1) {
-                        break;
-                    }
-                    i = 1;
-                }
-            }
-        }
-        // To perform rounding
-        return new BigDecimal(integerQuot, safeLongToInt(newScale), mc);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is the integral part of
-     * {@code this / divisor}. The quotient is rounded down towards zero to the
-     * next integer. For example, {@code 0.5/0.2 = 2}.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @return integral part of {@code this / divisor}.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     */
-    public BigDecimal divideToIntegralValue(BigDecimal divisor) {
-        BigInteger integralValue; // the integer of result
-        BigInteger powerOfTen; // some power of ten
-
-        long newScale = (long)this.scale - divisor.scale;
-        long tempScale = 0;
-        int i = 1;
-        int lastPow = TEN_POW.length - 1;
-
-        if (divisor.isZero()) {
-            throw new ArithmeticException("Division by zero");
-        }
-        if ((divisor.approxPrecision() + newScale > this.approxPrecision() + 1L)
-        || (this.isZero())) {
-            /* If the divisor's integer part is greater than this's integer part,
-             * the result must be zero with the appropriate scale */
-            integralValue = BigInteger.ZERO;
-        } else if (newScale == 0) {
-            integralValue = getUnscaledValue().divide( divisor.getUnscaledValue() );
-        } else if (newScale > 0) {
-            powerOfTen = Multiplication.powerOf10(newScale);
-            integralValue = getUnscaledValue().divide( divisor.getUnscaledValue().multiply(powerOfTen) );
-            integralValue = integralValue.multiply(powerOfTen);
-        } else {// (newScale < 0)
-            powerOfTen = Multiplication.powerOf10(-newScale);
-            integralValue = getUnscaledValue().multiply(powerOfTen).divide( divisor.getUnscaledValue() );
-            // To strip trailing zeros approximating to the preferred scale
-            while (!integralValue.testBit(0)) {
-                BigInteger[] quotAndRem = integralValue.divideAndRemainder(TEN_POW[i]);
-                if ((quotAndRem[1].signum() == 0)
-                        && (tempScale - i >= newScale)) {
-                    tempScale -= i;
-                    if (i < lastPow) {
-                        i++;
-                    }
-                    integralValue = quotAndRem[0];
-                } else {
-                    if (i == 1) {
-                        break;
-                    }
-                    i = 1;
-                }
-            }
-            newScale = tempScale;
-        }
-        return ((integralValue.signum() == 0)
-        ? zeroScaledBy(newScale)
-                : new BigDecimal(integralValue, safeLongToInt(newScale)));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is the integral part of
-     * {@code this / divisor}. The quotient is rounded down towards zero to the
-     * next integer. The rounding mode passed with the parameter {@code mc} is
-     * not considered. But if the precision of {@code mc > 0} and the integral
-     * part requires more digits, then an {@code ArithmeticException} is thrown.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param mc
-     *            math context which determines the maximal precision of the
-     *            result.
-     * @return integral part of {@code this / divisor}.
-     * @throws NullPointerException
-     *             if {@code divisor == null} or {@code mc == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if {@code mc.getPrecision() > 0} and the result requires more
-     *             digits to be represented.
-     */
-    public BigDecimal divideToIntegralValue(BigDecimal divisor, MathContext mc) {
-        int mcPrecision = mc.getPrecision();
-        int diffPrecision = this.precision() - divisor.precision();
-        int lastPow = TEN_POW.length - 1;
-        long diffScale = (long)this.scale - divisor.scale;
-        long newScale = diffScale;
-        long quotPrecision = diffPrecision - diffScale + 1;
-        BigInteger quotAndRem[] = new BigInteger[2];
-        // In special cases it call the dual method
-        if ((mcPrecision == 0) || (this.isZero()) || (divisor.isZero())) {
-            return this.divideToIntegralValue(divisor);
-        }
-        // Let be:   this = [u1,s1]   and   divisor = [u2,s2]
-        if (quotPrecision <= 0) {
-            quotAndRem[0] = BigInteger.ZERO;
-        } else if (diffScale == 0) {
-            // CASE s1 == s2:  to calculate   u1 / u2
-            quotAndRem[0] = this.getUnscaledValue().divide( divisor.getUnscaledValue() );
-        } else if (diffScale > 0) {
-            // CASE s1 >= s2:  to calculate   u1 / (u2 * 10^(s1-s2)
-            quotAndRem[0] = this.getUnscaledValue().divide(
-                    divisor.getUnscaledValue().multiply(Multiplication.powerOf10(diffScale)) );
-            // To chose  10^newScale  to get a quotient with at least 'mc.precision()' digits
-            newScale = Math.min(diffScale, Math.max(mcPrecision - quotPrecision + 1, 0));
-            // To calculate: (u1 / (u2 * 10^(s1-s2)) * 10^newScale
-            quotAndRem[0] = quotAndRem[0].multiply(Multiplication.powerOf10(newScale));
-        } else {// CASE s2 > s1:
-            /* To calculate the minimum power of ten, such that the quotient
-             *   (u1 * 10^exp) / u2   has at least 'mc.precision()' digits. */
-            long exp = Math.min(-diffScale, Math.max((long)mcPrecision - diffPrecision, 0));
-            long compRemDiv;
-            // Let be:   (u1 * 10^exp) / u2 = [q,r]
-            quotAndRem = this.getUnscaledValue().multiply(Multiplication.powerOf10(exp)).
-                    divideAndRemainder(divisor.getUnscaledValue());
-            newScale += exp; // To fix the scale
-            exp = -newScale; // The remaining power of ten
-            // If after division there is a remainder...
-            if ((quotAndRem[1].signum() != 0) && (exp > 0)) {
-                // Log10(r) + ((s2 - s1) - exp) > mc.precision ?
-                compRemDiv = (new BigDecimal(quotAndRem[1])).precision()
-                + exp - divisor.precision();
-                if (compRemDiv == 0) {
-                    // To calculate:  (r * 10^exp2) / u2
-                    quotAndRem[1] = quotAndRem[1].multiply(Multiplication.powerOf10(exp)).
-                            divide(divisor.getUnscaledValue());
-                    compRemDiv = Math.abs(quotAndRem[1].signum());
-                }
-                if (compRemDiv > 0) {
-                    throw new ArithmeticException("Division impossible");
-                }
-            }
-        }
-        // Fast return if the quotient is zero
-        if (quotAndRem[0].signum() == 0) {
-            return zeroScaledBy(diffScale);
-        }
-        BigInteger strippedBI = quotAndRem[0];
-        BigDecimal integralValue = new BigDecimal(quotAndRem[0]);
-        long resultPrecision = integralValue.precision();
-        int i = 1;
-        // To strip trailing zeros until the specified precision is reached
-        while (!strippedBI.testBit(0)) {
-            quotAndRem = strippedBI.divideAndRemainder(TEN_POW[i]);
-            if ((quotAndRem[1].signum() == 0) &&
-                    ((resultPrecision - i >= mcPrecision)
-                    || (newScale - i >= diffScale)) ) {
-                resultPrecision -= i;
-                newScale -= i;
-                if (i < lastPow) {
-                    i++;
-                }
-                strippedBI = quotAndRem[0];
-            } else {
-                if (i == 1) {
-                    break;
-                }
-                i = 1;
-            }
-        }
-        // To check if the result fit in 'mc.precision()' digits
-        if (resultPrecision > mcPrecision) {
-            throw new ArithmeticException("Division impossible");
-        }
-        integralValue.scale = safeLongToInt(newScale);
-        integralValue.setUnscaledValue(strippedBI);
-        return integralValue;
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this % divisor}.
-     * <p>
-     * The remainder is defined as {@code this -
-     * this.divideToIntegralValue(divisor) * divisor}.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @return {@code this % divisor}.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     */
-    public BigDecimal remainder(BigDecimal divisor) {
-        return divideAndRemainder(divisor)[1];
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this % divisor}.
-     * <p>
-     * The remainder is defined as {@code this -
-     * this.divideToIntegralValue(divisor) * divisor}.
-     * <p>
-     * The specified rounding mode {@code mc} is used for the division only.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param mc
-     *            rounding mode and precision to be used.
-     * @return {@code this % divisor}.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @throws ArithmeticException
-     *             if {@code mc.getPrecision() > 0} and the result of {@code
-     *             this.divideToIntegralValue(divisor, mc)} requires more digits
-     *             to be represented.
-     */
-    public BigDecimal remainder(BigDecimal divisor, MathContext mc) {
-        return divideAndRemainder(divisor, mc)[1];
-    }
-
-    /**
-     * Returns a {@code BigDecimal} array which contains the integral part of
-     * {@code this / divisor} at index 0 and the remainder {@code this %
-     * divisor} at index 1. The quotient is rounded down towards zero to the
-     * next integer.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @return {@code [this.divideToIntegralValue(divisor),
-     *         this.remainder(divisor)]}.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @see #divideToIntegralValue
-     * @see #remainder
-     */
-    public BigDecimal[] divideAndRemainder(BigDecimal divisor) {
-        BigDecimal quotAndRem[] = new BigDecimal[2];
-
-        quotAndRem[0] = this.divideToIntegralValue(divisor);
-        quotAndRem[1] = this.subtract( quotAndRem[0].multiply(divisor) );
-        return quotAndRem;
-    }
-
-    /**
-     * Returns a {@code BigDecimal} array which contains the integral part of
-     * {@code this / divisor} at index 0 and the remainder {@code this %
-     * divisor} at index 1. The quotient is rounded down towards zero to the
-     * next integer. The rounding mode passed with the parameter {@code mc} is
-     * not considered. But if the precision of {@code mc > 0} and the integral
-     * part requires more digits, then an {@code ArithmeticException} is thrown.
-     *
-     * @param divisor
-     *            value by which {@code this} is divided.
-     * @param mc
-     *            math context which determines the maximal precision of the
-     *            result.
-     * @return {@code [this.divideToIntegralValue(divisor),
-     *         this.remainder(divisor)]}.
-     * @throws NullPointerException
-     *             if {@code divisor == null}.
-     * @throws ArithmeticException
-     *             if {@code divisor == 0}.
-     * @see #divideToIntegralValue
-     * @see #remainder
-     */
-    public BigDecimal[] divideAndRemainder(BigDecimal divisor, MathContext mc) {
-        BigDecimal quotAndRem[] = new BigDecimal[2];
-
-        quotAndRem[0] = this.divideToIntegralValue(divisor, mc);
-        quotAndRem[1] = this.subtract( quotAndRem[0].multiply(divisor) );
-        return quotAndRem;
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this<sup>n</sup>}. The
-     * scale of the result is {@code n * this.scale()}.
-     *
-     * <p>{@code x.pow(0)} returns {@code 1}, even if {@code x == 0}.
-     *
-     * <p>Implementation Note: The implementation is based on the ANSI standard
-     * X3.274-1996 algorithm.
-     *
-     * @throws ArithmeticException
-     *             if {@code n < 0} or {@code n > 999999999}.
-     */
-    public BigDecimal pow(int n) {
-        if (n == 0) {
-            return ONE;
-        }
-        if ((n < 0) || (n > 999999999)) {
-            throw new ArithmeticException("Invalid operation");
-        }
-        long newScale = scale * (long)n;
-        // Let be: this = [u,s]   so:  this^n = [u^n, s*n]
-        return isZero() ? zeroScaledBy(newScale)
-                : new BigDecimal(getUnscaledValue().pow(n), safeLongToInt(newScale));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this<sup>n</sup>}. The
-     * result is rounded according to the passed context {@code mc}.
-     *
-     * <p>Implementation Note: The implementation is based on the ANSI standard
-     * X3.274-1996 algorithm.
-     *
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @throws ArithmeticException
-     *             if {@code n < 0} or {@code n > 999999999}.
-     */
-    public BigDecimal pow(int n, MathContext mc) {
-        // The ANSI standard X3.274-1996 algorithm
-        int m = Math.abs(n);
-        int mcPrecision = mc.getPrecision();
-        int elength = (int)Math.log10(m) + 1;   // decimal digits in 'n'
-        int oneBitMask; // mask of bits
-        BigDecimal accum; // the single accumulator
-        MathContext newPrecision = mc; // MathContext by default
-
-        // In particular cases, it reduces the problem to call the other 'pow()'
-        if ((n == 0) || ((isZero()) && (n > 0))) {
-            return pow(n);
-        }
-        if ((m > 999999999) || ((mcPrecision == 0) && (n < 0))
-                || ((mcPrecision > 0) && (elength > mcPrecision))) {
-            throw new ArithmeticException("Invalid operation");
-        }
-        if (mcPrecision > 0) {
-            newPrecision = new MathContext( mcPrecision + elength + 1,
-                    mc.getRoundingMode());
-        }
-        // The result is calculated as if 'n' were positive
-        accum = round(newPrecision);
-        oneBitMask = Integer.highestOneBit(m) >> 1;
-
-        while (oneBitMask > 0) {
-            accum = accum.multiply(accum, newPrecision);
-            if ((m & oneBitMask) == oneBitMask) {
-                accum = accum.multiply(this, newPrecision);
-            }
-            oneBitMask >>= 1;
-        }
-        // If 'n' is negative, the value is divided into 'ONE'
-        if (n < 0) {
-            accum = ONE.divide(accum, newPrecision);
-        }
-        // The final value is rounded to the destination precision
-        accum.inplaceRound(mc);
-        return accum;
-    }
-
-    /**
-     * Returns a {@code BigDecimal} whose value is the absolute value of
-     * {@code this}. The scale of the result is the same as the scale of this.
-     */
-    public BigDecimal abs() {
-        return ((signum() < 0) ? negate() : this);
-    }
-
-    /**
-     * Returns a {@code BigDecimal} whose value is the absolute value of
-     * {@code this}. The result is rounded according to the passed context
-     * {@code mc}.
-     */
-    public BigDecimal abs(MathContext mc) {
-        BigDecimal result = (signum() < 0) ? negate() : new BigDecimal(getUnscaledValue(), scale);
-        result.inplaceRound(mc);
-        return result;
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is the {@code -this}. The
-     * scale of the result is the same as the scale of this.
-     *
-     * @return {@code -this}
-     */
-    public BigDecimal negate() {
-        if(bitLength < 63 || (bitLength == 63 && smallValue!=Long.MIN_VALUE)) {
-            return valueOf(-smallValue,scale);
-        }
-        return new BigDecimal(getUnscaledValue().negate(), scale);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is the {@code -this}. The
-     * result is rounded according to the passed context {@code mc}.
-     *
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @return {@code -this}
-     */
-    public BigDecimal negate(MathContext mc) {
-        BigDecimal result = negate();
-        result.inplaceRound(mc);
-        return result;
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code +this}. The scale
-     * of the result is the same as the scale of this.
-     *
-     * @return {@code this}
-     */
-    public BigDecimal plus() {
-        return this;
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code +this}. The result
-     * is rounded according to the passed context {@code mc}.
-     *
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @return {@code this}, rounded
-     */
-    public BigDecimal plus(MathContext mc) {
-        return round(mc);
-    }
-
-    /**
-     * Returns the sign of this {@code BigDecimal}.
-     *
-     * @return {@code -1} if {@code this < 0},
-     *         {@code 0} if {@code this == 0},
-     *         {@code 1} if {@code this > 0}.
-     */
-    public int signum() {
-        if( bitLength < 64) {
-            return Long.signum( this.smallValue );
-        }
-        return getUnscaledValue().signum();
-    }
-
-    private boolean isZero() {
-        //Watch out: -1 has a bitLength=0
-        return bitLength == 0 && this.smallValue != -1;
-    }
-
-    /**
-     * Returns the scale of this {@code BigDecimal}. The scale is the number of
-     * digits behind the decimal point. The value of this {@code BigDecimal} is
-     * the {@code unsignedValue * 10<sup>-scale</sup>}. If the scale is negative,
-     * then this {@code BigDecimal} represents a big integer.
-     *
-     * @return the scale of this {@code BigDecimal}.
-     */
-    public int scale() {
-        return scale;
-    }
-
-    /**
-     * Returns the precision of this {@code BigDecimal}. The precision is the
-     * number of decimal digits used to represent this decimal. It is equivalent
-     * to the number of digits of the unscaled value. The precision of {@code 0}
-     * is {@code 1} (independent of the scale).
-     *
-     * @return the precision of this {@code BigDecimal}.
-     */
-    public int precision() {
-        // Return the cached value if we have one.
-        if (precision != 0) {
-            return precision;
-        }
-
-        if (bitLength == 0) {
-            precision = 1;
-        } else if (bitLength < 64) {
-            precision = decimalDigitsInLong(smallValue);
-        } else {
-            int decimalDigits = 1 + (int) ((bitLength - 1) * LOG10_2);
-            // If after division the number isn't zero, there exists an additional digit
-            if (getUnscaledValue().divide(Multiplication.powerOf10(decimalDigits)).signum() != 0) {
-                decimalDigits++;
-            }
-            precision = decimalDigits;
-        }
-        return precision;
-    }
-
-    private int decimalDigitsInLong(long value) {
-        if (value == Long.MIN_VALUE) {
-            return 19; // special case required because abs(MIN_VALUE) == MIN_VALUE
-        } else {
-            int index = Arrays.binarySearch(MathUtils.LONG_POWERS_OF_TEN, Math.abs(value));
-            return (index < 0) ? (-index - 1) : (index + 1);
-        }
-    }
-
-    /**
-     * Returns the unscaled value (mantissa) of this {@code BigDecimal} instance
-     * as a {@code BigInteger}. The unscaled value can be computed as
-     * {@code this * 10<sup>scale</sup>}.
-     */
-    public BigInteger unscaledValue() {
-        return getUnscaledValue();
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this}, rounded
-     * according to the passed context {@code mc}.
-     * <p>
-     * If {@code mc.precision = 0}, then no rounding is performed.
-     * <p>
-     * If {@code mc.precision > 0} and {@code mc.roundingMode == UNNECESSARY},
-     * then an {@code ArithmeticException} is thrown if the result cannot be
-     * represented exactly within the given precision.
-     *
-     * @param mc
-     *            rounding mode and precision for the result of this operation.
-     * @return {@code this} rounded according to the passed context.
-     * @throws ArithmeticException
-     *             if {@code mc.precision > 0} and {@code mc.roundingMode ==
-     *             UNNECESSARY} and this cannot be represented within the given
-     *             precision.
-     */
-    public BigDecimal round(MathContext mc) {
-        BigDecimal thisBD = new BigDecimal(getUnscaledValue(), scale);
-
-        thisBD.inplaceRound(mc);
-        return thisBD;
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance with the specified scale.
-     * <p>
-     * If the new scale is greater than the old scale, then additional zeros are
-     * added to the unscaled value. In this case no rounding is necessary.
-     * <p>
-     * If the new scale is smaller than the old scale, then trailing digits are
-     * removed. If these trailing digits are not zero, then the remaining
-     * unscaled value has to be rounded. For this rounding operation the
-     * specified rounding mode is used.
-     *
-     * @param newScale
-     *            scale of the result returned.
-     * @param roundingMode
-     *            rounding mode to be used to round the result.
-     * @return a new {@code BigDecimal} instance with the specified scale.
-     * @throws NullPointerException
-     *             if {@code roundingMode == null}.
-     * @throws ArithmeticException
-     *             if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
-     *             necessary according to the given scale.
-     */
-    public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
-        if (roundingMode == null) {
-            throw new NullPointerException("roundingMode == null");
-        }
-        long diffScale = newScale - (long)scale;
-        // Let be:  'this' = [u,s]
-        if(diffScale == 0) {
-            return this;
-        }
-        if(diffScale > 0) {
-        // return  [u * 10^(s2 - s), newScale]
-            if(diffScale < MathUtils.LONG_POWERS_OF_TEN.length &&
-                    (this.bitLength + LONG_POWERS_OF_TEN_BIT_LENGTH[(int)diffScale]) < 64 ) {
-                return valueOf(this.smallValue*MathUtils.LONG_POWERS_OF_TEN[(int)diffScale],newScale);
-            }
-            return new BigDecimal(Multiplication.multiplyByTenPow(getUnscaledValue(),(int)diffScale), newScale);
-        }
-        // diffScale < 0
-        // return  [u,s] / [1,newScale]  with the appropriate scale and rounding
-        if(this.bitLength < 64 && -diffScale < MathUtils.LONG_POWERS_OF_TEN.length) {
-            return dividePrimitiveLongs(this.smallValue, MathUtils.LONG_POWERS_OF_TEN[(int)-diffScale], newScale,roundingMode);
-        }
-        return divideBigIntegers(this.getUnscaledValue(),Multiplication.powerOf10(-diffScale),newScale,roundingMode);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance with the specified scale.
-     * <p>
-     * If the new scale is greater than the old scale, then additional zeros are
-     * added to the unscaled value. In this case no rounding is necessary.
-     * <p>
-     * If the new scale is smaller than the old scale, then trailing digits are
-     * removed. If these trailing digits are not zero, then the remaining
-     * unscaled value has to be rounded. For this rounding operation the
-     * specified rounding mode is used.
-     *
-     * @param newScale
-     *            scale of the result returned.
-     * @param roundingMode
-     *            rounding mode to be used to round the result.
-     * @return a new {@code BigDecimal} instance with the specified scale.
-     * @throws IllegalArgumentException
-     *             if {@code roundingMode} is not a valid rounding mode.
-     * @throws ArithmeticException
-     *             if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
-     *             necessary according to the given scale.
-     */
-    public BigDecimal setScale(int newScale, int roundingMode) {
-        return setScale(newScale, RoundingMode.valueOf(roundingMode));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance with the specified scale. If
-     * the new scale is greater than the old scale, then additional zeros are
-     * added to the unscaled value. If the new scale is smaller than the old
-     * scale, then trailing zeros are removed. If the trailing digits are not
-     * zeros then an ArithmeticException is thrown.
-     * <p>
-     * If no exception is thrown, then the following equation holds: {@code
-     * x.setScale(s).compareTo(x) == 0}.
-     *
-     * @param newScale
-     *            scale of the result returned.
-     * @return a new {@code BigDecimal} instance with the specified scale.
-     * @throws ArithmeticException
-     *             if rounding would be necessary.
-     */
-    public BigDecimal setScale(int newScale) {
-        return setScale(newScale, RoundingMode.UNNECESSARY);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance where the decimal point has
-     * been moved {@code n} places to the left. If {@code n < 0} then the
-     * decimal point is moved {@code -n} places to the right.
-     *
-     * <p>The result is obtained by changing its scale. If the scale of the result
-     * becomes negative, then its precision is increased such that the scale is
-     * zero.
-     *
-     * <p>Note, that {@code movePointLeft(0)} returns a result which is
-     * mathematically equivalent, but which has {@code scale >= 0}.
-     */
-    public BigDecimal movePointLeft(int n) {
-        return movePoint(scale + (long)n);
-    }
-
-    private BigDecimal movePoint(long newScale) {
-        if (isZero()) {
-            return zeroScaledBy(Math.max(newScale, 0));
-        }
-        /*
-         * When: 'n'== Integer.MIN_VALUE isn't possible to call to
-         * movePointRight(-n) since -Integer.MIN_VALUE == Integer.MIN_VALUE
-         */
-        if(newScale >= 0) {
-            if(bitLength < 64) {
-                return valueOf(smallValue, safeLongToInt(newScale));
-            }
-            return new BigDecimal(getUnscaledValue(), safeLongToInt(newScale));
-        }
-        if(-newScale < MathUtils.LONG_POWERS_OF_TEN.length &&
-                bitLength + LONG_POWERS_OF_TEN_BIT_LENGTH[(int)-newScale] < 64 ) {
-            return valueOf(smallValue*MathUtils.LONG_POWERS_OF_TEN[(int)-newScale],0);
-        }
-        return new BigDecimal(Multiplication.multiplyByTenPow(
-                getUnscaledValue(), safeLongToInt(-newScale)), 0);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance where the decimal point has
-     * been moved {@code n} places to the right. If {@code n < 0} then the
-     * decimal point is moved {@code -n} places to the left.
-     *
-     * <p>The result is obtained by changing its scale. If the scale of the result
-     * becomes negative, then its precision is increased such that the scale is
-     * zero.
-     *
-     * <p>Note, that {@code movePointRight(0)} returns a result which is
-     * mathematically equivalent, but which has scale >= 0.
-     */
-    public BigDecimal movePointRight(int n) {
-        return movePoint(scale - (long)n);
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} whose value is {@code this * 10<sup>n</sup>}.
-     * The scale of the result is {@code this.scale()} - {@code n}.
-     * The precision of the result is the precision of {@code this}.
-     *
-     * <p>This method has the same effect as {@link #movePointRight}, except that
-     * the precision is not changed.
-     */
-    public BigDecimal scaleByPowerOfTen(int n) {
-        long newScale = scale - (long)n;
-        if(bitLength < 64) {
-            //Taking care when a 0 is to be scaled
-            if( smallValue==0  ){
-                return zeroScaledBy( newScale );
-            }
-            return valueOf(smallValue, safeLongToInt(newScale));
-        }
-        return new BigDecimal(getUnscaledValue(), safeLongToInt(newScale));
-    }
-
-    /**
-     * Returns a new {@code BigDecimal} instance with the same value as {@code
-     * this} but with a unscaled value where the trailing zeros have been
-     * removed. If the unscaled value of {@code this} has n trailing zeros, then
-     * the scale and the precision of the result has been reduced by n.
-     *
-     * @return a new {@code BigDecimal} instance equivalent to this where the
-     *         trailing zeros of the unscaled value have been removed.
-     */
-    public BigDecimal stripTrailingZeros() {
-        int i = 1; // 1 <= i <= 18
-        int lastPow = TEN_POW.length - 1;
-        long newScale = scale;
-
-        if (isZero()) {
-            return new BigDecimal(BigInteger.ZERO, 0);
-        }
-        BigInteger strippedBI = getUnscaledValue();
-        BigInteger[] quotAndRem;
-
-        // while the number is even...
-        while (!strippedBI.testBit(0)) {
-            // To divide by 10^i
-            quotAndRem = strippedBI.divideAndRemainder(TEN_POW[i]);
-            // To look the remainder
-            if (quotAndRem[1].signum() == 0) {
-                // To adjust the scale
-                newScale -= i;
-                if (i < lastPow) {
-                    // To set to the next power
-                    i++;
-                }
-                strippedBI = quotAndRem[0];
-            } else {
-                if (i == 1) {
-                    // 'this' has no more trailing zeros
-                    break;
-                }
-                // To set to the smallest power of ten
-                i = 1;
-            }
-        }
-        return new BigDecimal(strippedBI, safeLongToInt(newScale));
-    }
-
-    /**
-     * Compares this {@code BigDecimal} with {@code val}. Returns one of the
-     * three values {@code 1}, {@code 0}, or {@code -1}. The method behaves as
-     * if {@code this.subtract(val)} is computed. If this difference is > 0 then
-     * 1 is returned, if the difference is < 0 then -1 is returned, and if the
-     * difference is 0 then 0 is returned. This means, that if two decimal
-     * instances are compared which are equal in value but differ in scale, then
-     * these two instances are considered as equal.
-     *
-     * @param val
-     *            value to be compared with {@code this}.
-     * @return {@code 1} if {@code this > val}, {@code -1} if {@code this < val},
-     *         {@code 0} if {@code this == val}.
-     * @throws NullPointerException
-     *             if {@code val == null}.
-     */
-    public int compareTo(BigDecimal val) {
-        int thisSign = signum();
-        int valueSign = val.signum();
-
-        if( thisSign == valueSign) {
-            if(this.scale == val.scale && this.bitLength<64 && val.bitLength<64 ) {
-                return (smallValue < val.smallValue) ? -1 : (smallValue > val.smallValue) ? 1 : 0;
-            }
-            long diffScale = (long)this.scale - val.scale;
-            int diffPrecision = this.approxPrecision() - val.approxPrecision();
-            if (diffPrecision > diffScale + 1) {
-                return thisSign;
-            } else if (diffPrecision < diffScale - 1) {
-                return -thisSign;
-            } else {// thisSign == val.signum()  and  diffPrecision is aprox. diffScale
-                BigInteger thisUnscaled = this.getUnscaledValue();
-                BigInteger valUnscaled = val.getUnscaledValue();
-                // If any of both precision is bigger, append zeros to the shorter one
-                if (diffScale < 0) {
-                    thisUnscaled = thisUnscaled.multiply(Multiplication.powerOf10(-diffScale));
-                } else if (diffScale > 0) {
-                    valUnscaled = valUnscaled.multiply(Multiplication.powerOf10(diffScale));
-                }
-                return thisUnscaled.compareTo(valUnscaled);
-            }
-        } else if (thisSign < valueSign) {
-            return -1;
-        } else  {
-            return 1;
-        }
-    }
-
-    /**
-     * Returns {@code true} if {@code x} is a {@code BigDecimal} instance and if
-     * this instance is equal to this big decimal. Two big decimals are equal if
-     * their unscaled value and their scale is equal. For example, 1.0
-     * (10*10<sup>-1</sup>) is not equal to 1.00 (100*10<sup>-2</sup>). Similarly, zero
-     * instances are not equal if their scale differs.
-     */
-    @Override
-    public boolean equals(Object x) {
-        if (this == x) {
-            return true;
-        }
-        if (x instanceof BigDecimal) {
-            BigDecimal x1 = (BigDecimal) x;
-            return x1.scale == scale
-                    && x1.bitLength == bitLength
-                    && (bitLength < 64 ? (x1.smallValue == smallValue) : x1.intVal.equals(intVal));
-        }
-        return false;
-    }
-
-    /**
-     * Returns the minimum of this {@code BigDecimal} and {@code val}.
-     *
-     * @param val
-     *            value to be used to compute the minimum with this.
-     * @return {@code min(this, val}.
-     * @throws NullPointerException
-     *             if {@code val == null}.
-     */
-    public BigDecimal min(BigDecimal val) {
-        return ((compareTo(val) <= 0) ? this : val);
-    }
-
-    /**
-     * Returns the maximum of this {@code BigDecimal} and {@code val}.
-     *
-     * @param val
-     *            value to be used to compute the maximum with this.
-     * @return {@code max(this, val}.
-     * @throws NullPointerException
-     *             if {@code val == null}.
-     */
-    public BigDecimal max(BigDecimal val) {
-        return ((compareTo(val) >= 0) ? this : val);
-    }
-
-    /**
-     * Returns a hash code for this {@code BigDecimal}.
-     *
-     * @return hash code for {@code this}.
-     */
-    @Override
-    public int hashCode() {
-        if (hashCode != 0) {
-            return hashCode;
-        }
-        if (bitLength < 64) {
-            hashCode = (int)(smallValue & 0xffffffff);
-            hashCode = 33 * hashCode +  (int)((smallValue >> 32) & 0xffffffff);
-            hashCode = 17 * hashCode + scale;
-            return hashCode;
-        }
-        hashCode = 17 * intVal.hashCode() + scale;
-        return hashCode;
-    }
-
-    /**
-     * Returns a canonical string representation of this {@code BigDecimal}. If
-     * necessary, scientific notation is used. This representation always prints
-     * all significant digits of this value.
-     * <p>
-     * If the scale is negative or if {@code scale - precision >= 6} then
-     * scientific notation is used.
-     *
-     * @return a string representation of {@code this} in scientific notation if
-     *         necessary.
-     */
-    @Override
-    public String toString() {
-        if (toStringImage != null) {
-            return toStringImage;
-        }
-        if(bitLength < 32) {
-            toStringImage = Conversion.toDecimalScaledString(smallValue,scale);
-            return toStringImage;
-        }
-        String intString = getUnscaledValue().toString();
-        if (scale == 0) {
-            return intString;
-        }
-        int begin = (getUnscaledValue().signum() < 0) ? 2 : 1;
-        int end = intString.length();
-        long exponent = -(long)scale + end - begin;
-        StringBuilder result = new StringBuilder();
-
-        result.append(intString);
-        if ((scale > 0) && (exponent >= -6)) {
-            if (exponent >= 0) {
-                result.insert(end - scale, '.');
-            } else {
-                result.insert(begin - 1, "0.");
-                result.insert(begin + 1, CH_ZEROS, 0, -(int)exponent - 1);
-            }
-        } else {
-            if (end - begin >= 1) {
-                result.insert(begin, '.');
-                end++;
-            }
-            result.insert(end, 'E');
-            if (exponent > 0) {
-                result.insert(++end, '+');
-            }
-            result.insert(++end, Long.toString(exponent));
-        }
-        toStringImage = result.toString();
-        return toStringImage;
-    }
-
-    /**
-     * Returns a string representation of this {@code BigDecimal}. This
-     * representation always prints all significant digits of this value.
-     * <p>
-     * If the scale is negative or if {@code scale - precision >= 6} then
-     * engineering notation is used. Engineering notation is similar to the
-     * scientific notation except that the exponent is made to be a multiple of
-     * 3 such that the integer part is >= 1 and < 1000.
-     *
-     * @return a string representation of {@code this} in engineering notation
-     *         if necessary.
-     */
-    public String toEngineeringString() {
-        String intString = getUnscaledValue().toString();
-        if (scale == 0) {
-            return intString;
-        }
-        int begin = (getUnscaledValue().signum() < 0) ? 2 : 1;
-        int end = intString.length();
-        long exponent = -(long)scale + end - begin;
-        StringBuilder result = new StringBuilder(intString);
-
-        if ((scale > 0) && (exponent >= -6)) {
-            if (exponent >= 0) {
-                result.insert(end - scale, '.');
-            } else {
-                result.insert(begin - 1, "0.");
-                result.insert(begin + 1, CH_ZEROS, 0, -(int)exponent - 1);
-            }
-        } else {
-            int delta = end - begin;
-            int rem = (int)(exponent % 3);
-
-            if (rem != 0) {
-                // adjust exponent so it is a multiple of three
-                if (getUnscaledValue().signum() == 0) {
-                    // zero value
-                    rem = (rem < 0) ? -rem : 3 - rem;
-                    exponent += rem;
-                } else {
-                    // nonzero value
-                    rem = (rem < 0) ? rem + 3 : rem;
-                    exponent -= rem;
-                    begin += rem;
-                }
-                if (delta < 3) {
-                    for (int i = rem - delta; i > 0; i--) {
-                        result.insert(end++, '0');
-                    }
-                }
-            }
-            if (end - begin >= 1) {
-                result.insert(begin, '.');
-                end++;
-            }
-            if (exponent != 0) {
-                result.insert(end, 'E');
-                if (exponent > 0) {
-                    result.insert(++end, '+');
-                }
-                result.insert(++end, Long.toString(exponent));
-            }
-        }
-        return result.toString();
-    }
-
-    /**
-     * Returns a string representation of this {@code BigDecimal}. No scientific
-     * notation is used. This methods adds zeros where necessary.
-     * <p>
-     * If this string representation is used to create a new instance, this
-     * instance is generally not identical to {@code this} as the precision
-     * changes.
-     * <p>
-     * {@code x.equals(new BigDecimal(x.toPlainString())} usually returns
-     * {@code false}.
-     * <p>
-     * {@code x.compareTo(new BigDecimal(x.toPlainString())} returns {@code 0}.
-     *
-     * @return a string representation of {@code this} without exponent part.
-     */
-    public String toPlainString() {
-        String intStr = getUnscaledValue().toString();
-        if ((scale == 0) || ((isZero()) && (scale < 0))) {
-            return intStr;
-        }
-        int begin = (signum() < 0) ? 1 : 0;
-        int delta = scale;
-        // We take space for all digits, plus a possible decimal point, plus 'scale'
-        StringBuilder result = new StringBuilder(intStr.length() + 1 + Math.abs(scale));
-
-        if (begin == 1) {
-            // If the number is negative, we insert a '-' character at front
-            result.append('-');
-        }
-        if (scale > 0) {
-            delta -= (intStr.length() - begin);
-            if (delta >= 0) {
-                result.append("0.");
-                // To append zeros after the decimal point
-                for (; delta > CH_ZEROS.length; delta -= CH_ZEROS.length) {
-                    result.append(CH_ZEROS);
-                }
-                result.append(CH_ZEROS, 0, delta);
-                result.append(intStr.substring(begin));
-            } else {
-                delta = begin - delta;
-                result.append(intStr.substring(begin, delta));
-                result.append('.');
-                result.append(intStr.substring(delta));
-            }
-        } else {// (scale <= 0)
-            result.append(intStr.substring(begin));
-            // To append trailing zeros
-            for (; delta < -CH_ZEROS.length; delta += CH_ZEROS.length) {
-                result.append(CH_ZEROS);
-            }
-            result.append(CH_ZEROS, 0, -delta);
-        }
-        return result.toString();
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a big integer instance. A fractional
-     * part is discarded.
-     *
-     * @return this {@code BigDecimal} as a big integer instance.
-     */
-    public BigInteger toBigInteger() {
-        if ((scale == 0) || (isZero())) {
-            return getUnscaledValue();
-        } else if (scale < 0) {
-            return getUnscaledValue().multiply(Multiplication.powerOf10(-(long)scale));
-        } else {// (scale > 0)
-            return getUnscaledValue().divide(Multiplication.powerOf10(scale));
-        }
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a big integer instance if it has no
-     * fractional part. If this {@code BigDecimal} has a fractional part, i.e.
-     * if rounding would be necessary, an {@code ArithmeticException} is thrown.
-     *
-     * @return this {@code BigDecimal} as a big integer value.
-     * @throws ArithmeticException
-     *             if rounding is necessary.
-     */
-    public BigInteger toBigIntegerExact() {
-        if ((scale == 0) || (isZero())) {
-            return getUnscaledValue();
-        } else if (scale < 0) {
-            return getUnscaledValue().multiply(Multiplication.powerOf10(-(long)scale));
-        } else {// (scale > 0)
-            BigInteger[] integerAndFraction;
-            // An optimization before do a heavy division
-            if ((scale > approxPrecision()) || (scale > getUnscaledValue().getLowestSetBit())) {
-                throw new ArithmeticException("Rounding necessary");
-            }
-            integerAndFraction = getUnscaledValue().divideAndRemainder(Multiplication.powerOf10(scale));
-            if (integerAndFraction[1].signum() != 0) {
-                // It exists a non-zero fractional part
-                throw new ArithmeticException("Rounding necessary");
-            }
-            return integerAndFraction[0];
-        }
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as an long value. Any fractional part is
-     * discarded. If the integral part of {@code this} is too big to be
-     * represented as an long, then {@code this % 2<sup>64</sup>} is returned.
-     */
-    @Override
-    public long longValue() {
-        /*
-         * If scale <= -64 there are at least 64 trailing bits zero in
-         * 10^(-scale). If the scale is positive and very large the long value
-         * could be zero.
-         */
-        return ((scale <= -64) || (scale > approxPrecision()) ? 0L : toBigInteger().longValue());
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a long value if it has no fractional
-     * part and if its value fits to the int range ([-2<sup>63</sup>..2<sup>63</sup>-1]). If
-     * these conditions are not met, an {@code ArithmeticException} is thrown.
-     *
-     * @throws ArithmeticException
-     *             if rounding is necessary or the number doesn't fit in a long.
-     */
-    public long longValueExact() {
-        return valueExact(64);
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as an int value. Any fractional part is
-     * discarded. If the integral part of {@code this} is too big to be
-     * represented as an int, then {@code this % 2<sup>32</sup>} is returned.
-     */
-    @Override
-    public int intValue() {
-        /*
-         * If scale <= -32 there are at least 32 trailing bits zero in
-         * 10^(-scale). If the scale is positive and very large the long value
-         * could be zero.
-         */
-        return ((scale <= -32) || (scale > approxPrecision()) ? 0 : toBigInteger().intValue());
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a int value if it has no fractional
-     * part and if its value fits to the int range ([-2<sup>31</sup>..2<sup>31</sup>-1]). If
-     * these conditions are not met, an {@code ArithmeticException} is thrown.
-     *
-     * @throws ArithmeticException
-     *             if rounding is necessary or the number doesn't fit in an int.
-     */
-    public int intValueExact() {
-        return (int) valueExact(32);
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a short value if it has no fractional
-     * part and if its value fits to the short range ([-2<sup>15</sup>..2<sup>15</sup>-1]). If
-     * these conditions are not met, an {@code ArithmeticException} is thrown.
-     *
-     * @throws ArithmeticException
-     *             if rounding is necessary of the number doesn't fit in a short.
-     */
-    public short shortValueExact() {
-        return (short) valueExact(16);
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a byte value if it has no fractional
-     * part and if its value fits to the byte range ([-128..127]). If these
-     * conditions are not met, an {@code ArithmeticException} is thrown.
-     *
-     * @throws ArithmeticException
-     *             if rounding is necessary or the number doesn't fit in a byte.
-     */
-    public byte byteValueExact() {
-        return (byte) valueExact(8);
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a float value. If {@code this} is too
-     * big to be represented as an float, then {@code Float.POSITIVE_INFINITY}
-     * or {@code Float.NEGATIVE_INFINITY} is returned.
-     * <p>
-     * Note, that if the unscaled value has more than 24 significant digits,
-     * then this decimal cannot be represented exactly in a float variable. In
-     * this case the result is rounded.
-     * <p>
-     * For example, if the instance {@code x1 = new BigDecimal("0.1")} cannot be
-     * represented exactly as a float, and thus {@code x1.equals(new
-     * BigDecimal(x1.floatValue())} returns {@code false} for this case.
-     * <p>
-     * Similarly, if the instance {@code new BigDecimal(16777217)} is converted
-     * to a float, the result is {@code 1.6777216E}7.
-     *
-     * @return this {@code BigDecimal} as a float value.
-     */
-    @Override
-    public float floatValue() {
-        /* A similar code like in doubleValue() could be repeated here,
-         * but this simple implementation is quite efficient. */
-        float floatResult = signum();
-        long powerOfTwo = this.bitLength - (long)(scale / LOG10_2);
-        if ((powerOfTwo < -149) || (floatResult == 0.0f)) {
-            // Cases which 'this' is very small
-            floatResult *= 0.0f;
-        } else if (powerOfTwo > 129) {
-            // Cases which 'this' is very large
-            floatResult *= Float.POSITIVE_INFINITY;
-        } else {
-            floatResult = (float)doubleValue();
-        }
-        return floatResult;
-    }
-
-    /**
-     * Returns this {@code BigDecimal} as a double value. If {@code this} is too
-     * big to be represented as an float, then {@code Double.POSITIVE_INFINITY}
-     * or {@code Double.NEGATIVE_INFINITY} is returned.
-     * <p>
-     * Note, that if the unscaled value has more than 53 significant digits,
-     * then this decimal cannot be represented exactly in a double variable. In
-     * this case the result is rounded.
-     * <p>
-     * For example, if the instance {@code x1 = new BigDecimal("0.1")} cannot be
-     * represented exactly as a double, and thus {@code x1.equals(new
-     * BigDecimal(x1.doubleValue())} returns {@code false} for this case.
-     * <p>
-     * Similarly, if the instance {@code new BigDecimal(9007199254740993L)} is
-     * converted to a double, the result is {@code 9.007199254740992E15}.
-     * <p>
-     *
-     * @return this {@code BigDecimal} as a double value.
-     */
-    @Override
-    public double doubleValue() {
-        int sign = signum();
-        int exponent = 1076; // bias + 53
-        int lowestSetBit;
-        int discardedSize;
-        long powerOfTwo = this.bitLength - (long)(scale / LOG10_2);
-        long bits; // IEEE-754 Standard
-        long tempBits; // for temporal calculations
-        BigInteger mantissa;
-
-        if ((powerOfTwo < -1074) || (sign == 0)) {
-            // Cases which 'this' is very small
-            return (sign * 0.0d);
-        } else if (powerOfTwo > 1025) {
-            // Cases which 'this' is very large
-            return (sign * Double.POSITIVE_INFINITY);
-        }
-        mantissa = getUnscaledValue().abs();
-        // Let be:  this = [u,s], with s > 0
-        if (scale <= 0) {
-            // mantissa = abs(u) * 10^s
-            mantissa = mantissa.multiply(Multiplication.powerOf10(-scale));
-        } else {// (scale > 0)
-            BigInteger quotAndRem[];
-            BigInteger powerOfTen = Multiplication.powerOf10(scale);
-            int k = 100 - (int)powerOfTwo;
-            int compRem;
-
-            if (k > 0) {
-                /* Computing (mantissa * 2^k) , where 'k' is a enough big
-                 * power of '2' to can divide by 10^s */
-                mantissa = mantissa.shiftLeft(k);
-                exponent -= k;
-            }
-            // Computing (mantissa * 2^k) / 10^s
-            quotAndRem = mantissa.divideAndRemainder(powerOfTen);
-            // To check if the fractional part >= 0.5
-            compRem = quotAndRem[1].shiftLeftOneBit().compareTo(powerOfTen);
-            // To add two rounded bits at end of mantissa
-            mantissa = quotAndRem[0].shiftLeft(2).add(
-                    BigInteger.valueOf((compRem * (compRem + 3)) / 2 + 1));
-            exponent -= 2;
-        }
-        lowestSetBit = mantissa.getLowestSetBit();
-        discardedSize = mantissa.bitLength() - 54;
-        if (discardedSize > 0) {// (n > 54)
-            // mantissa = (abs(u) * 10^s) >> (n - 54)
-            bits = mantissa.shiftRight(discardedSize).longValue();
-            tempBits = bits;
-            // #bits = 54, to check if the discarded fraction produces a carry
-            if ((((bits & 1) == 1) && (lowestSetBit < discardedSize))
-                    || ((bits & 3) == 3)) {
-                bits += 2;
-            }
-        } else {// (n <= 54)
-            // mantissa = (abs(u) * 10^s) << (54 - n)
-            bits = mantissa.longValue() << -discardedSize;
-            tempBits = bits;
-            // #bits = 54, to check if the discarded fraction produces a carry:
-            if ((bits & 3) == 3) {
-                bits += 2;
-            }
-        }
-        // Testing bit 54 to check if the carry creates a new binary digit
-        if ((bits & 0x40000000000000L) == 0) {
-            // To drop the last bit of mantissa (first discarded)
-            bits >>= 1;
-            // exponent = 2^(s-n+53+bias)
-            exponent += discardedSize;
-        } else {// #bits = 54
-            bits >>= 2;
-            exponent += discardedSize + 1;
-        }
-        // To test if the 53-bits number fits in 'double'
-        if (exponent > 2046) {// (exponent - bias > 1023)
-            return (sign * Double.POSITIVE_INFINITY);
-        } else if (exponent <= 0) {// (exponent - bias <= -1023)
-            // Denormalized numbers (having exponent == 0)
-            if (exponent < -53) {// exponent - bias < -1076
-                return (sign * 0.0d);
-            }
-            // -1076 <= exponent - bias <= -1023
-            // To discard '- exponent + 1' bits
-            bits = tempBits >> 1;
-            tempBits = bits & (-1L >>> (63 + exponent));
-            bits >>= (-exponent );
-            // To test if after discard bits, a new carry is generated
-            if (((bits & 3) == 3) || (((bits & 1) == 1) && (tempBits != 0)
-            && (lowestSetBit < discardedSize))) {
-                bits += 1;
-            }
-            exponent = 0;
-            bits >>= 1;
-        }
-        // Construct the 64 double bits: [sign(1), exponent(11), mantissa(52)]
-        bits = (sign & 0x8000000000000000L) | ((long)exponent << 52)
-                | (bits & 0xFFFFFFFFFFFFFL);
-        return Double.longBitsToDouble(bits);
-    }
-
-    /**
-     * Returns the unit in the last place (ULP) of this {@code BigDecimal}
-     * instance. An ULP is the distance to the nearest big decimal with the same
-     * precision.
-     *
-     * <p>The amount of a rounding error in the evaluation of a floating-point
-     * operation is often expressed in ULPs. An error of 1 ULP is often seen as
-     * a tolerable error.
-     *
-     * <p>For class {@code BigDecimal}, the ULP of a number is simply 10<sup>-scale</sup>.
-     * For example, {@code new BigDecimal(0.1).ulp()} returns {@code 1E-55}.
-     *
-     * @return unit in the last place (ULP) of this {@code BigDecimal} instance.
-     */
-    public BigDecimal ulp() {
-        return valueOf(1, scale);
-    }
-
-    /* Private Methods */
-
-    /**
-     * It does all rounding work of the public method
-     * {@code round(MathContext)}, performing an inplace rounding
-     * without creating a new object.
-     *
-     * @param mc
-     *            the {@code MathContext} for perform the rounding.
-     * @see #round(MathContext)
-     */
-    private void inplaceRound(MathContext mc) {
-        int mcPrecision = mc.getPrecision();
-        if (approxPrecision() < mcPrecision || mcPrecision == 0) {
-            return;
-        }
-        int discardedPrecision = precision() - mcPrecision;
-        // If no rounding is necessary it returns immediately
-        if ((discardedPrecision <= 0)) {
-            return;
-        }
-        // When the number is small perform an efficient rounding
-        if (this.bitLength < 64) {
-            smallRound(mc, discardedPrecision);
-            return;
-        }
-        // Getting the integer part and the discarded fraction
-        BigInteger sizeOfFraction = Multiplication.powerOf10(discardedPrecision);
-        BigInteger[] integerAndFraction = getUnscaledValue().divideAndRemainder(sizeOfFraction);
-        long newScale = (long)scale - discardedPrecision;
-        int compRem;
-        BigDecimal tempBD;
-        // If the discarded fraction is non-zero, perform rounding
-        if (integerAndFraction[1].signum() != 0) {
-            // To check if the discarded fraction >= 0.5
-            compRem = (integerAndFraction[1].abs().shiftLeftOneBit().compareTo(sizeOfFraction));
-            // To look if there is a carry
-            compRem =  roundingBehavior( integerAndFraction[0].testBit(0) ? 1 : 0,
-                    integerAndFraction[1].signum() * (5 + compRem),
-                    mc.getRoundingMode());
-            if (compRem != 0) {
-                integerAndFraction[0] = integerAndFraction[0].add(BigInteger.valueOf(compRem));
-            }
-            tempBD = new BigDecimal(integerAndFraction[0]);
-            // If after to add the increment the precision changed, we normalize the size
-            if (tempBD.precision() > mcPrecision) {
-                integerAndFraction[0] = integerAndFraction[0].divide(BigInteger.TEN);
-                newScale--;
-            }
-        }
-        // To update all internal fields
-        scale = safeLongToInt(newScale);
-        precision = mcPrecision;
-        setUnscaledValue(integerAndFraction[0]);
-    }
-
-    /**
-     * Returns -1, 0, and 1 if {@code value1 < value2}, {@code value1 == value2},
-     * and {@code value1 > value2}, respectively, when comparing without regard
-     * to the values' sign.
-     *
-     * <p>Note that this implementation deals correctly with Long.MIN_VALUE,
-     * whose absolute magnitude is larger than any other {@code long} value.
-     */
-    private static int compareAbsoluteValues(long value1, long value2) {
-        // Map long values to the range -1 .. Long.MAX_VALUE so that comparison
-        // of absolute magnitude can be done using regular long arithmetics.
-        // This deals correctly with Long.MIN_VALUE, whose absolute magnitude
-        // is larger than any other long value, and which is mapped to
-        // Long.MAX_VALUE here.
-        // Values that only differ by sign get mapped to the same value, for
-        // example both +3 and -3 get mapped to +2.
-        value1 = Math.abs(value1) - 1;
-        value2 = Math.abs(value2) - 1;
-        // Unlike Long.compare(), we guarantee to return specifically -1 and +1
-        return value1 > value2 ? 1 : (value1 < value2 ? -1 : 0);
-    }
-
-    /**
-     * Compares {@code n} against {@code 0.5 * d} in absolute terms (ignoring sign)
-     * and with arithmetics that are safe against overflow or loss of precision.
-     * Returns -1 if {@code n} is less than {@code 0.5 * d}, 0 if {@code n == 0.5 * d},
-     * or +1 if {@code n > 0.5 * d} when comparing the absolute values under such
-     * arithmetics.
-     */
-    private static int compareForRounding(long n, long d) {
-        long halfD = d / 2; //  rounds towards 0
-        if (n == halfD || n == -halfD) {
-            // In absolute terms: Because n == halfD, we know that 2 * n + lsb == d
-            // for some lsb value 0 or 1. This means that n == d/2 (result 0) if
-            // lsb is 0, or n < d/2 (result -1) if lsb is 1. In either case, the
-            // result is -lsb.
-            // Since we're calculating in absolute terms, we need the absolute lsb
-            // (d & 1) as opposed to the signed lsb (d % 2) which would be -1 for
-            // negative odd values of d.
-            int lsb = (int) d & 1;
-            return -lsb; // returns 0 or -1
-        } else {
-            // In absolute terms, either 2 * n + 1 < d (in the case of n < halfD),
-            // or 2 * n > d (in the case of n > halfD).
-            // In either case, comparing n against halfD gets the right result
-            // -1 or +1, respectively.
-            return compareAbsoluteValues(n, halfD);
-        }
-    }
-
-    /**
-     * This method implements an efficient rounding for numbers which unscaled
-     * value fits in the type {@code long}.
-     *
-     * @param mc
-     *            the context to use
-     * @param discardedPrecision
-     *            the number of decimal digits that are discarded
-     * @see #round(MathContext)
-     */
-    private void smallRound(MathContext mc, int discardedPrecision) {
-        long sizeOfFraction = MathUtils.LONG_POWERS_OF_TEN[discardedPrecision];
-        long newScale = (long)scale - discardedPrecision;
-        long unscaledVal = smallValue;
-        // Getting the integer part and the discarded fraction
-        long integer = unscaledVal / sizeOfFraction;
-        long fraction = unscaledVal % sizeOfFraction;
-        int compRem;
-        // If the discarded fraction is non-zero perform rounding
-        if (fraction != 0) {
-            // To check if the discarded fraction >= 0.5
-            compRem = compareForRounding(fraction, sizeOfFraction);
-            // To look if there is a carry
-            integer += roundingBehavior( ((int)integer) & 1,
-                    Long.signum(fraction) * (5 + compRem),
-                    mc.getRoundingMode());
-            // If after to add the increment the precision changed, we normalize the size
-            if (Math.log10(Math.abs(integer)) >= mc.getPrecision()) {
-                integer /= 10;
-                newScale--;
-            }
-        }
-        // To update all internal fields
-        scale = safeLongToInt(newScale);
-        precision = mc.getPrecision();
-        smallValue = integer;
-        bitLength = bitLength(integer);
-        intVal = null;
-    }
-
-    /**
-     * Return an increment that can be -1,0 or 1, depending of
-     * {@code roundingMode}.
-     *
-     * @param parityBit
-     *            can be 0 or 1, it's only used in the case
-     *            {@code HALF_EVEN}
-     * @param fraction
-     *            the mantissa to be analyzed
-     * @param roundingMode
-     *            the type of rounding
-     * @return the carry propagated after rounding
-     */
-    private static int roundingBehavior(int parityBit, int fraction, RoundingMode roundingMode) {
-        int increment = 0; // the carry after rounding
-
-        switch (roundingMode) {
-            case UNNECESSARY:
-                if (fraction != 0) {
-                    throw new ArithmeticException("Rounding necessary");
-                }
-                break;
-            case UP:
-                increment = Integer.signum(fraction);
-                break;
-            case DOWN:
-                break;
-            case CEILING:
-                increment = Math.max(Integer.signum(fraction), 0);
-                break;
-            case FLOOR:
-                increment = Math.min(Integer.signum(fraction), 0);
-                break;
-            case HALF_UP:
-                if (Math.abs(fraction) >= 5) {
-                    increment = Integer.signum(fraction);
-                }
-                break;
-            case HALF_DOWN:
-                if (Math.abs(fraction) > 5) {
-                    increment = Integer.signum(fraction);
-                }
-                break;
-            case HALF_EVEN:
-                if (Math.abs(fraction) + parityBit > 5) {
-                    increment = Integer.signum(fraction);
-                }
-                break;
-        }
-        return increment;
-    }
-
-    /**
-     * If {@code intVal} has a fractional part throws an exception,
-     * otherwise it counts the number of bits of value and checks if it's out of
-     * the range of the primitive type. If the number fits in the primitive type
-     * returns this number as {@code long}, otherwise throws an
-     * exception.
-     *
-     * @param bitLengthOfType
-     *            number of bits of the type whose value will be calculated
-     *            exactly
-     * @return the exact value of the integer part of {@code BigDecimal}
-     *         when is possible
-     * @throws ArithmeticException when rounding is necessary or the
-     *             number don't fit in the primitive type
-     */
-    private long valueExact(int bitLengthOfType) {
-        BigInteger bigInteger = toBigIntegerExact();
-
-        if (bigInteger.bitLength() < bitLengthOfType) {
-            // It fits in the primitive type
-            return bigInteger.longValue();
-        }
-        throw new ArithmeticException("Rounding necessary");
-    }
-
-    /**
-     * If the precision already was calculated it returns that value, otherwise
-     * it calculates a very good approximation efficiently . Note that this
-     * value will be {@code precision()} or {@code precision()-1}
-     * in the worst case.
-     *
-     * @return an approximation of {@code precision()} value
-     */
-    private int approxPrecision() {
-        return precision > 0
-                ? precision
-                : (int) ((this.bitLength - 1) * LOG10_2) + 1;
-    }
-
-    private static int safeLongToInt(long longValue) {
-        if (longValue < Integer.MIN_VALUE || longValue > Integer.MAX_VALUE) {
-            throw new ArithmeticException("Out of int range: " + longValue);
-        }
-        return (int) longValue;
-    }
-
-    /**
-     * It returns the value 0 with the most approximated scale of type
-     * {@code int}. if {@code longScale > Integer.MAX_VALUE} the
-     * scale will be {@code Integer.MAX_VALUE}; if
-     * {@code longScale < Integer.MIN_VALUE} the scale will be
-     * {@code Integer.MIN_VALUE}; otherwise {@code longScale} is
-     * casted to the type {@code int}.
-     *
-     * @param longScale
-     *            the scale to which the value 0 will be scaled.
-     * @return the value 0 scaled by the closer scale of type {@code int}.
-     * @see #scale
-     */
-    private static BigDecimal zeroScaledBy(long longScale) {
-        if (longScale == (int) longScale) {
-            return valueOf(0,(int)longScale);
-            }
-        if (longScale >= 0) {
-            return new BigDecimal( 0, Integer.MAX_VALUE);
-        }
-        return new BigDecimal( 0, Integer.MIN_VALUE);
-    }
-
-    /**
-     * Assigns all transient fields upon deserialization of a
-     * {@code BigDecimal} instance (bitLength and smallValue). The transient
-     * field precision is assigned lazily.
-     */
-    private void readObject(ObjectInputStream in) throws IOException,
-            ClassNotFoundException {
-        in.defaultReadObject();
-
-        this.bitLength = intVal.bitLength();
-        if (this.bitLength < 64) {
-            this.smallValue = intVal.longValue();
-        }
-    }
-
-    /**
-     * Prepares this {@code BigDecimal} for serialization, i.e. the
-     * non-transient field {@code intVal} is assigned.
-     */
-    private void writeObject(ObjectOutputStream out) throws IOException {
-        getUnscaledValue();
-        out.defaultWriteObject();
-    }
-
-    private BigInteger getUnscaledValue() {
-        if(intVal == null) {
-            intVal = BigInteger.valueOf(smallValue);
-        }
-        return intVal;
-    }
-
-    private void setUnscaledValue(BigInteger unscaledValue) {
-        this.intVal = unscaledValue;
-        this.bitLength = unscaledValue.bitLength();
-        if(this.bitLength < 64) {
-            this.smallValue = unscaledValue.longValue();
-        }
-    }
-
-    private static int bitLength(long smallValue) {
-        if(smallValue < 0) {
-            smallValue = ~smallValue;
-        }
-        return 64 - Long.numberOfLeadingZeros(smallValue);
-    }
-
-    private static int bitLength(int smallValue) {
-        if(smallValue < 0) {
-            smallValue = ~smallValue;
-        }
-        return 32 - Integer.numberOfLeadingZeros(smallValue);
-    }
-
-}
diff --git a/luni/src/main/java/java/math/BigInt.java b/luni/src/main/java/java/math/BigInt.java
deleted file mode 100644
index 4448ce1..0000000
--- a/luni/src/main/java/java/math/BigInt.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.math;
-
-import dalvik.annotation.optimization.ReachabilitySensitive;
-import libcore.util.NativeAllocationRegistry;
-
-/*
- * In contrast to BigIntegers this class doesn't fake two's complement representation.
- * Any Bit-Operations, including Shifting, solely regard the unsigned magnitude.
- * Moreover BigInt objects are mutable and offer efficient in-place-operations.
- */
-final class BigInt {
-
-    private static NativeAllocationRegistry registry = NativeAllocationRegistry.createMalloced(
-            BigInt.class.getClassLoader(), NativeBN.getNativeFinalizer());
-
-    /* Fields used for the internal representation. */
-    @ReachabilitySensitive
-    private transient long bignum = 0;
-
-    @Override
-    public String toString() {
-        return this.decString();
-    }
-
-    boolean hasNativeBignum() {
-        return this.bignum != 0;
-    }
-
-    private void makeValid() {
-        if (this.bignum == 0) {
-            this.bignum = NativeBN.BN_new();
-            registry.registerNativeAllocation(this, this.bignum);
-        }
-    }
-
-    private static BigInt newBigInt() {
-        BigInt bi = new BigInt();
-        bi.bignum = NativeBN.BN_new();
-        registry.registerNativeAllocation(bi, bi.bignum);
-        return bi;
-    }
-
-
-    static int cmp(BigInt a, BigInt b) {
-        return NativeBN.BN_cmp(a.bignum, b.bignum);
-    }
-
-
-    void putCopy(BigInt from) {
-        this.makeValid();
-        NativeBN.BN_copy(this.bignum, from.bignum);
-    }
-
-    BigInt copy() {
-        BigInt bi = new BigInt();
-        bi.putCopy(this);
-        return bi;
-    }
-
-
-    void putLongInt(long val) {
-        this.makeValid();
-        NativeBN.putLongInt(this.bignum, val);
-    }
-
-    void putULongInt(long val, boolean neg) {
-        this.makeValid();
-        NativeBN.putULongInt(this.bignum, val, neg);
-    }
-
-    private NumberFormatException invalidBigInteger(String s) {
-        throw new NumberFormatException("Invalid BigInteger: " + s);
-    }
-
-    void putDecString(String original) {
-        String s = checkString(original, 10);
-        this.makeValid();
-        int usedLen = NativeBN.BN_dec2bn(this.bignum, s);
-        if (usedLen < s.length()) {
-            throw invalidBigInteger(original);
-        }
-    }
-
-    void putHexString(String original) {
-        String s = checkString(original, 16);
-        this.makeValid();
-        int usedLen = NativeBN.BN_hex2bn(this.bignum, s);
-        if (usedLen < s.length()) {
-            throw invalidBigInteger(original);
-        }
-    }
-
-    /**
-     * Returns a string suitable for passing to OpenSSL.
-     * Throws if 's' doesn't match Java's rules for valid BigInteger strings.
-     * BN_dec2bn and BN_hex2bn do very little checking, so we need to manually
-     * ensure we comply with Java's rules.
-     * http://code.google.com/p/android/issues/detail?id=7036
-     */
-    String checkString(String s, int base) {
-        if (s == null) {
-            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,
-        // and no other characters.
-        int charCount = s.length();
-        int i = 0;
-        if (charCount > 0) {
-            char ch = s.charAt(0);
-            if (ch == '+') {
-                // Java supports leading +, but OpenSSL doesn't, so we need to strip it.
-                s = s.substring(1);
-                --charCount;
-            } else if (ch == '-') {
-                ++i;
-            }
-        }
-        if (charCount - i == 0) {
-            throw invalidBigInteger(s);
-        }
-        boolean nonAscii = false;
-        for (; i < charCount; ++i) {
-            char ch = s.charAt(i);
-            if (Character.digit(ch, base) == -1) {
-                throw invalidBigInteger(s);
-            }
-            if (ch > 128) {
-                nonAscii = true;
-            }
-        }
-        return nonAscii ? toAscii(s, base) : s;
-    }
-
-    // Java supports non-ASCII decimal digits, but OpenSSL doesn't.
-    // We need to translate the decimal digits but leave any other characters alone.
-    // This method assumes it's being called on a string that has already been validated.
-    private static String toAscii(String s, int base) {
-        int length = s.length();
-        StringBuilder result = new StringBuilder(length);
-        for (int i = 0; i < length; ++i) {
-            char ch = s.charAt(i);
-            int value = Character.digit(ch, base);
-            if (value >= 0 && value <= 9) {
-                ch = (char) ('0' + value);
-            }
-            result.append(ch);
-        }
-        return result.toString();
-    }
-
-    void putBigEndian(byte[] a, boolean neg) {
-        this.makeValid();
-        NativeBN.BN_bin2bn(a, a.length, neg, this.bignum);
-    }
-
-    void putLittleEndianInts(int[] a, boolean neg) {
-        this.makeValid();
-        NativeBN.litEndInts2bn(a, a.length, neg, this.bignum);
-    }
-
-    void putBigEndianTwosComplement(byte[] a) {
-        this.makeValid();
-        NativeBN.twosComp2bn(a, a.length, this.bignum);
-    }
-
-
-    long longInt() {
-        return NativeBN.longInt(this.bignum);
-    }
-
-    String decString() {
-        return NativeBN.BN_bn2dec(this.bignum);
-    }
-
-    String hexString() {
-        return NativeBN.BN_bn2hex(this.bignum);
-    }
-
-    byte[] bigEndianMagnitude() {
-        return NativeBN.BN_bn2bin(this.bignum);
-    }
-
-    int[] littleEndianIntsMagnitude() {
-        return NativeBN.bn2litEndInts(this.bignum);
-    }
-
-    int sign() {
-        return NativeBN.sign(this.bignum);
-    }
-
-    void setSign(int val) {
-        if (val > 0) {
-            NativeBN.BN_set_negative(this.bignum, 0);
-        } else {
-            if (val < 0) NativeBN.BN_set_negative(this.bignum, 1);
-        }
-    }
-
-    boolean twosCompFitsIntoBytes(int desiredByteCount) {
-        int actualByteCount = (NativeBN.bitLength(this.bignum) + 7) / 8;
-        return actualByteCount <= desiredByteCount;
-    }
-
-    int bitLength() {
-        return NativeBN.bitLength(this.bignum);
-    }
-
-    boolean isBitSet(int n) {
-        return NativeBN.BN_is_bit_set(this.bignum, n);
-    }
-
-    // n > 0: shift left (multiply)
-    static BigInt shift(BigInt a, int n) {
-        BigInt r = newBigInt();
-        NativeBN.BN_shift(r.bignum, a.bignum, n);
-        return r;
-    }
-
-    void shift(int n) {
-        NativeBN.BN_shift(this.bignum, this.bignum, n);
-    }
-
-    void addPositiveInt(int w) {
-        NativeBN.BN_add_word(this.bignum, w);
-    }
-
-    void multiplyByPositiveInt(int w) {
-        NativeBN.BN_mul_word(this.bignum, w);
-    }
-
-    static int remainderByPositiveInt(BigInt a, int w) {
-        return NativeBN.BN_mod_word(a.bignum, w);
-    }
-
-    static BigInt addition(BigInt a, BigInt b) {
-        BigInt r = newBigInt();
-        NativeBN.BN_add(r.bignum, a.bignum, b.bignum);
-        return r;
-    }
-
-    void add(BigInt a) {
-        NativeBN.BN_add(this.bignum, this.bignum, a.bignum);
-    }
-
-    static BigInt subtraction(BigInt a, BigInt b) {
-        BigInt r = newBigInt();
-        NativeBN.BN_sub(r.bignum, a.bignum, b.bignum);
-        return r;
-    }
-
-
-    static BigInt gcd(BigInt a, BigInt b) {
-        BigInt r = newBigInt();
-        NativeBN.BN_gcd(r.bignum, a.bignum, b.bignum);
-        return r;
-    }
-
-    static BigInt product(BigInt a, BigInt b) {
-        BigInt r = newBigInt();
-        NativeBN.BN_mul(r.bignum, a.bignum, b.bignum);
-        return r;
-    }
-
-    static BigInt bigExp(BigInt a, BigInt p) {
-        // Sign of p is ignored!
-        BigInt r = newBigInt();
-        NativeBN.BN_exp(r.bignum, a.bignum, p.bignum);
-        return r;
-    }
-
-    static BigInt exp(BigInt a, int p) {
-        // Sign of p is ignored!
-        BigInt power = new BigInt();
-        power.putLongInt(p);
-        return bigExp(a, power);
-        // OPTIONAL:
-        // int BN_sqr(BigInteger r, BigInteger a, BN_CTX ctx);
-        // int BN_sqr(BIGNUM *r, const BIGNUM *a,BN_CTX *ctx);
-    }
-
-    static void division(BigInt dividend, BigInt divisor, BigInt quotient, BigInt remainder) {
-        long quot, rem;
-        if (quotient != null) {
-            quotient.makeValid();
-            quot = quotient.bignum;
-        } else {
-            quot = 0;
-        }
-        if (remainder != null) {
-            remainder.makeValid();
-            rem = remainder.bignum;
-        } else {
-            rem = 0;
-        }
-        NativeBN.BN_div(quot, rem, dividend.bignum, divisor.bignum);
-    }
-
-    static BigInt modulus(BigInt a, BigInt m) {
-        // Sign of p is ignored! ?
-        BigInt r = newBigInt();
-        NativeBN.BN_nnmod(r.bignum, a.bignum, m.bignum);
-        return r;
-    }
-
-    static BigInt modExp(BigInt a, BigInt p, BigInt m) {
-        // Sign of p is ignored!
-        BigInt r = newBigInt();
-        NativeBN.BN_mod_exp(r.bignum, a.bignum, p.bignum, m.bignum);
-        return r;
-    }
-
-
-    static BigInt modInverse(BigInt a, BigInt m) {
-        BigInt r = newBigInt();
-        NativeBN.BN_mod_inverse(r.bignum, a.bignum, m.bignum);
-        return r;
-    }
-
-
-    static BigInt generatePrimeDefault(int bitLength) {
-        BigInt r = newBigInt();
-        NativeBN.BN_generate_prime_ex(r.bignum, bitLength, false, 0, 0);
-        return r;
-    }
-
-    boolean isPrime(int certainty) {
-        return NativeBN.BN_primality_test(bignum, certainty, false);
-    }
-}
diff --git a/luni/src/main/java/java/math/BigInteger.java b/luni/src/main/java/java/math/BigInteger.java
deleted file mode 100644
index b96fdb2..0000000
--- a/luni/src/main/java/java/math/BigInteger.java
+++ /dev/null
@@ -1,1275 +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 java.math;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.Random;
-import libcore.util.NonNull;
-import libcore.util.Nullable;
-
-/**
- * An immutable arbitrary-precision signed integer.
- *
- * <h3>Fast Cryptography</h3>
- * This implementation is efficient for operations traditionally used in
- * cryptography, such as the generation of large prime numbers and computation
- * of the modular inverse.
- *
- * <h3>Slow Two's Complement Bitwise Operations</h3>
- * This API includes operations for bitwise operations in two's complement
- * representation. Two's complement is not the internal representation used by
- * this implementation, so such methods may be inefficient. Use {@link
- * java.util.BitSet} for high-performance bitwise operations on
- * arbitrarily-large sequences of bits.
- */
-public class BigInteger extends Number
-        implements Comparable<BigInteger>, Serializable {
-
-    /** This is the serialVersionUID used by the sun implementation. */
-    private static final long serialVersionUID = -8287574255936472291L;
-
-    private transient BigInt bigInt;
-
-    private transient boolean nativeIsValid = false;
-
-    private transient boolean javaIsValid = false;
-
-    /** The magnitude of this in the little-endian representation. */
-    transient int[] digits;
-
-    /**
-     * The length of this in measured in ints. Can be less than
-     * digits.length().
-     */
-    transient int numberLength;
-
-    /** The sign of this. */
-    transient int sign;
-
-    /** The {@code BigInteger} constant 0. */
-    @NonNull public static final BigInteger ZERO = new BigInteger(0, 0);
-
-    /** The {@code BigInteger} constant 1. */
-    @NonNull public static final BigInteger ONE = new BigInteger(1, 1);
-
-    /** The {@code BigInteger} constant 10. */
-    @NonNull public static final BigInteger TEN = new BigInteger(1, 10);
-
-    /** The {@code BigInteger} constant -1. */
-    static final BigInteger MINUS_ONE = new BigInteger(-1, 1);
-
-    /** All the {@code BigInteger} numbers in the range [0,10] are cached. */
-    static final BigInteger[] SMALL_VALUES = { ZERO, ONE, new BigInteger(1, 2),
-            new BigInteger(1, 3), new BigInteger(1, 4), new BigInteger(1, 5),
-            new BigInteger(1, 6), new BigInteger(1, 7), new BigInteger(1, 8),
-            new BigInteger(1, 9), TEN };
-
-    private transient int firstNonzeroDigit = -2;
-
-    /** sign field, used for serialization. */
-    private int signum;
-
-    /** absolute value field, used for serialization */
-    private byte[] magnitude;
-
-    /** Cache for the hash code. */
-    private transient int hashCode = 0;
-
-    BigInteger(BigInt bigInt) {
-        if (bigInt == null || !bigInt.hasNativeBignum()) {
-            throw new AssertionError();
-        }
-        setBigInt(bigInt);
-    }
-
-    BigInteger(int sign, long value) {
-        BigInt bigInt = new BigInt();
-        bigInt.putULongInt(value, (sign < 0));
-        setBigInt(bigInt);
-    }
-
-    /**
-     * Constructs a number without creating new space. This construct should be
-     * used only if the three fields of representation are known.
-     *
-     * @param sign the sign of the number.
-     * @param numberLength the length of the internal array.
-     * @param digits a reference of some array created before.
-     */
-    BigInteger(int sign, int numberLength, int[] digits) {
-        setJavaRepresentation(sign, numberLength, digits);
-    }
-
-    /**
-     * Constructs a random non-negative {@code BigInteger} instance in the range
-     * {@code [0, pow(2, numBits)-1]}.
-     *
-     * @param numBits maximum length of the new {@code BigInteger} in bits.
-     * @param random is the random number generator to be used.
-     * @throws IllegalArgumentException if {@code numBits} < 0.
-     */
-    public BigInteger(int numBits, @NonNull Random random) {
-        if (numBits < 0) {
-            throw new IllegalArgumentException("numBits < 0: " + numBits);
-        }
-        if (numBits == 0) {
-            setJavaRepresentation(0, 1, new int[] { 0 });
-        } else {
-            int sign = 1;
-            int numberLength = (numBits + 31) >> 5;
-            int[] digits = new int[numberLength];
-            for (int i = 0; i < numberLength; i++) {
-                digits[i] = random.nextInt();
-            }
-            // Clear any extra bits.
-            digits[numberLength - 1] >>>= (-numBits) & 31;
-            setJavaRepresentation(sign, numberLength, digits);
-        }
-        javaIsValid = true;
-    }
-
-    /**
-     * Constructs a random {@code BigInteger} instance in the range {@code [0,
-     * pow(2, bitLength)-1]} which is probably prime. The probability that the
-     * returned {@code BigInteger} is prime is greater than
-     * {@code 1 - 1/2<sup>certainty</sup>)}.
-     *
-     * <p><b>Note:</b> the {@code Random} argument is ignored if
-     * {@code bitLength >= 16}, where this implementation will use OpenSSL's
-     * {@code BN_generate_prime_ex} as a source of cryptographically strong pseudo-random numbers.
-     *
-     * @param bitLength length of the new {@code BigInteger} in bits.
-     * @param certainty tolerated primality uncertainty.
-     * @throws ArithmeticException if {@code bitLength < 2}.
-     * @see <a href="http://www.openssl.org/docs/crypto/BN_rand.html">
-     *      Specification of random generator used from OpenSSL library</a>
-     */
-    public BigInteger(int bitLength, int certainty, @NonNull Random random) {
-        if (bitLength < 2) {
-            throw new ArithmeticException("bitLength < 2: " + bitLength);
-        }
-        if (bitLength < 16) {
-            // We have to generate short primes ourselves, because OpenSSL bottoms out at 16 bits.
-            int candidate;
-            do {
-                candidate = random.nextInt() & ((1 << bitLength) - 1);
-                candidate |= (1 << (bitLength - 1)); // Set top bit.
-                if (bitLength > 2) {
-                    candidate |= 1; // Any prime longer than 2 bits must have the bottom bit set.
-                }
-            } while (!isSmallPrime(candidate));
-            BigInt prime = new BigInt();
-            prime.putULongInt(candidate, false);
-            setBigInt(prime);
-        } else {
-            // We need a loop here to work around an OpenSSL bug; http://b/8588028.
-            do {
-                setBigInt(BigInt.generatePrimeDefault(bitLength));
-            } while (bitLength() != bitLength);
-        }
-    }
-
-    private static boolean isSmallPrime(int x) {
-        if (x == 2) {
-            return true;
-        }
-        if ((x % 2) == 0) {
-            return false;
-        }
-        final int max = (int) Math.sqrt(x);
-        for (int i = 3; i <= max; i += 2) {
-            if ((x % i) == 0) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Constructs a new {@code BigInteger} by parsing {@code value}. The string
-     * representation consists of an optional plus or minus sign followed by a
-     * non-empty sequence of decimal digits. Digits are interpreted as if by
-     * {@code Character.digit(char,10)}.
-     *
-     * @param value string representation of the new {@code BigInteger}.
-     * @throws NullPointerException if {@code value == null}.
-     * @throws NumberFormatException if {@code value} is not a valid
-     *     representation of a {@code BigInteger}.
-     */
-    public BigInteger(@NonNull String value) {
-        BigInt bigInt = new BigInt();
-        bigInt.putDecString(value);
-        setBigInt(bigInt);
-    }
-
-    /**
-     * Constructs a new {@code BigInteger} instance by parsing {@code value}.
-     * The string representation consists of an optional plus or minus sign
-     * followed by a non-empty sequence of digits in the specified radix. Digits
-     * are interpreted as if by {@code Character.digit(char, radix)}.
-     *
-     * @param value string representation of the new {@code BigInteger}.
-     * @param radix the base to be used for the conversion.
-     * @throws NullPointerException if {@code value == null}.
-     * @throws NumberFormatException if {@code value} is not a valid
-     *     representation of a {@code BigInteger} or if {@code radix <
-     *     Character.MIN_RADIX} or {@code radix > Character.MAX_RADIX}.
-     */
-    public BigInteger(@NonNull String value, int radix) {
-        if (value == null) {
-            throw new NullPointerException("value == null");
-        }
-        if (radix == 10) {
-            BigInt bigInt = new BigInt();
-            bigInt.putDecString(value);
-            setBigInt(bigInt);
-        } else if (radix == 16) {
-            BigInt bigInt = new BigInt();
-            bigInt.putHexString(value);
-            setBigInt(bigInt);
-        } else {
-            if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
-                throw new NumberFormatException("Invalid radix: " + radix);
-            }
-            if (value.isEmpty()) {
-                throw new NumberFormatException("value.isEmpty()");
-            }
-            BigInteger.parseFromString(this, value, radix);
-        }
-    }
-
-    /**
-     * Constructs a new {@code BigInteger} instance with the given sign and
-     * magnitude.
-     *
-     * @param signum sign of the new {@code BigInteger} (-1 for negative, 0 for
-     *     zero, 1 for positive).
-     * @param magnitude magnitude of the new {@code BigInteger} with the most
-     *     significant byte first.
-     * @throws NullPointerException if {@code magnitude == null}.
-     * @throws NumberFormatException if the sign is not one of -1, 0, 1 or if
-     *     the sign is zero and the magnitude contains non-zero entries.
-     */
-    public BigInteger(int signum, byte @NonNull [] magnitude) {
-        if (magnitude == null) {
-            throw new NullPointerException("magnitude == null");
-        }
-        if (signum < -1 || signum > 1) {
-            throw new NumberFormatException("Invalid signum: " + signum);
-        }
-        if (signum == 0) {
-            for (byte element : magnitude) {
-                if (element != 0) {
-                    throw new NumberFormatException("signum-magnitude mismatch");
-                }
-            }
-        }
-        BigInt bigInt = new BigInt();
-        bigInt.putBigEndian(magnitude, signum < 0);
-        setBigInt(bigInt);
-    }
-
-    /**
-     * Constructs a new {@code BigInteger} from the given two's complement
-     * representation. The most significant byte is the entry at index 0. The
-     * most significant bit of this entry determines the sign of the new {@code
-     * BigInteger} instance. The array must be nonempty.
-     *
-     * @param value two's complement representation of the new {@code
-     *     BigInteger}.
-     * @throws NullPointerException if {@code value == null}.
-     * @throws NumberFormatException if the length of {@code value} is zero.
-     */
-    public BigInteger(byte @NonNull [] value) {
-        if (value.length == 0) {
-            throw new NumberFormatException("value.length == 0");
-        }
-        BigInt bigInt = new BigInt();
-        bigInt.putBigEndianTwosComplement(value);
-        setBigInt(bigInt);
-    }
-
-    /**
-     * Returns the internal native representation of this big integer, computing
-     * it if necessary.
-     */
-    BigInt getBigInt() {
-        if (nativeIsValid) {
-            return bigInt;
-        }
-
-        synchronized (this) {
-            if (nativeIsValid) {
-                return bigInt;
-            }
-            BigInt bigInt = new BigInt();
-            bigInt.putLittleEndianInts(digits, (sign < 0));
-            setBigInt(bigInt);
-            return bigInt;
-        }
-    }
-
-    private void setBigInt(BigInt bigInt) {
-        this.bigInt = bigInt;
-        this.nativeIsValid = true;
-    }
-
-    private void setJavaRepresentation(int sign, int numberLength, int[] digits) {
-        // decrement numberLength to drop leading zeroes...
-        while (numberLength > 0 && digits[--numberLength] == 0) {
-            ;
-        }
-        // ... and then increment it back because we always drop one too many
-        if (digits[numberLength++] == 0) {
-            sign = 0;
-        }
-        this.sign = sign;
-        this.digits = digits;
-        this.numberLength = numberLength;
-        this.javaIsValid = true;
-    }
-
-    void prepareJavaRepresentation() {
-        if (javaIsValid) {
-            return;
-        }
-
-        synchronized (this) {
-            if (javaIsValid) {
-                return;
-            }
-            int sign = bigInt.sign();
-            int[] digits = (sign != 0) ? bigInt.littleEndianIntsMagnitude() : new int[] { 0 };
-            setJavaRepresentation(sign, digits.length, digits);
-        }
-    }
-
-    /** Returns a {@code BigInteger} whose value is equal to {@code value}. */
-    @NonNull public static BigInteger valueOf(long value) {
-        if (value < 0) {
-            if (value != -1) {
-                return new BigInteger(-1, -value);
-            }
-            return MINUS_ONE;
-        } else if (value < SMALL_VALUES.length) {
-            return SMALL_VALUES[(int) value];
-        } else {// (value > 10)
-            return new BigInteger(1, value);
-        }
-    }
-
-    /**
-     * Returns the two's complement representation of this {@code BigInteger} in
-     * a byte array.
-     */
-    public byte @NonNull [] toByteArray() {
-        return twosComplement();
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is the absolute value of {@code
-     * this}.
-     */
-    @NonNull public BigInteger abs() {
-        BigInt bigInt = getBigInt();
-        if (bigInt.sign() >= 0) {
-            return this;
-        }
-        BigInt a = bigInt.copy();
-        a.setSign(1);
-        return new BigInteger(a);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is the {@code -this}.
-     */
-    @NonNull public BigInteger negate() {
-        BigInt bigInt = getBigInt();
-        int sign = bigInt.sign();
-        if (sign == 0) {
-            return this;
-        }
-        BigInt a = bigInt.copy();
-        a.setSign(-sign);
-        return new BigInteger(a);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this + value}.
-     */
-    @NonNull public BigInteger add(@NonNull BigInteger value) {
-        BigInt lhs = getBigInt();
-        BigInt rhs = value.getBigInt();
-        if (rhs.sign() == 0) {
-            return this;
-        }
-        if (lhs.sign() == 0) {
-            return value;
-        }
-        return new BigInteger(BigInt.addition(lhs, rhs));
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this - value}.
-     */
-    @NonNull public BigInteger subtract(@NonNull BigInteger value) {
-        BigInt lhs = getBigInt();
-        BigInt rhs = value.getBigInt();
-        if (rhs.sign() == 0) {
-            return this;
-        }
-        return new BigInteger(BigInt.subtraction(lhs, rhs));
-    }
-
-    /**
-     * Returns the sign of this {@code BigInteger}.
-     *
-     * @return {@code -1} if {@code this < 0}, {@code 0} if {@code this == 0},
-     *     {@code 1} if {@code this > 0}.
-     */
-    public int signum() {
-        if (javaIsValid) {
-            return sign;
-        }
-        return getBigInt().sign();
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this >> n}. For
-     * negative arguments, the result is also negative. The shift distance may
-     * be negative which means that {@code this} is shifted left.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method on negative values is
-     * not recommended as the current implementation is not efficient.
-     *
-     * @param n shift distance
-     * @return {@code this >> n} if {@code n >= 0}; {@code this << (-n)}
-     *     otherwise
-     */
-    @NonNull public BigInteger shiftRight(int n) {
-        return shiftLeft(-n);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this << n}. The
-     * result is equivalent to {@code this * pow(2, n)} if n >= 0. The shift
-     * distance may be negative which means that {@code this} is shifted right.
-     * The result then corresponds to {@code floor(this / pow(2, -n))}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method on negative values is
-     * not recommended as the current implementation is not efficient.
-     *
-     * @param n shift distance.
-     * @return {@code this << n} if {@code n >= 0}; {@code this >> (-n)}.
-     *     otherwise
-     */
-    @NonNull public BigInteger shiftLeft(int n) {
-        if (n == 0) {
-            return this;
-        }
-        int sign = signum();
-        if (sign == 0) {
-            return this;
-        }
-        if ((sign > 0) || (n >= 0)) {
-            return new BigInteger(BigInt.shift(getBigInt(), n));
-        } else {
-            // Negative numbers faking 2's complement:
-            // Not worth optimizing this:
-            // Sticking to Harmony Java implementation.
-            return BitLevel.shiftRight(this, -n);
-        }
-    }
-
-    BigInteger shiftLeftOneBit() {
-        return (signum() == 0) ? this : BitLevel.shiftLeftOneBit(this);
-    }
-
-    /**
-     * Returns the length of the value's two's complement representation without
-     * leading zeros for positive numbers / without leading ones for negative
-     * values.
-     *
-     * <p>The two's complement representation of {@code this} will be at least
-     * {@code bitLength() + 1} bits long.
-     *
-     * <p>The value will fit into an {@code int} if {@code bitLength() < 32} or
-     * into a {@code long} if {@code bitLength() < 64}.
-     *
-     * @return the length of the minimal two's complement representation for
-     *     {@code this} without the sign bit.
-     */
-    public int bitLength() {
-        // Optimization to avoid unnecessary duplicate representation:
-        if (!nativeIsValid && javaIsValid) {
-            return BitLevel.bitLength(this);
-        }
-        return getBigInt().bitLength();
-    }
-
-    /**
-     * Tests whether the bit at position n in {@code this} is set. The result is
-     * equivalent to {@code this & pow(2, n) != 0}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     *
-     * @param n position where the bit in {@code this} has to be inspected.
-     * @throws ArithmeticException if {@code n < 0}.
-     */
-    public boolean testBit(int n) {
-        if (n < 0) {
-            throw new ArithmeticException("n < 0: " + n);
-        }
-        int sign = signum();
-        if (sign > 0 && nativeIsValid && !javaIsValid) {
-            return getBigInt().isBitSet(n);
-        } else {
-            // Negative numbers faking 2's complement:
-            // Not worth optimizing this:
-            // Sticking to Harmony Java implementation.
-            prepareJavaRepresentation();
-            if (n == 0) {
-                return ((digits[0] & 1) != 0);
-            }
-            int intCount = n >> 5;
-            if (intCount >= numberLength) {
-                return (sign < 0);
-            }
-            int digit = digits[intCount];
-            n = (1 << (n & 31)); // int with 1 set to the needed position
-            if (sign < 0) {
-                int firstNonZeroDigit = getFirstNonzeroDigit();
-                if (intCount < firstNonZeroDigit) {
-                    return false;
-                } else if (firstNonZeroDigit == intCount) {
-                    digit = -digit;
-                } else {
-                    digit = ~digit;
-                }
-            }
-            return ((digit & n) != 0);
-        }
-    }
-
-    /**
-     * Returns a {@code BigInteger} which has the same binary representation
-     * as {@code this} but with the bit at position n set. The result is
-     * equivalent to {@code this | pow(2, n)}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     *
-     * @param n position where the bit in {@code this} has to be set.
-     * @throws ArithmeticException if {@code n < 0}.
-     */
-    @NonNull public BigInteger setBit(int n) {
-        prepareJavaRepresentation();
-        if (!testBit(n)) {
-            return BitLevel.flipBit(this, n);
-        } else {
-            return this;
-        }
-    }
-
-    /**
-     * Returns a {@code BigInteger} which has the same binary representation
-     * as {@code this} but with the bit at position n cleared. The result is
-     * equivalent to {@code this & ~pow(2, n)}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     *
-     * @param n position where the bit in {@code this} has to be cleared.
-     * @throws ArithmeticException if {@code n < 0}.
-     */
-    @NonNull public BigInteger clearBit(int n) {
-        prepareJavaRepresentation();
-        if (testBit(n)) {
-            return BitLevel.flipBit(this, n);
-        } else {
-            return this;
-        }
-    }
-
-    /**
-     * Returns a {@code BigInteger} which has the same binary representation
-     * as {@code this} but with the bit at position n flipped. The result is
-     * equivalent to {@code this ^ pow(2, n)}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     *
-     * @param n position where the bit in {@code this} has to be flipped.
-     * @throws ArithmeticException if {@code n < 0}.
-     */
-    @NonNull public BigInteger flipBit(int n) {
-        prepareJavaRepresentation();
-        if (n < 0) {
-            throw new ArithmeticException("n < 0: " + n);
-        }
-        return BitLevel.flipBit(this, n);
-    }
-
-    /**
-     * Returns the position of the lowest set bit in the two's complement
-     * representation of this {@code BigInteger}. If all bits are zero (this==0)
-     * then -1 is returned as result.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     */
-    public int getLowestSetBit() {
-        prepareJavaRepresentation();
-        if (sign == 0) {
-            return -1;
-        }
-        // (sign != 0) implies that exists some non zero digit
-        int i = getFirstNonzeroDigit();
-        return ((i << 5) + Integer.numberOfTrailingZeros(digits[i]));
-    }
-
-    /**
-     * Returns the number of bits in the two's complement representation of
-     * {@code this} which differ from the sign bit. If {@code this} is negative,
-     * the result is equivalent to the number of bits set in the two's
-     * complement representation of {@code -this - 1}.
-     *
-     * <p>Use {@code bitLength(0)} to find the length of the binary value in
-     * bits.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     */
-    public int bitCount() {
-        prepareJavaRepresentation();
-        return BitLevel.bitCount(this);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code ~this}. The result
-     * of this operation is {@code -this-1}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     */
-    @NonNull public BigInteger not() {
-        this.prepareJavaRepresentation();
-        return Logical.not(this);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this & value}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended
-     * as the current implementation is not efficient.
-     *
-     * @param value value to be and'ed with {@code this}.
-     * @throws NullPointerException if {@code value == null}.
-     */
-    @NonNull public BigInteger and(@NonNull BigInteger value) {
-        this.prepareJavaRepresentation();
-        value.prepareJavaRepresentation();
-        return Logical.and(this, value);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this | value}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     *
-     * @param value value to be or'ed with {@code this}.
-     * @throws NullPointerException if {@code value == null}.
-     */
-    @NonNull public BigInteger or(@NonNull BigInteger value) {
-        this.prepareJavaRepresentation();
-        value.prepareJavaRepresentation();
-        return Logical.or(this, value);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this ^ value}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended as
-     * the current implementation is not efficient.
-     *
-     * @param value value to be xor'ed with {@code this}
-     * @throws NullPointerException if {@code value == null}
-     */
-    @NonNull public BigInteger xor(@NonNull BigInteger value) {
-        this.prepareJavaRepresentation();
-        value.prepareJavaRepresentation();
-        return Logical.xor(this, value);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this & ~value}.
-     * Evaluating {@code x.andNot(value)} returns the same result as {@code
-     * x.and(value.not())}.
-     *
-     * <p><b>Implementation Note:</b> Usage of this method is not recommended
-     * as the current implementation is not efficient.
-     *
-     * @param value value to be not'ed and then and'ed with {@code this}.
-     * @throws NullPointerException if {@code value == null}.
-     */
-    @NonNull public BigInteger andNot(@NonNull BigInteger value) {
-        this.prepareJavaRepresentation();
-        value.prepareJavaRepresentation();
-        return Logical.andNot(this, value);
-    }
-
-    /**
-     * Returns this {@code BigInteger} as an int value. If {@code this} is too
-     * big to be represented as an int, then {@code this % (1 << 32)} is
-     * returned.
-     */
-    @Override
-    public int intValue() {
-        if (nativeIsValid && bigInt.twosCompFitsIntoBytes(4)) {
-            return (int) bigInt.longInt();
-        }
-        this.prepareJavaRepresentation();
-        return (sign * digits[0]);
-    }
-
-    /**
-     * Returns this {@code BigInteger} as a long value. If {@code this} is too
-     * big to be represented as a long, then {@code this % pow(2, 64)} is
-     * returned.
-     */
-    @Override
-    public long longValue() {
-        if (nativeIsValid && bigInt.twosCompFitsIntoBytes(8)) {
-            return bigInt.longInt();
-        }
-        prepareJavaRepresentation();
-        long value = numberLength > 1
-                ? ((long) digits[1]) << 32 | digits[0] & 0xFFFFFFFFL
-                : digits[0] & 0xFFFFFFFFL;
-        return sign * value;
-    }
-
-    /**
-     * Returns this {@code BigInteger} as a float. If {@code this} is too big to
-     * be represented as a float, then {@code Float.POSITIVE_INFINITY} or
-     * {@code Float.NEGATIVE_INFINITY} is returned. Note that not all integers
-     * in the range {@code [-Float.MAX_VALUE, Float.MAX_VALUE]} can be exactly
-     * represented as a float.
-     */
-    @Override
-    public float floatValue() {
-        return (float) doubleValue();
-    }
-
-    /**
-     * Returns this {@code BigInteger} as a double. If {@code this} is too big
-     * to be represented as a double, then {@code Double.POSITIVE_INFINITY} or
-     * {@code Double.NEGATIVE_INFINITY} is returned. Note that not all integers
-     * in the range {@code [-Double.MAX_VALUE, Double.MAX_VALUE]} can be exactly
-     * represented as a double.
-     */
-    @Override
-    public double doubleValue() {
-        return Conversion.bigInteger2Double(this);
-    }
-
-    /**
-     * Compares this {@code BigInteger} with {@code value}. Returns {@code -1}
-     * if {@code this < value}, {@code 0} if {@code this == value} and {@code 1}
-     * if {@code this > value}, .
-     *
-     * @param value value to be compared with {@code this}.
-     * @throws NullPointerException if {@code value == null}.
-     */
-    public int compareTo(@NonNull BigInteger value) {
-        return BigInt.cmp(getBigInt(), value.getBigInt());
-    }
-
-    /**
-     * Returns the minimum of this {@code BigInteger} and {@code value}.
-     *
-     * @param value value to be used to compute the minimum with {@code this}.
-     * @throws NullPointerException if {@code value == null}.
-     */
-    @NonNull public BigInteger min(@NonNull BigInteger value) {
-        return this.compareTo(value) == -1 ? this : value;
-    }
-
-    /**
-     * Returns the maximum of this {@code BigInteger} and {@code value}.
-     *
-     * @param value value to be used to compute the maximum with {@code this}
-     * @throws NullPointerException if {@code value == null}
-     */
-    @NonNull public BigInteger max(@NonNull BigInteger value) {
-        return this.compareTo(value) == 1 ? this : value;
-    }
-
-    @Override
-    public int hashCode() {
-        if (hashCode == 0) {
-            prepareJavaRepresentation();
-            int hash = 0;
-            for (int i = 0; i < numberLength; ++i) {
-                hash = hash * 33 + digits[i];
-            }
-            hashCode = hash * sign;
-        }
-        return hashCode;
-    }
-
-    @Override
-    public boolean equals(@Nullable Object x) {
-        if (this == x) {
-            return true;
-        }
-        if (x instanceof BigInteger) {
-            return this.compareTo((BigInteger) x) == 0;
-        }
-        return false;
-    }
-
-    /**
-     * Returns a string representation of this {@code BigInteger} in decimal
-     * form.
-     */
-    @Override
-    @NonNull public String toString() {
-        return getBigInt().decString();
-    }
-
-    /**
-     * Returns a string containing a string representation of this {@code
-     * BigInteger} with base radix. If {@code radix < Character.MIN_RADIX} or
-     * {@code radix > Character.MAX_RADIX} then a decimal representation is
-     * returned. The characters of the string representation are generated with
-     * method {@code Character.forDigit}.
-     *
-     * @param radix base to be used for the string representation.
-     */
-    @NonNull public String toString(int radix) {
-        if (radix == 10) {
-            return getBigInt().decString();
-        } else {
-            prepareJavaRepresentation();
-            return Conversion.bigInteger2String(this, radix);
-        }
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is greatest common divisor
-     * of {@code this} and {@code value}. If {@code this == 0} and {@code
-     * value == 0} then zero is returned, otherwise the result is positive.
-     *
-     * @param value value with which the greatest common divisor is computed.
-     * @throws NullPointerException if {@code value == null}.
-     */
-    @NonNull public BigInteger gcd(@NonNull BigInteger value) {
-        // First optimize the case in which the two arguments have very different
-        // length.
-        int thisLen = bitLength();
-        int valueLen = value.bitLength();
-        final int gcdDirectRatio = 16;
-        if (thisLen > gcdDirectRatio * valueLen) {
-            // A division-based step reduces the length of this by a factor of at
-            // least gcdDirectRatio, thus ensuring that a division-based step will
-            // easily pay for itself.
-            if (value.signum() == 0) {
-                return this.abs();
-            }
-            return value.gcd(this.mod(value.abs()));
-        } else if (valueLen > gcdDirectRatio * thisLen) {
-            if (signum() == 0) {
-                return value.abs();
-            }
-            return this.gcd(value.mod(this.abs()));
-        }
-
-        return new BigInteger(BigInt.gcd(getBigInt(), value.getBigInt()));
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this * value}.
-     *
-     * @throws NullPointerException if {@code value == null}.
-     */
-    @NonNull public BigInteger multiply(@NonNull BigInteger value) {
-        return new BigInteger(BigInt.product(getBigInt(), value.getBigInt()));
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code pow(this, exp)}.
-     *
-     * @throws ArithmeticException if {@code exp < 0}.
-     */
-    @NonNull public BigInteger pow(int exp) {
-        if (exp < 0) {
-            throw new ArithmeticException("exp < 0: " + exp);
-        }
-        return new BigInteger(BigInt.exp(getBigInt(), exp));
-    }
-
-    /**
-     * Returns a two element {@code BigInteger} array containing
-     * {@code this / divisor} at index 0 and {@code this % divisor} at index 1.
-     *
-     * @param divisor value by which {@code this} is divided.
-     * @throws NullPointerException if {@code divisor == null}.
-     * @throws ArithmeticException if {@code divisor == 0}.
-     * @see #divide
-     * @see #remainder
-     */
-    public @NonNull BigInteger @NonNull [] divideAndRemainder(@NonNull BigInteger divisor) {
-        BigInt divisorBigInt = divisor.getBigInt();
-        BigInt quotient = new BigInt();
-        BigInt remainder = new BigInt();
-        BigInt.division(getBigInt(), divisorBigInt, quotient, remainder);
-        return new BigInteger[] {new BigInteger(quotient), new BigInteger(remainder) };
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this / divisor}.
-     *
-     * @param divisor value by which {@code this} is divided.
-     * @return {@code this / divisor}.
-     * @throws NullPointerException if {@code divisor == null}.
-     * @throws ArithmeticException if {@code divisor == 0}.
-     */
-    @NonNull public BigInteger divide(@NonNull BigInteger divisor) {
-        BigInt quotient = new BigInt();
-        BigInt.division(getBigInt(), divisor.getBigInt(), quotient, null);
-        return new BigInteger(quotient);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this % divisor}.
-     * Regarding signs this methods has the same behavior as the % operator on
-     * ints: the sign of the remainder is the same as the sign of this.
-     *
-     * @param divisor value by which {@code this} is divided.
-     * @throws NullPointerException if {@code divisor == null}.
-     * @throws ArithmeticException if {@code divisor == 0}.
-     */
-    @NonNull public BigInteger remainder(@NonNull BigInteger divisor) {
-        BigInt remainder = new BigInt();
-        BigInt.division(getBigInt(), divisor.getBigInt(), null, remainder);
-        return new BigInteger(remainder);
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code 1/this mod m}. The
-     * modulus {@code m} must be positive. The result is guaranteed to be in the
-     * interval {@code [0, m)} (0 inclusive, m exclusive). If {@code this} is
-     * not relatively prime to m, then an exception is thrown.
-     *
-     * @param m the modulus.
-     * @throws NullPointerException if {@code m == null}
-     * @throws ArithmeticException if {@code m < 0 or} if {@code this} is not
-     *     relatively prime to {@code m}
-     */
-    @NonNull public BigInteger modInverse(@NonNull BigInteger m) {
-        if (m.signum() <= 0) {
-            throw new ArithmeticException("modulus not positive");
-        }
-        return new BigInteger(BigInt.modInverse(getBigInt(), m.getBigInt()));
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code
-     * pow(this, exponent) mod modulus}. The modulus must be positive. The
-     * result is guaranteed to be in the interval {@code [0, modulus)}.
-     * If the exponent is negative, then
-     * {@code pow(this.modInverse(modulus), -exponent) mod modulus} is computed.
-     * The inverse of this only exists if {@code this} is relatively prime to the modulus,
-     * otherwise an exception is thrown.
-     *
-     * @throws NullPointerException if {@code modulus == null} or {@code exponent == null}.
-     * @throws ArithmeticException if {@code modulus < 0} or if {@code exponent < 0} and
-     *     not relatively prime to {@code modulus}.
-     */
-    @NonNull public BigInteger modPow(@NonNull BigInteger exponent, @NonNull BigInteger modulus) {
-        if (modulus.signum() <= 0) {
-            throw new ArithmeticException("modulus.signum() <= 0");
-        }
-        int exponentSignum = exponent.signum();
-        if (exponentSignum == 0) { // OpenSSL gets this case wrong; http://b/8574367.
-            return ONE.mod(modulus);
-        }
-        BigInteger base = exponentSignum < 0 ? modInverse(modulus) : this;
-        return new BigInteger(BigInt.modExp(base.getBigInt(), exponent.getBigInt(), modulus.getBigInt()));
-    }
-
-    /**
-     * Returns a {@code BigInteger} whose value is {@code this mod m}. The
-     * modulus {@code m} must be positive. The result is guaranteed to be in the
-     * interval {@code [0, m)} (0 inclusive, m exclusive). The behavior of this
-     * function is not equivalent to the behavior of the % operator defined for
-     * the built-in {@code int}'s.
-     *
-     * @param m the modulus.
-     * @return {@code this mod m}.
-     * @throws NullPointerException if {@code m == null}.
-     * @throws ArithmeticException if {@code m < 0}.
-     */
-    @NonNull public BigInteger mod(@NonNull BigInteger m) {
-        if (m.signum() <= 0) {
-            throw new ArithmeticException("m.signum() <= 0");
-        }
-        return new BigInteger(BigInt.modulus(getBigInt(), m.getBigInt()));
-    }
-
-    /**
-     * Tests whether this {@code BigInteger} is probably prime. If {@code true}
-     * is returned, then this is prime with a probability greater than
-     * {@code 1 - 1/2<sup>certainty</sup>)}. If {@code false} is returned, then this
-     * is definitely composite. If the argument {@code certainty} <= 0, then
-     * this method returns true.
-     *
-     * @param certainty tolerated primality uncertainty.
-     * @return {@code true}, if {@code this} is probably prime, {@code false}
-     *     otherwise.
-     */
-    public boolean isProbablePrime(int certainty) {
-        if (certainty <= 0) {
-            return true;
-        }
-        return getBigInt().isPrime(certainty);
-    }
-
-    /**
-     * Returns the smallest integer x > {@code this} which is probably prime as
-     * a {@code BigInteger} instance. The probability that the returned {@code
-     * BigInteger} is prime is greater than {@code 1 - 1/2<sup>100</sup>}.
-     *
-     * @return smallest integer > {@code this} which is probably prime.
-     * @throws ArithmeticException if {@code this < 0}.
-     */
-    @NonNull public BigInteger nextProbablePrime() {
-        if (sign < 0) {
-            throw new ArithmeticException("sign < 0");
-        }
-        return Primality.nextProbablePrime(this);
-    }
-
-    /**
-     * Returns a random positive {@code BigInteger} instance in the range {@code
-     * [0, pow(2, bitLength)-1]} which is probably prime. The probability that
-     * the returned {@code BigInteger} is prime is greater than {@code 1 - 1/2<sup>100</sup>)}.
-     *
-     * @param bitLength length of the new {@code BigInteger} in bits.
-     * @return probably prime random {@code BigInteger} instance.
-     * @throws IllegalArgumentException if {@code bitLength < 2}.
-     */
-    @NonNull public static BigInteger probablePrime(int bitLength, @NonNull Random random) {
-        return new BigInteger(bitLength, 100, random);
-    }
-
-    /* Private Methods */
-
-    /**
-     * Returns the two's complement representation of this BigInteger in a byte
-     * array.
-     */
-    private byte[] twosComplement() {
-        prepareJavaRepresentation();
-        if (this.sign == 0) {
-            return new byte[] { 0 };
-        }
-        BigInteger temp = this;
-        int bitLen = bitLength();
-        int iThis = getFirstNonzeroDigit();
-        int bytesLen = (bitLen >> 3) + 1;
-        /* Puts the little-endian int array representing the magnitude
-         * of this BigInteger into the big-endian byte array. */
-        byte[] bytes = new byte[bytesLen];
-        int firstByteNumber = 0;
-        int highBytes;
-        int bytesInInteger = 4;
-        int hB;
-
-        if (bytesLen - (numberLength << 2) == 1) {
-            bytes[0] = (byte) ((sign < 0) ? -1 : 0);
-            highBytes = 4;
-            firstByteNumber++;
-        } else {
-            hB = bytesLen & 3;
-            highBytes = (hB == 0) ? 4 : hB;
-        }
-
-        int digitIndex = iThis;
-        bytesLen -= iThis << 2;
-
-        if (sign < 0) {
-            int digit = -temp.digits[digitIndex];
-            digitIndex++;
-            if (digitIndex == numberLength) {
-                bytesInInteger = highBytes;
-            }
-            for (int i = 0; i < bytesInInteger; i++, digit >>= 8) {
-                bytes[--bytesLen] = (byte) digit;
-            }
-            while (bytesLen > firstByteNumber) {
-                digit = ~temp.digits[digitIndex];
-                digitIndex++;
-                if (digitIndex == numberLength) {
-                    bytesInInteger = highBytes;
-                }
-                for (int i = 0; i < bytesInInteger; i++, digit >>= 8) {
-                    bytes[--bytesLen] = (byte) digit;
-                }
-            }
-        } else {
-            while (bytesLen > firstByteNumber) {
-                int digit = temp.digits[digitIndex];
-                digitIndex++;
-                if (digitIndex == numberLength) {
-                    bytesInInteger = highBytes;
-                }
-                for (int i = 0; i < bytesInInteger; i++, digit >>= 8) {
-                    bytes[--bytesLen] = (byte) digit;
-                }
-            }
-        }
-        return bytes;
-    }
-
-
-    static int multiplyByInt(int[] res, int[] a, int aSize, int factor) {
-        long carry = 0;
-
-        for (int i = 0; i < aSize; i++) {
-            carry += (a[i] & 0xFFFFFFFFL) * (factor & 0xFFFFFFFFL);
-            res[i] = (int) carry;
-            carry >>>= 32;
-        }
-        return (int) carry;
-    }
-
-    static int inplaceAdd(int[] a, int aSize, int addend) {
-        long carry = addend & 0xFFFFFFFFL;
-
-        for (int i = 0; (carry != 0) && (i < aSize); i++) {
-            carry += a[i] & 0xFFFFFFFFL;
-            a[i] = (int) carry;
-            carry >>= 32;
-        }
-        return (int) carry;
-    }
-
-    /** @see BigInteger#BigInteger(String, int) */
-    private static void parseFromString(BigInteger bi, String value, int radix) {
-        int stringLength = value.length();
-        int endChar = stringLength;
-
-        int sign;
-        int startChar;
-        if (value.charAt(0) == '-') {
-            sign = -1;
-            startChar = 1;
-            stringLength--;
-        } else {
-            sign = 1;
-            startChar = 0;
-        }
-
-        /*
-         * We use the following algorithm: split a string into portions of n
-         * characters and convert each portion to an integer according to the
-         * radix. Then convert an pow(radix, n) based number to binary using the
-         * multiplication method. See D. Knuth, The Art of Computer Programming,
-         * vol. 2.
-         */
-
-        int charsPerInt = Conversion.digitFitInInt[radix];
-        int bigRadixDigitsLength = stringLength / charsPerInt;
-        int topChars = stringLength % charsPerInt;
-
-        if (topChars != 0) {
-            bigRadixDigitsLength++;
-        }
-        int[] digits = new int[bigRadixDigitsLength];
-        // Get the maximal power of radix that fits in int
-        int bigRadix = Conversion.bigRadices[radix - 2];
-        // Parse an input string and accumulate the BigInteger's magnitude
-        int digitIndex = 0; // index of digits array
-        int substrEnd = startChar + ((topChars == 0) ? charsPerInt : topChars);
-
-        for (int substrStart = startChar; substrStart < endChar;
-                substrStart = substrEnd, substrEnd = substrStart + charsPerInt) {
-            int bigRadixDigit = Integer.parseInt(value.substring(substrStart, substrEnd), radix);
-            int newDigit = multiplyByInt(digits, digits, digitIndex, bigRadix);
-            newDigit += inplaceAdd(digits, digitIndex, bigRadixDigit);
-            digits[digitIndex++] = newDigit;
-        }
-        int numberLength = digitIndex;
-        bi.setJavaRepresentation(sign, numberLength, digits);
-    }
-
-    int getFirstNonzeroDigit() {
-        if (firstNonzeroDigit == -2) {
-            int i;
-            if (this.sign == 0) {
-                i = -1;
-            } else {
-                for (i = 0; digits[i] == 0; i++) {
-                    ;
-                }
-            }
-            firstNonzeroDigit = i;
-        }
-        return firstNonzeroDigit;
-    }
-
-    /**
-     * Returns a copy of the current instance to achieve immutability
-     */
-    BigInteger copy() {
-        prepareJavaRepresentation();
-        int[] copyDigits = new int[numberLength];
-        System.arraycopy(digits, 0, copyDigits, 0, numberLength);
-        return new BigInteger(sign, numberLength, copyDigits);
-    }
-
-    /**
-     * Assigns all transient fields upon deserialization of a {@code BigInteger}
-     * instance.
-     */
-    private void readObject(ObjectInputStream in)
-            throws IOException, ClassNotFoundException {
-        in.defaultReadObject();
-        BigInt bigInt = new BigInt();
-        bigInt.putBigEndian(magnitude, signum < 0);
-        setBigInt(bigInt);
-    }
-
-    /**
-     * Prepares this {@code BigInteger} for serialization, i.e. the
-     * non-transient fields {@code signum} and {@code magnitude} are assigned.
-     */
-    private void writeObject(ObjectOutputStream out) throws IOException {
-        BigInt bigInt = getBigInt();
-        signum = bigInt.sign();
-        magnitude = bigInt.bigEndianMagnitude();
-        out.defaultWriteObject();
-    }
-}
diff --git a/luni/src/main/java/java/math/BitLevel.java b/luni/src/main/java/java/math/BitLevel.java
deleted file mode 100644
index 91f7a9b..0000000
--- a/luni/src/main/java/java/math/BitLevel.java
+++ /dev/null
@@ -1,255 +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 java.math;
-
-/**
- * Static library that provides all the <b>bit level</b> operations for
- * {@link BigInteger}. The operations are:
- * <ul type="circle">
- * <li>Left Shifting</li>
- * <li>Right Shifting</li>
- * <li>Bit clearing</li>
- * <li>Bit setting</li>
- * <li>Bit counting</li>
- * <li>Bit testing</li>
- * <li>Getting of the lowest bit set</li>
- * </ul>
- * All operations are provided in immutable way, and some in both mutable and
- * immutable.
- */
-class BitLevel {
-
-    /** Just to denote that this class can't be instantiated. */
-    private BitLevel() {}
-
-    /** @see BigInteger#bitLength() */
-    static int bitLength(BigInteger val) {
-        val.prepareJavaRepresentation();
-        if (val.sign == 0) {
-            return 0;
-        }
-        int bLength = (val.numberLength << 5);
-        int highDigit = val.digits[val.numberLength - 1];
-
-        if (val.sign < 0) {
-            int i = val.getFirstNonzeroDigit();
-            // We reduce the problem to the positive case.
-            if (i == val.numberLength - 1) {
-                highDigit--;
-            }
-        }
-        // Subtracting all sign bits
-        bLength -= Integer.numberOfLeadingZeros(highDigit);
-        return bLength;
-    }
-
-    /** @see BigInteger#bitCount() */
-    static int bitCount(BigInteger val) {
-        val.prepareJavaRepresentation();
-        int bCount = 0;
-
-        if (val.sign == 0) {
-            return 0;
-        }
-
-        int i = val.getFirstNonzeroDigit();
-        if (val.sign > 0) {
-            for ( ; i < val.numberLength; i++) {
-                bCount += Integer.bitCount(val.digits[i]);
-            }
-        } else {// (sign < 0)
-            // this digit absorbs the carry
-            bCount += Integer.bitCount(-val.digits[i]);
-            for (i++; i < val.numberLength; i++) {
-                bCount += Integer.bitCount(~val.digits[i]);
-            }
-            // We take the complement sum:
-            bCount = (val.numberLength << 5) - bCount;
-        }
-        return bCount;
-    }
-
-    /**
-     * Performs a fast bit testing for positive numbers. The bit to to be tested
-     * must be in the range {@code [0, val.bitLength()-1]}
-     */
-    static boolean testBit(BigInteger val, int n) {
-        val.prepareJavaRepresentation();
-        // PRE: 0 <= n < val.bitLength()
-        return ((val.digits[n >> 5] & (1 << (n & 31))) != 0);
-    }
-
-    /**
-     * Check if there are 1s in the lowest bits of this BigInteger
-     *
-     * @param numberOfBits the number of the lowest bits to check
-     * @return false if all bits are 0s, true otherwise
-     */
-    static boolean nonZeroDroppedBits(int numberOfBits, int[] digits) {
-        int intCount = numberOfBits >> 5;
-        int bitCount = numberOfBits & 31;
-        int i;
-
-        for (i = 0; (i < intCount) && (digits[i] == 0); i++) {
-            ;
-        }
-        return ((i != intCount) || (digits[i] << (32 - bitCount) != 0));
-    }
-
-    static void shiftLeftOneBit(int[] result, int[] source, int srcLen) {
-        int carry = 0;
-        for (int i = 0; i < srcLen; i++) {
-            int val = source[i];
-            result[i] = (val << 1) | carry;
-            carry = val >>> 31;
-        }
-        if(carry != 0) {
-            result[srcLen] = carry;
-        }
-    }
-
-    static BigInteger shiftLeftOneBit(BigInteger source) {
-        source.prepareJavaRepresentation();
-        int srcLen = source.numberLength;
-        int resLen = srcLen + 1;
-        int[] resDigits = new int[resLen];
-        shiftLeftOneBit(resDigits, source.digits, srcLen);
-        return new BigInteger(source.sign, resLen, resDigits);
-    }
-
-    /** @see BigInteger#shiftRight(int) */
-    static BigInteger shiftRight(BigInteger source, int count) {
-        source.prepareJavaRepresentation();
-        int intCount = count >> 5; // count of integers
-        count &= 31; // count of remaining bits
-        if (intCount >= source.numberLength) {
-            return ((source.sign < 0) ? BigInteger.MINUS_ONE : BigInteger.ZERO);
-        }
-        int i;
-        int resLength = source.numberLength - intCount;
-        int[] resDigits = new int[resLength + 1];
-
-        shiftRight(resDigits, resLength, source.digits, intCount, count);
-        if (source.sign < 0) {
-            // Checking if the dropped bits are zeros (the remainder equals to
-            // 0)
-            for (i = 0; (i < intCount) && (source.digits[i] == 0); i++) {
-                ;
-            }
-            // If the remainder is not zero, add 1 to the result
-            if ((i < intCount)
-                    || ((count > 0) && ((source.digits[i] << (32 - count)) != 0))) {
-                for (i = 0; (i < resLength) && (resDigits[i] == -1); i++) {
-                    resDigits[i] = 0;
-                }
-                if (i == resLength) {
-                    resLength++;
-                }
-                resDigits[i]++;
-            }
-        }
-        return new BigInteger(source.sign, resLength, resDigits);
-    }
-
-    /**
-     * Shifts right an array of integers. Total shift distance in bits is
-     * intCount * 32 + count.
-     *
-     * @param result
-     *            the destination array
-     * @param resultLen
-     *            the destination array's length
-     * @param source
-     *            the source array
-     * @param intCount
-     *            the number of elements to be shifted
-     * @param count
-     *            the number of bits to be shifted
-     * @return dropped bit's are all zero (i.e. remaider is zero)
-     */
-    static boolean shiftRight(int[] result, int resultLen, int[] source, int intCount, int count) {
-        int i;
-        boolean allZero = true;
-        for (i = 0; i < intCount; i++)
-            allZero &= source[i] == 0;
-        if (count == 0) {
-            System.arraycopy(source, intCount, result, 0, resultLen);
-            i = resultLen;
-        } else {
-            int leftShiftCount = 32 - count;
-
-            allZero &= ( source[i] << leftShiftCount ) == 0;
-            for (i = 0; i < resultLen - 1; i++) {
-                result[i] = ( source[i + intCount] >>> count )
-                | ( source[i + intCount + 1] << leftShiftCount );
-            }
-            result[i] = ( source[i + intCount] >>> count );
-            i++;
-        }
-
-        return allZero;
-    }
-
-
-    /**
-     * Performs a flipBit on the BigInteger, returning a BigInteger with the the
-     * specified bit flipped.
-     */
-    static BigInteger flipBit(BigInteger val, int n){
-        val.prepareJavaRepresentation();
-        int resSign = (val.sign == 0) ? 1 : val.sign;
-        int intCount = n >> 5;
-        int bitN = n & 31;
-        int resLength = Math.max(intCount + 1, val.numberLength) + 1;
-        int[] resDigits = new int[resLength];
-        int i;
-
-        int bitNumber = 1 << bitN;
-        System.arraycopy(val.digits, 0, resDigits, 0, val.numberLength);
-
-        if (val.sign < 0) {
-            if (intCount >= val.numberLength) {
-                resDigits[intCount] = bitNumber;
-            } else {
-                //val.sign<0 y intCount < val.numberLength
-                int firstNonZeroDigit = val.getFirstNonzeroDigit();
-                if (intCount > firstNonZeroDigit) {
-                    resDigits[intCount] ^= bitNumber;
-                } else if (intCount < firstNonZeroDigit) {
-                    resDigits[intCount] = -bitNumber;
-                    for (i=intCount + 1; i < firstNonZeroDigit; i++) {
-                        resDigits[i]=-1;
-                    }
-                    resDigits[i] = resDigits[i]--;
-                } else {
-                    i = intCount;
-                    resDigits[i] = -((-resDigits[intCount]) ^ bitNumber);
-                    if (resDigits[i] == 0) {
-                        for (i++; resDigits[i] == -1 ; i++) {
-                            resDigits[i] = 0;
-                        }
-                        resDigits[i]++;
-                    }
-                }
-            }
-        } else {//case where val is positive
-            resDigits[intCount] ^= bitNumber;
-        }
-        return new BigInteger(resSign, resLength, resDigits);
-    }
-}
diff --git a/luni/src/main/java/java/math/Conversion.java b/luni/src/main/java/java/math/Conversion.java
deleted file mode 100644
index 585fff4..0000000
--- a/luni/src/main/java/java/math/Conversion.java
+++ /dev/null
@@ -1,461 +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 java.math;
-
-/**
- * Static library that provides {@link BigInteger} base conversion from/to any
- * integer represented in an {@link java.lang.String} Object.
- */
-class Conversion {
-
-    /** Just to denote that this class can't be instantiated */
-    private Conversion() {}
-
-    /**
-     * Holds the maximal exponent for each radix, so that radix<sup>digitFitInInt[radix]</sup>
-     * fit in an {@code int} (32 bits).
-     */
-    static final int[] digitFitInInt = { -1, -1, 31, 19, 15, 13, 11,
-            11, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6,
-            6, 6, 6, 6, 6, 6, 6, 5 };
-
-    /**
-     * bigRadices values are precomputed maximal powers of radices (integer
-     * numbers from 2 to 36) that fit into unsigned int (32 bits). bigRadices[0] =
-     * 2 ^ 31, bigRadices[8] = 10 ^ 9, etc.
-     */
-
-    static final int[] bigRadices = { -2147483648, 1162261467,
-            1073741824, 1220703125, 362797056, 1977326743, 1073741824,
-            387420489, 1000000000, 214358881, 429981696, 815730721, 1475789056,
-            170859375, 268435456, 410338673, 612220032, 893871739, 1280000000,
-            1801088541, 113379904, 148035889, 191102976, 244140625, 308915776,
-            387420489, 481890304, 594823321, 729000000, 887503681, 1073741824,
-            1291467969, 1544804416, 1838265625, 60466176 };
-
-
-    /** @see BigInteger#toString(int) */
-    static String bigInteger2String(BigInteger val, int radix) {
-        val.prepareJavaRepresentation();
-        int sign = val.sign;
-        int numberLength = val.numberLength;
-        int[] digits = val.digits;
-
-        if (sign == 0) {
-            return "0";
-        }
-        if (numberLength == 1) {
-            int highDigit = digits[numberLength - 1];
-            long v = highDigit & 0xFFFFFFFFL;
-            if (sign < 0) {
-                v = -v;
-            }
-            return Long.toString(v, radix);
-        }
-        if ((radix == 10) || (radix < Character.MIN_RADIX)
-                || (radix > Character.MAX_RADIX)) {
-            return val.toString();
-        }
-        double bitsForRadixDigit;
-        bitsForRadixDigit = Math.log(radix) / Math.log(2);
-        int resLengthInChars = (int) (val.abs().bitLength() / bitsForRadixDigit + ((sign < 0) ? 1
-                : 0)) + 1;
-
-        char[] result = new char[resLengthInChars];
-        int currentChar = resLengthInChars;
-        int resDigit;
-        if (radix != 16) {
-            int[] temp = new int[numberLength];
-            System.arraycopy(digits, 0, temp, 0, numberLength);
-            int tempLen = numberLength;
-            int charsPerInt = digitFitInInt[radix];
-            int i;
-            // get the maximal power of radix that fits in int
-            int bigRadix = bigRadices[radix - 2];
-            while (true) {
-                // divide the array of digits by bigRadix and convert remainders
-                // to characters collecting them in the char array
-                resDigit = Division.divideArrayByInt(temp, temp, tempLen,
-                        bigRadix);
-                int previous = currentChar;
-                do {
-                    result[--currentChar] = Character.forDigit(
-                            resDigit % radix, radix);
-                } while (((resDigit /= radix) != 0) && (currentChar != 0));
-                int delta = charsPerInt - previous + currentChar;
-                for (i = 0; i < delta && currentChar > 0; i++) {
-                    result[--currentChar] = '0';
-                }
-                for (i = tempLen - 1; (i > 0) && (temp[i] == 0); i--) {
-                    ;
-                }
-                tempLen = i + 1;
-                if ((tempLen == 1) && (temp[0] == 0)) { // the quotient is 0
-                    break;
-                }
-            }
-        } else {
-            // radix == 16
-            for (int i = 0; i < numberLength; i++) {
-                for (int j = 0; (j < 8) && (currentChar > 0); j++) {
-                    resDigit = digits[i] >> (j << 2) & 0xf;
-                    result[--currentChar] = Character.forDigit(resDigit, 16);
-                }
-            }
-        }
-        while (result[currentChar] == '0') {
-            currentChar++;
-        }
-        if (sign == -1) {
-            result[--currentChar] = '-';
-        }
-        return new String(result, currentChar, resLengthInChars - currentChar);
-    }
-
-    /**
-     * Builds the correspondent {@code String} representation of {@code val}
-     * being scaled by {@code scale}.
-     *
-     * @see BigInteger#toString()
-     * @see BigDecimal#toString()
-     */
-    static String toDecimalScaledString(BigInteger val, int scale) {
-        val.prepareJavaRepresentation();
-        int sign = val.sign;
-        int numberLength = val.numberLength;
-        int[] digits = val.digits;
-        int resLengthInChars;
-        int currentChar;
-        char[] result;
-
-        if (sign == 0) {
-            switch (scale) {
-                case 0:
-                    return "0";
-                case 1:
-                    return "0.0";
-                case 2:
-                    return "0.00";
-                case 3:
-                    return "0.000";
-                case 4:
-                    return "0.0000";
-                case 5:
-                    return "0.00000";
-                case 6:
-                    return "0.000000";
-                default:
-                    StringBuilder result1 = new StringBuilder();
-                    if (scale < 0) {
-                        result1.append("0E+");
-                    } else {
-                        result1.append("0E");
-                    }
-                    result1.append(-scale);
-                    return result1.toString();
-            }
-        }
-        // one 32-bit unsigned value may contains 10 decimal digits
-        resLengthInChars = numberLength * 10 + 1 + 7;
-        // Explanation why +1+7:
-        // +1 - one char for sign if needed.
-        // +7 - For "special case 2" (see below) we have 7 free chars for
-        // inserting necessary scaled digits.
-        result = new char[resLengthInChars + 1];
-        // allocated [resLengthInChars+1] characters.
-        // a free latest character may be used for "special case 1" (see
-        // below)
-        currentChar = resLengthInChars;
-        if (numberLength == 1) {
-            int highDigit = digits[0];
-            if (highDigit < 0) {
-                long v = highDigit & 0xFFFFFFFFL;
-                do {
-                    long prev = v;
-                    v /= 10;
-                    result[--currentChar] = (char) (0x0030 + ((int) (prev - v * 10)));
-                } while (v != 0);
-            } else {
-                int v = highDigit;
-                do {
-                    int prev = v;
-                    v /= 10;
-                    result[--currentChar] = (char) (0x0030 + (prev - v * 10));
-                } while (v != 0);
-            }
-        } else {
-            int[] temp = new int[numberLength];
-            int tempLen = numberLength;
-            System.arraycopy(digits, 0, temp, 0, tempLen);
-            BIG_LOOP: while (true) {
-                // divide the array of digits by bigRadix and convert
-                // remainders
-                // to characters collecting them in the char array
-                long result11 = 0;
-                for (int i1 = tempLen - 1; i1 >= 0; i1--) {
-                    long temp1 = (result11 << 32)
-                            + (temp[i1] & 0xFFFFFFFFL);
-                    long res = divideLongByBillion(temp1);
-                    temp[i1] = (int) res;
-                    result11 = (int) (res >> 32);
-                }
-                int resDigit = (int) result11;
-                int previous = currentChar;
-                do {
-                    result[--currentChar] = (char) (0x0030 + (resDigit % 10));
-                } while (((resDigit /= 10) != 0) && (currentChar != 0));
-                int delta = 9 - previous + currentChar;
-                for (int i = 0; (i < delta) && (currentChar > 0); i++) {
-                    result[--currentChar] = '0';
-                }
-                int j = tempLen - 1;
-                for (; temp[j] == 0; j--) {
-                    if (j == 0) { // means temp[0] == 0
-                        break BIG_LOOP;
-                    }
-                }
-                tempLen = j + 1;
-            }
-            while (result[currentChar] == '0') {
-                currentChar++;
-            }
-        }
-        boolean negNumber = (sign < 0);
-        int exponent = resLengthInChars - currentChar - scale - 1;
-        if (scale == 0) {
-            if (negNumber) {
-                result[--currentChar] = '-';
-            }
-            return new String(result, currentChar, resLengthInChars
-                    - currentChar);
-        }
-        if ((scale > 0) && (exponent >= -6)) {
-            if (exponent >= 0) {
-                // special case 1
-                int insertPoint = currentChar + exponent;
-                for (int j = resLengthInChars - 1; j >= insertPoint; j--) {
-                    result[j + 1] = result[j];
-                }
-                result[++insertPoint] = '.';
-                if (negNumber) {
-                    result[--currentChar] = '-';
-                }
-                return new String(result, currentChar, resLengthInChars
-                        - currentChar + 1);
-            }
-            // special case 2
-            for (int j = 2; j < -exponent + 1; j++) {
-                result[--currentChar] = '0';
-            }
-            result[--currentChar] = '.';
-            result[--currentChar] = '0';
-            if (negNumber) {
-                result[--currentChar] = '-';
-            }
-            return new String(result, currentChar, resLengthInChars
-                    - currentChar);
-        }
-        int startPoint = currentChar + 1;
-        int endPoint = resLengthInChars;
-        StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
-        if (negNumber) {
-            result1.append('-');
-        }
-        if (endPoint - startPoint >= 1) {
-            result1.append(result[currentChar]);
-            result1.append('.');
-            result1.append(result, currentChar + 1, resLengthInChars
-                    - currentChar - 1);
-        } else {
-            result1.append(result, currentChar, resLengthInChars
-                    - currentChar);
-        }
-        result1.append('E');
-        if (exponent > 0) {
-            result1.append('+');
-        }
-        result1.append(Integer.toString(exponent));
-        return result1.toString();
-    }
-
-    /* can process only 32-bit numbers */
-    static String toDecimalScaledString(long value, int scale) {
-        int resLengthInChars;
-        int currentChar;
-        char[] result;
-        boolean negNumber = value < 0;
-        if(negNumber) {
-            value = -value;
-        }
-        if (value == 0) {
-            switch (scale) {
-                case 0: return "0";
-                case 1: return "0.0";
-                case 2: return "0.00";
-                case 3: return "0.000";
-                case 4: return "0.0000";
-                case 5: return "0.00000";
-                case 6: return "0.000000";
-                default:
-                    StringBuilder result1 = new StringBuilder();
-                    if (scale  < 0) {
-                        result1.append("0E+");
-                    } else {
-                        result1.append("0E");
-                    }
-                    result1.append( (scale == Integer.MIN_VALUE) ? "2147483648" : Integer.toString(-scale));
-                    return result1.toString();
-            }
-        }
-        // one 32-bit unsigned value may contains 10 decimal digits
-        resLengthInChars = 18;
-        // Explanation why +1+7:
-        // +1 - one char for sign if needed.
-        // +7 - For "special case 2" (see below) we have 7 free chars for
-        //  inserting necessary scaled digits.
-        result = new char[resLengthInChars+1];
-        //  Allocated [resLengthInChars+1] characters.
-        // a free latest character may be used for "special case 1" (see below)
-        currentChar = resLengthInChars;
-        long v = value;
-        do {
-            long prev = v;
-            v /= 10;
-            result[--currentChar] = (char) (0x0030 + (prev - v * 10));
-        } while (v != 0);
-
-        long exponent = (long)resLengthInChars - (long)currentChar - scale - 1L;
-        if (scale == 0) {
-            if (negNumber) {
-                result[--currentChar] = '-';
-            }
-            return new String(result, currentChar, resLengthInChars - currentChar);
-        }
-        if (scale > 0 && exponent >= -6) {
-            if (exponent >= 0) {
-                // special case 1
-                int insertPoint = currentChar + (int) exponent ;
-                for (int j=resLengthInChars-1; j>=insertPoint; j--) {
-                    result[j+1] = result[j];
-                }
-                result[++insertPoint]='.';
-                if (negNumber) {
-                    result[--currentChar] = '-';
-                }
-                return new String(result, currentChar, resLengthInChars - currentChar + 1);
-            }
-            // special case 2
-            for (int j = 2; j < -exponent + 1; j++) {
-                result[--currentChar] = '0';
-            }
-            result[--currentChar] = '.';
-            result[--currentChar] = '0';
-            if (negNumber) {
-                result[--currentChar] = '-';
-            }
-            return new String(result, currentChar, resLengthInChars - currentChar);
-        }
-        int startPoint = currentChar + 1;
-        int endPoint = resLengthInChars;
-        StringBuilder result1 = new StringBuilder(16 + endPoint - startPoint);
-        if (negNumber) {
-            result1.append('-');
-        }
-        if (endPoint - startPoint >= 1) {
-            result1.append(result[currentChar]);
-            result1.append('.');
-            result1.append(result,currentChar+1,resLengthInChars - currentChar-1);
-        } else {
-            result1.append(result,currentChar,resLengthInChars - currentChar);
-        }
-        result1.append('E');
-        if (exponent > 0) {
-            result1.append('+');
-        }
-        result1.append(Long.toString(exponent));
-        return result1.toString();
-    }
-
-    static long divideLongByBillion(long a) {
-        long quot;
-        long rem;
-
-        if (a >= 0) {
-            long bLong = 1000000000L;
-            quot = (a / bLong);
-            rem = (a % bLong);
-        } else {
-            /*
-             * Make the dividend positive shifting it right by 1 bit then get
-             * the quotient an remainder and correct them properly
-             */
-            long aPos = a >>> 1;
-            long bPos = 1000000000L >>> 1;
-            quot = aPos / bPos;
-            rem = aPos % bPos;
-            // double the remainder and add 1 if 'a' is odd
-            rem = (rem << 1) + (a & 1);
-        }
-        return ((rem << 32) | (quot & 0xFFFFFFFFL));
-    }
-
-    /** @see BigInteger#doubleValue() */
-    static double bigInteger2Double(BigInteger val) {
-        val.prepareJavaRepresentation();
-        // val.bitLength() < 64
-        if ((val.numberLength < 2)
-                || ((val.numberLength == 2) && (val.digits[1] > 0))) {
-            return val.longValue();
-        }
-        // val.bitLength() >= 33 * 32 > 1024
-        if (val.numberLength > 32) {
-            return ((val.sign > 0) ? Double.POSITIVE_INFINITY
-                    : Double.NEGATIVE_INFINITY);
-        }
-        int bitLen = val.abs().bitLength();
-        long exponent = bitLen - 1;
-        int delta = bitLen - 54;
-        // We need 54 top bits from this, the 53th bit is always 1 in lVal.
-        long lVal = val.abs().shiftRight(delta).longValue();
-        /*
-         * Take 53 bits from lVal to mantissa. The least significant bit is
-         * needed for rounding.
-         */
-        long mantissa = lVal & 0x1FFFFFFFFFFFFFL;
-        if (exponent == 1023) {
-            if (mantissa == 0X1FFFFFFFFFFFFFL) {
-                return ((val.sign > 0) ? Double.POSITIVE_INFINITY
-                        : Double.NEGATIVE_INFINITY);
-            }
-            if (mantissa == 0x1FFFFFFFFFFFFEL) {
-                return ((val.sign > 0) ? Double.MAX_VALUE : -Double.MAX_VALUE);
-            }
-        }
-        // Round the mantissa
-        if (((mantissa & 1) == 1)
-                && (((mantissa & 2) == 2) || BitLevel.nonZeroDroppedBits(delta,
-                        val.digits))) {
-            mantissa += 2;
-        }
-        mantissa >>= 1; // drop the rounding bit
-        long resSign = (val.sign < 0) ? 0x8000000000000000L : 0;
-        exponent = ((1023 + exponent) << 52) & 0x7FF0000000000000L;
-        long result = resSign | exponent | mantissa;
-        return Double.longBitsToDouble(result);
-    }
-}
diff --git a/luni/src/main/java/java/math/Division.java b/luni/src/main/java/java/math/Division.java
deleted file mode 100644
index d642783..0000000
--- a/luni/src/main/java/java/math/Division.java
+++ /dev/null
@@ -1,91 +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 java.math;
-
-/**
- * Static library that provides all operations related with division and modular
- * arithmetic to {@link BigInteger}. Some methods are provided in both mutable
- * and immutable way. There are several variants provided listed below:
- *
- * <ul type="circle">
- * <li> <b>Division</b>
- * <ul type="circle">
- * <li>{@link BigInteger} division and remainder by {@link BigInteger}.</li>
- * <li>{@link BigInteger} division and remainder by {@code int}.</li>
- * <li><i>gcd</i> between {@link BigInteger} numbers.</li>
- * </ul>
- * </li>
- * <li> <b>Modular arithmetic </b>
- * <ul type="circle">
- * <li>Modular exponentiation between {@link BigInteger} numbers.</li>
- * <li>Modular inverse of a {@link BigInteger} numbers.</li>
- * </ul>
- * </li>
- *</ul>
- */
-class Division {
-
-    /**
-     * Divides an array by an integer value. Implements the Knuth's division
-     * algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
-     *
-     * @return remainder
-     */
-    static int divideArrayByInt(int[] quotient, int[] dividend, final int dividendLength,
-            final int divisor) {
-
-        long rem = 0;
-        long bLong = divisor & 0xffffffffL;
-
-        for (int i = dividendLength - 1; i >= 0; i--) {
-            long temp = (rem << 32) | (dividend[i] & 0xffffffffL);
-            long quot;
-            if (temp >= 0) {
-                quot = (temp / bLong);
-                rem = (temp % bLong);
-            } else {
-                /*
-                 * make the dividend positive shifting it right by 1 bit then
-                 * get the quotient an remainder and correct them properly
-                 */
-                long aPos = temp >>> 1;
-                long bPos = divisor >>> 1;
-                quot = aPos / bPos;
-                rem = aPos % bPos;
-                // double the remainder and add 1 if a is odd
-                rem = (rem << 1) + (temp & 1);
-                if ((divisor & 1) != 0) {
-                    // the divisor is odd
-                    if (quot <= rem) {
-                        rem -= quot;
-                    } else {
-                        if (quot - rem <= bLong) {
-                            rem += bLong - quot;
-                            quot -= 1;
-                        } else {
-                            rem += (bLong << 1) - quot;
-                            quot -= 2;
-                        }
-                    }
-                }
-            }
-            quotient[i] = (int) (quot & 0xffffffffL);
-        }
-        return (int) rem;
-    }
-}
diff --git a/luni/src/main/java/java/math/Logical.java b/luni/src/main/java/java/math/Logical.java
deleted file mode 100644
index 9de0924..0000000
--- a/luni/src/main/java/java/math/Logical.java
+++ /dev/null
@@ -1,773 +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 java.math;
-
-/**
- * The library implements some logical operations over {@code BigInteger}. The
- * operations provided are listed below.
- * <ul type="circle">
- * <li>not</li>
- * <li>and</li>
- * <li>andNot</li>
- * <li>or</li>
- * <li>xor</li>
- * </ul>
- */
-class Logical {
-
-    /** Just to denote that this class can't be instantiated. */
-
-    private Logical() {}
-
-
-    /** @see BigInteger#not() */
-    static BigInteger not(BigInteger val) {
-        if (val.sign == 0) {
-            return BigInteger.MINUS_ONE;
-        }
-        if (val.equals(BigInteger.MINUS_ONE)) {
-            return BigInteger.ZERO;
-        }
-        int[] resDigits = new int[val.numberLength + 1];
-        int i;
-
-        if (val.sign > 0) {
-            // ~val = -val + 1
-            if (val.digits[val.numberLength - 1] != -1) {
-                for (i = 0; val.digits[i] == -1; i++) {
-                    ;
-                }
-            } else {
-                for (i = 0; (i < val.numberLength) && (val.digits[i] == -1); i++) {
-                    ;
-                }
-                if (i == val.numberLength) {
-                    resDigits[i] = 1;
-                    return new BigInteger(-val.sign, i + 1, resDigits);
-                }
-            }
-            // Here a carry 1 was generated
-        } else {// (val.sign < 0)
-            // ~val = -val - 1
-            for (i = 0; val.digits[i] == 0; i++) {
-                resDigits[i] = -1;
-            }
-            // Here a borrow -1 was generated
-        }
-        // Now, the carry/borrow can be absorbed
-        resDigits[i] = val.digits[i] + val.sign;
-        // Copying the remaining unchanged digit
-        for (i++; i < val.numberLength; i++) {
-            resDigits[i] = val.digits[i];
-        }
-        return new BigInteger(-val.sign, i, resDigits);
-    }
-
-    /** @see BigInteger#and(BigInteger) */
-    static BigInteger and(BigInteger val, BigInteger that) {
-        if (that.sign == 0 || val.sign == 0) {
-            return BigInteger.ZERO;
-        }
-        if (that.equals(BigInteger.MINUS_ONE)){
-            return val;
-        }
-        if (val.equals(BigInteger.MINUS_ONE)) {
-            return that;
-        }
-
-        if (val.sign > 0) {
-            if (that.sign > 0) {
-                return andPositive(val, that);
-            } else {
-                return andDiffSigns(val, that);
-            }
-        } else {
-            if (that.sign > 0) {
-                return andDiffSigns(that, val);
-            } else if (val.numberLength > that.numberLength) {
-                return andNegative(val, that);
-            } else {
-                return andNegative(that, val);
-            }
-        }
-    }
-
-    /** @return sign = 1, magnitude = val.magnitude & that.magnitude*/
-    static BigInteger andPositive(BigInteger val, BigInteger that) {
-        // PRE: both arguments are positive
-        int resLength = Math.min(val.numberLength, that.numberLength);
-        int i = Math.max(val.getFirstNonzeroDigit(), that.getFirstNonzeroDigit());
-
-        if (i >= resLength) {
-            return BigInteger.ZERO;
-        }
-
-        int[] resDigits = new int[resLength];
-        for ( ; i < resLength; i++) {
-            resDigits[i] = val.digits[i] & that.digits[i];
-        }
-
-        return new BigInteger(1, resLength, resDigits);
-    }
-
-    /** @return sign = positive.magnitude & magnitude = -negative.magnitude */
-    static BigInteger andDiffSigns(BigInteger positive, BigInteger negative) {
-        // PRE: positive is positive and negative is negative
-        int iPos = positive.getFirstNonzeroDigit();
-        int iNeg = negative.getFirstNonzeroDigit();
-
-        // Look if the trailing zeros of the negative will "blank" all
-        // the positive digits
-        if (iNeg >= positive.numberLength) {
-            return BigInteger.ZERO;
-        }
-        int resLength = positive.numberLength;
-        int[] resDigits = new int[resLength];
-
-        // Must start from max(iPos, iNeg)
-        int i = Math.max(iPos, iNeg);
-        if (i == iNeg) {
-            resDigits[i] = -negative.digits[i] & positive.digits[i];
-            i++;
-        }
-        int limit = Math.min(negative.numberLength, positive.numberLength);
-        for ( ; i < limit; i++) {
-            resDigits[i] = ~negative.digits[i] & positive.digits[i];
-        }
-        // if the negative was shorter must copy the remaining digits
-        // from positive
-        if (i >= negative.numberLength) {
-            for ( ; i < positive.numberLength; i++) {
-                resDigits[i] = positive.digits[i];
-            }
-        } // else positive ended and must "copy" virtual 0's, do nothing then
-
-        return new BigInteger(1, resLength, resDigits);
-    }
-
-    /** @return sign = -1, magnitude = -(-longer.magnitude & -shorter.magnitude)*/
-    static BigInteger andNegative(BigInteger longer, BigInteger shorter) {
-        // PRE: longer and shorter are negative
-        // PRE: longer has at least as many digits as shorter
-        int iLonger = longer.getFirstNonzeroDigit();
-        int iShorter = shorter.getFirstNonzeroDigit();
-
-        // Does shorter matter?
-        if (iLonger >= shorter.numberLength) {
-            return longer;
-        }
-
-        int resLength;
-        int[] resDigits;
-        int i = Math.max(iShorter, iLonger);
-        int digit;
-        if (iShorter > iLonger) {
-            digit = -shorter.digits[i] & ~longer.digits[i];
-        } else if (iShorter < iLonger) {
-            digit = ~shorter.digits[i] & -longer.digits[i];
-        } else {
-            digit = -shorter.digits[i] & -longer.digits[i];
-        }
-        if (digit == 0) {
-            for (i++; i < shorter.numberLength && (digit = ~(longer.digits[i] | shorter.digits[i])) == 0; i++)
-                ;  // digit = ~longer.digits[i] & ~shorter.digits[i]
-            if (digit == 0) {
-                // shorter has only the remaining virtual sign bits
-                for ( ; i < longer.numberLength && (digit = ~longer.digits[i]) == 0; i++)
-                    ;
-                if (digit == 0) {
-                    resLength = longer.numberLength + 1;
-                    resDigits = new int[resLength];
-                    resDigits[resLength - 1] = 1;
-
-                    return new BigInteger(-1, resLength, resDigits);
-                }
-            }
-        }
-        resLength = longer.numberLength;
-                resDigits = new int[resLength];
-        resDigits[i] = -digit;
-        for (i++; i < shorter.numberLength; i++){
-            // resDigits[i] = ~(~longer.digits[i] & ~shorter.digits[i];)
-            resDigits[i] = longer.digits[i] | shorter.digits[i];
-        }
-        // shorter has only the remaining virtual sign bits
-        for ( ; i < longer.numberLength; i++){
-            resDigits[i] = longer.digits[i];
-        }
-
-        return new BigInteger(-1, resLength, resDigits);
-    }
-
-    /** @see BigInteger#andNot(BigInteger) */
-    static BigInteger andNot(BigInteger val, BigInteger that) {
-        if (that.sign == 0 ) {
-            return val;
-        }
-        if (val.sign == 0) {
-            return BigInteger.ZERO;
-        }
-        if (val.equals(BigInteger.MINUS_ONE)) {
-            return that.not();
-        }
-        if (that.equals(BigInteger.MINUS_ONE)){
-            return BigInteger.ZERO;
-        }
-
-        //if val == that, return 0
-
-       if (val.sign > 0) {
-            if (that.sign > 0) {
-                return andNotPositive(val, that);
-            } else {
-                return andNotPositiveNegative(val, that);
-                    }
-                } else {
-            if (that.sign > 0) {
-                return andNotNegativePositive(val, that);
-            } else  {
-                return andNotNegative(val, that);
-            }
-        }
-    }
-
-    /** @return sign = 1, magnitude = val.magnitude & ~that.magnitude*/
-    static BigInteger andNotPositive(BigInteger val, BigInteger that) {
-        // PRE: both arguments are positive
-        int[] resDigits = new int[val.numberLength];
-
-        int limit = Math.min(val.numberLength, that.numberLength);
-        int i;
-        for (i = val.getFirstNonzeroDigit(); i < limit; i++) {
-            resDigits[i] = val.digits[i] & ~that.digits[i];
-        }
-        for ( ; i < val.numberLength; i++) {
-            resDigits[i] = val.digits[i];
-        }
-
-        return new BigInteger(1, val.numberLength, resDigits);
-    }
-
-    /** @return sign = 1, magnitude = positive.magnitude & ~(-negative.magnitude)*/
-    static BigInteger andNotPositiveNegative(BigInteger positive, BigInteger negative) {
-        // PRE: positive > 0 && negative < 0
-        int iNeg = negative.getFirstNonzeroDigit();
-        int iPos = positive.getFirstNonzeroDigit();
-
-        if (iNeg >= positive.numberLength) {
-            return positive;
-        }
-
-        int resLength = Math.min(positive.numberLength, negative.numberLength);
-        int[] resDigits = new int[resLength];
-
-        // Always start from first non zero of positive
-        int i = iPos;
-        for ( ; i < iNeg; i++) {
-            // resDigits[i] = positive.digits[i] & -1 (~0)
-            resDigits[i] = positive.digits[i];
-        }
-        if (i == iNeg) {
-            resDigits[i] = positive.digits[i] & (negative.digits[i] - 1);
-            i++;
-        }
-        for ( ; i < resLength; i++) {
-            // resDigits[i] = positive.digits[i] & ~(~negative.digits[i]);
-            resDigits[i] = positive.digits[i] & negative.digits[i];
-        }
-
-        return new BigInteger(1, resLength, resDigits);
-    }
-
-    /** @return sign = -1, magnitude = -(-negative.magnitude & ~positive.magnitude)*/
-    static BigInteger andNotNegativePositive(BigInteger negative, BigInteger positive) {
-        // PRE: negative < 0 && positive > 0
-        int resLength;
-        int[] resDigits;
-        int limit;
-        int digit;
-
-        int iNeg = negative.getFirstNonzeroDigit();
-        int iPos = positive.getFirstNonzeroDigit();
-
-        if (iNeg >= positive.numberLength) {
-            return negative;
-        }
-
-        resLength = Math.max(negative.numberLength, positive.numberLength);
-        int i = iNeg;
-        if (iPos > iNeg) {
-            resDigits = new int[resLength];
-            limit = Math.min(negative.numberLength, iPos);
-            for ( ; i < limit; i++) {
-                // 1st case:  resDigits [i] = -(-negative.digits[i] & (~0))
-                // otherwise: resDigits[i] = ~(~negative.digits[i] & ~0)  ;
-                resDigits[i] = negative.digits[i];
-            }
-            if (i == negative.numberLength) {
-                for (i = iPos; i < positive.numberLength; i++) {
-                    // resDigits[i] = ~(~positive.digits[i] & -1);
-                    resDigits[i] = positive.digits[i];
-                }
-            }
-        } else {
-            digit = -negative.digits[i] & ~positive.digits[i];
-            if (digit == 0) {
-                limit = Math.min(positive.numberLength, negative.numberLength);
-                for (i++; i < limit && (digit = ~(negative.digits[i] | positive.digits[i])) == 0; i++)
-                    ; // digit = ~negative.digits[i] & ~positive.digits[i]
-                if (digit == 0) {
-                    // the shorter has only the remaining virtual sign bits
-                    for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++)
-                        ; // digit = -1 & ~positive.digits[i]
-                    for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++)
-                        ; // digit = ~negative.digits[i] & ~0
-                    if (digit == 0) {
-                        resLength++;
-                        resDigits = new int[resLength];
-                        resDigits[resLength - 1] = 1;
-
-                        return new BigInteger(-1, resLength, resDigits);
-                    }
-                }
-            }
-                        resDigits = new int[resLength];
-            resDigits[i] = -digit;
-            i++;
-                    }
-
-        limit = Math.min(positive.numberLength, negative.numberLength);
-        for ( ; i < limit; i++) {
-            //resDigits[i] = ~(~negative.digits[i] & ~positive.digits[i]);
-            resDigits[i] = negative.digits[i] | positive.digits[i];
-        }
-        // Actually one of the next two cycles will be executed
-        for ( ; i < negative.numberLength; i++) {
-            resDigits[i] = negative.digits[i];
-                }
-        for ( ; i < positive.numberLength; i++) {
-            resDigits[i] = positive.digits[i];
-        }
-
-        return new BigInteger(-1, resLength, resDigits);
-    }
-
-    /** @return sign = 1, magnitude = -val.magnitude & ~(-that.magnitude)*/
-    static BigInteger andNotNegative(BigInteger val, BigInteger that) {
-        // PRE: val < 0 && that < 0
-        int iVal = val.getFirstNonzeroDigit();
-        int iThat = that.getFirstNonzeroDigit();
-
-        if (iVal >= that.numberLength) {
-            return BigInteger.ZERO;
-        }
-
-        int resLength = that.numberLength;
-        int[] resDigits = new int[resLength];
-        int limit;
-        int i = iVal;
-        if (iVal < iThat) {
-            // resDigits[i] = -val.digits[i] & -1;
-            resDigits[i] = -val.digits[i];
-            limit = Math.min(val.numberLength, iThat);
-            for (i++; i < limit; i++) {
-                // resDigits[i] = ~val.digits[i] & -1;
-                resDigits[i] = ~val.digits[i];
-            }
-            if (i == val.numberLength) {
-                for ( ; i < iThat; i++) {
-                    // resDigits[i] = -1 & -1;
-                    resDigits[i] = -1;
-                }
-                // resDigits[i] = -1 & ~-that.digits[i];
-                resDigits[i] = that.digits[i] - 1;
-        } else {
-                // resDigits[i] = ~val.digits[i] & ~-that.digits[i];
-                resDigits[i] = ~val.digits[i] & (that.digits[i] - 1);
-            }
-        } else if (iThat < iVal ) {
-            // resDigits[i] = -val.digits[i] & ~~that.digits[i];
-            resDigits[i] = -val.digits[i] & that.digits[i];
-        } else {
-            // resDigits[i] = -val.digits[i] & ~-that.digits[i];
-            resDigits[i] = -val.digits[i] & (that.digits[i] - 1);
-            }
-
-        limit = Math.min(val.numberLength, that.numberLength);
-        for (i++; i < limit; i++) {
-            // resDigits[i] = ~val.digits[i] & ~~that.digits[i];
-            resDigits[i] = ~val.digits[i] & that.digits[i];
-        }
-        for ( ; i < that.numberLength; i++) {
-            // resDigits[i] = -1 & ~~that.digits[i];
-            resDigits[i] = that.digits[i];
-        }
-
-        return new BigInteger(1, resLength, resDigits);
-    }
-
-    /** @see BigInteger#or(BigInteger) */
-    static BigInteger or(BigInteger val, BigInteger that) {
-        if (that.equals(BigInteger.MINUS_ONE) || val.equals(BigInteger.MINUS_ONE)) {
-            return BigInteger.MINUS_ONE;
-        }
-        if (that.sign == 0) {
-            return val;
-        }
-        if (val.sign == 0) {
-            return that;
-        }
-
-        if (val.sign > 0) {
-            if (that.sign > 0) {
-                if (val.numberLength > that.numberLength) {
-                    return orPositive(val, that);
-                } else {
-                    return orPositive(that, val);
-                }
-            } else {
-                return orDiffSigns(val, that);
-            }
-        } else {
-            if (that.sign > 0) {
-                return orDiffSigns(that, val);
-            } else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
-                return orNegative(that, val);
-            } else {
-                return orNegative(val, that);
-            }
-        }
-    }
-
-    /** @return sign = 1, magnitude = longer.magnitude | shorter.magnitude*/
-    static BigInteger orPositive(BigInteger longer, BigInteger shorter) {
-        // PRE: longer and shorter are positive;
-        // PRE: longer has at least as many digits as shorter
-        int resLength = longer.numberLength;
-        int[] resDigits = new int[resLength];
-
-        int i;
-        for (i = 0; i < shorter.numberLength; i++) {
-            resDigits[i] = longer.digits[i] | shorter.digits[i];
-        }
-        for ( ; i < resLength; i++) {
-            resDigits[i] = longer.digits[i];
-        }
-
-        return new BigInteger(1, resLength, resDigits);
-    }
-
-    /** @return sign = -1, magnitude = -(-val.magnitude | -that.magnitude) */
-    static BigInteger orNegative(BigInteger val, BigInteger that){
-        // PRE: val and that are negative;
-        // PRE: val has at least as many trailing zeros digits as that
-        int iThat = that.getFirstNonzeroDigit();
-        int iVal = val.getFirstNonzeroDigit();
-        int i;
-
-        if (iVal >= that.numberLength) {
-            return that;
-        }else if (iThat >= val.numberLength) {
-            return val;
-        }
-
-        int resLength = Math.min(val.numberLength, that.numberLength);
-        int[] resDigits = new int[resLength];
-
-        //Looking for the first non-zero digit of the result
-        if (iThat == iVal) {
-            resDigits[iVal] = -(-val.digits[iVal] | -that.digits[iVal]);
-            i = iVal;
-        } else {
-            for (i = iThat; i < iVal; i++) {
-                resDigits[i] = that.digits[i];
-            }
-            resDigits[i] = that.digits[i] & (val.digits[i] - 1);
-        }
-
-        for (i++; i < resLength; i++) {
-            resDigits[i] = val.digits[i] & that.digits[i];
-        }
-
-        return new BigInteger(-1, resLength, resDigits);
-    }
-
-    /** @return sign = -1, magnitude = -(positive.magnitude | -negative.magnitude) */
-    static BigInteger orDiffSigns(BigInteger positive, BigInteger negative){
-        // Jumping over the least significant zero bits
-        int iNeg = negative.getFirstNonzeroDigit();
-        int iPos = positive.getFirstNonzeroDigit();
-        int i;
-        int limit;
-
-        // Look if the trailing zeros of the positive will "copy" all
-        // the negative digits
-        if (iPos >= negative.numberLength) {
-            return negative;
-        }
-        int resLength = negative.numberLength;
-        int[] resDigits = new int[resLength];
-
-        if (iNeg < iPos ) {
-            // We know for sure that this will
-            // be the first non zero digit in the result
-            for (i = iNeg; i < iPos; i++) {
-            resDigits[i] = negative.digits[i];
-            }
-        } else if (iPos < iNeg) {
-            i = iPos;
-            resDigits[i] = -positive.digits[i];
-            limit = Math.min(positive.numberLength, iNeg);
-            for (i++; i < limit; i++ ) {
-                resDigits[i] = ~positive.digits[i];
-            }
-            if (i != positive.numberLength) {
-                resDigits[i] = ~(-negative.digits[i] | positive.digits[i]);
-            } else{
-                  for (; i<iNeg; i++) {
-                      resDigits[i] = -1;
-                  }
-                  // resDigits[i] = ~(-negative.digits[i] | 0);
-                  resDigits[i] = negative.digits[i] - 1;
-            }
-            i++;
-        } else {// iNeg == iPos
-            // Applying two complement to negative and to result
-            i = iPos;
-            resDigits[i] = -(-negative.digits[i] | positive.digits[i]);
-            i++;
-        }
-        limit = Math.min(negative.numberLength, positive.numberLength);
-        for (; i < limit; i++) {
-            // Applying two complement to negative and to result
-            // resDigits[i] = ~(~negative.digits[i] | positive.digits[i] );
-            resDigits[i] = negative.digits[i] & ~positive.digits[i];
-        }
-        for ( ; i < negative.numberLength; i++) {
-            resDigits[i] = negative.digits[i];
-        }
-
-        return new BigInteger(-1, resLength, resDigits);
-    }
-
-    /** @see BigInteger#xor(BigInteger) */
-    static BigInteger xor(BigInteger val, BigInteger that) {
-        if (that.sign == 0) {
-            return val;
-        }
-        if (val.sign == 0) {
-            return that;
-        }
-        if (that.equals(BigInteger.MINUS_ONE)) {
-            return val.not();
-        }
-        if (val.equals(BigInteger.MINUS_ONE)) {
-            return that.not();
-        }
-
-        if (val.sign > 0) {
-            if (that.sign > 0) {
-                if (val.numberLength > that.numberLength) {
-                    return xorPositive(val, that);
-                } else {
-                    return xorPositive(that, val);
-                }
-            } else {
-                return xorDiffSigns(val, that);
-            }
-        } else {
-            if (that.sign > 0) {
-                return xorDiffSigns(that, val);
-            } else if (that.getFirstNonzeroDigit() > val.getFirstNonzeroDigit()) {
-                return xorNegative(that, val);
-            } else {
-                return xorNegative(val, that);
-            }
-        }
-    }
-
-    /** @return sign = 0, magnitude = longer.magnitude | shorter.magnitude */
-    static BigInteger xorPositive(BigInteger longer, BigInteger shorter) {
-        // PRE: longer and shorter are positive;
-        // PRE: longer has at least as many digits as shorter
-        int resLength = longer.numberLength;
-        int[] resDigits = new int[resLength];
-        int i = Math.min(longer.getFirstNonzeroDigit(), shorter.getFirstNonzeroDigit());
-        for ( ; i < shorter.numberLength; i++) {
-            resDigits[i] = longer.digits[i] ^ shorter.digits[i];
-        }
-        for ( ; i < longer.numberLength; i++ ){
-            resDigits[i] = longer.digits[i];
-        }
-
-        return new BigInteger(1, resLength, resDigits);
-    }
-
-    /** @return sign = 0, magnitude = -val.magnitude ^ -that.magnitude */
-    static BigInteger xorNegative(BigInteger val, BigInteger that){
-        // PRE: val and that are negative
-        // PRE: val has at least as many trailing zero digits as that
-        int resLength = Math.max(val.numberLength, that.numberLength);
-        int[] resDigits = new int[resLength];
-        int iVal = val.getFirstNonzeroDigit();
-        int iThat = that.getFirstNonzeroDigit();
-        int i = iThat;
-        int limit;
-
-
-        if (iVal == iThat) {
-            resDigits[i] = -val.digits[i] ^ -that.digits[i];
-        } else {
-            resDigits[i] = -that.digits[i];
-            limit = Math.min(that.numberLength, iVal);
-            for (i++; i < limit; i++) {
-                resDigits[i] = ~that.digits[i];
-            }
-            // Remains digits in that?
-            if (i == that.numberLength) {
-                //Jumping over the remaining zero to the first non one
-                for ( ;i < iVal; i++) {
-                    //resDigits[i] = 0 ^ -1;
-                    resDigits[i] = -1;
-                }
-                //resDigits[i] = -val.digits[i] ^ -1;
-                resDigits[i] = val.digits[i] - 1;
-            } else {
-                resDigits[i] = -val.digits[i] ^ ~that.digits[i];
-            }
-        }
-
-        limit = Math.min(val.numberLength, that.numberLength);
-        //Perform ^ between that al val until that ends
-        for (i++; i < limit; i++) {
-            //resDigits[i] = ~val.digits[i] ^ ~that.digits[i];
-            resDigits[i] = val.digits[i] ^ that.digits[i];
-        }
-        //Perform ^ between val digits and -1 until val ends
-        for ( ; i < val.numberLength; i++) {
-            //resDigits[i] = ~val.digits[i] ^ -1  ;
-            resDigits[i] = val.digits[i] ;
-        }
-        for ( ; i < that.numberLength; i++) {
-            //resDigits[i] = -1 ^ ~that.digits[i] ;
-            resDigits[i] = that.digits[i];
-        }
-
-        return new BigInteger(1, resLength, resDigits);
-    }
-
-    /** @return sign = 1, magnitude = -(positive.magnitude ^ -negative.magnitude)*/
-    static BigInteger xorDiffSigns(BigInteger positive, BigInteger negative){
-        int resLength = Math.max(negative.numberLength, positive.numberLength);
-        int[] resDigits;
-        int iNeg = negative.getFirstNonzeroDigit();
-        int iPos = positive.getFirstNonzeroDigit();
-        int i;
-        int limit;
-
-        //The first
-        if (iNeg < iPos) {
-            resDigits = new int[resLength];
-            i = iNeg;
-            //resDigits[i] = -(-negative.digits[i]);
-            resDigits[i] = negative.digits[i];
-            limit = Math.min(negative.numberLength, iPos);
-            //Skip the positive digits while they are zeros
-            for (i++; i < limit; i++) {
-                //resDigits[i] = ~(~negative.digits[i]);
-                resDigits[i] = negative.digits[i];
-            }
-            //if the negative has no more elements, must fill the
-            //result with the remaining digits of the positive
-            if (i == negative.numberLength) {
-                for ( ; i < positive.numberLength; i++) {
-                    //resDigits[i] = ~(positive.digits[i] ^ -1) -> ~(~positive.digits[i])
-                    resDigits[i] = positive.digits[i];
-                }
-            }
-        } else if (iPos < iNeg) {
-            resDigits = new int[resLength];
-            i = iPos;
-            //Applying two complement to the first non-zero digit of the result
-            resDigits[i] = -positive.digits[i];
-            limit = Math.min(positive.numberLength, iNeg);
-            for (i++; i < limit; i++) {
-                //Continue applying two complement the result
-                resDigits[i] = ~positive.digits[i];
-            }
-            //When the first non-zero digit of the negative is reached, must apply
-            //two complement (arithmetic negation) to it, and then operate
-            if (i == iNeg) {
-                resDigits[i] = ~(positive.digits[i] ^ -negative.digits[i]);
-                i++;
-            } else {
-                //if the positive has no more elements must fill the remaining digits with
-                //the negative ones
-                for ( ; i < iNeg; i++) {
-                    // resDigits[i] = ~(0 ^ 0)
-                    resDigits[i] = -1;
-                }
-                for ( ; i < negative.numberLength; i++) {
-                    //resDigits[i] = ~(~negative.digits[i] ^ 0)
-                    resDigits[i] = negative.digits[i];
-                }
-            }
-                } else {
-            //The first non-zero digit of the positive and negative are the same
-            i = iNeg;
-            int digit = positive.digits[i] ^ -negative.digits[i];
-            if (digit == 0) {
-                limit = Math.min(positive.numberLength, negative.numberLength);
-                for (i++; i < limit && (digit = positive.digits[i] ^ ~negative.digits[i]) == 0; i++)
-                    ;
-                if (digit == 0) {
-                    // shorter has only the remaining virtual sign bits
-                    for ( ; i < positive.numberLength && (digit = ~positive.digits[i]) == 0; i++)
-                        ;
-                    for ( ; i < negative.numberLength && (digit = ~negative.digits[i]) == 0; i++)
-                        ;
-                    if (digit == 0) {
-                        resLength = resLength + 1;
-                        resDigits = new int[resLength];
-                        resDigits[resLength - 1] = 1;
-
-                        return new BigInteger(-1, resLength, resDigits);
-                }
-            }
-        }
-            resDigits = new int[resLength];
-            resDigits[i] = -digit;
-            i++;
-        }
-
-        limit = Math.min(negative.numberLength, positive.numberLength);
-        for ( ; i < limit; i++) {
-            resDigits[i] = ~(~negative.digits[i] ^ positive.digits[i]);
-        }
-        for ( ; i < positive.numberLength; i++) {
-            // resDigits[i] = ~(positive.digits[i] ^ -1)
-            resDigits[i] = positive.digits[i];
-        }
-        for ( ; i < negative.numberLength; i++) {
-            // resDigits[i] = ~(0 ^ ~negative.digits[i])
-            resDigits[i] = negative.digits[i];
-        }
-
-        return new BigInteger(-1, resLength, resDigits);
-    }
-}
diff --git a/luni/src/main/java/java/math/MathContext.java b/luni/src/main/java/java/math/MathContext.java
deleted file mode 100644
index 6f3f1ed..0000000
--- a/luni/src/main/java/java/math/MathContext.java
+++ /dev/null
@@ -1,249 +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 java.math;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.Serializable;
-import java.io.StreamCorruptedException;
-
-/**
- * Immutable objects describing settings such as rounding mode and digit
- * precision for the numerical operations provided by class {@link BigDecimal}.
- */
-public final class MathContext implements Serializable {
-    private static final long serialVersionUID = 5579720004786848255L;
-
-    /**
-     * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> quadruple
-     * decimal precision format: 34 digit precision and
-     * {@link RoundingMode#HALF_EVEN} rounding.
-     */
-    public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN);
-
-    /**
-     * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single decimal
-     * precision format: 7 digit precision and {@link RoundingMode#HALF_EVEN}
-     * rounding.
-     */
-    public static final MathContext DECIMAL32 = new MathContext(7, RoundingMode.HALF_EVEN);
-
-    /**
-     * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double decimal
-     * precision format: 16 digit precision and {@link RoundingMode#HALF_EVEN}
-     * rounding.
-     */
-    public static final MathContext DECIMAL64 = new MathContext(16, RoundingMode.HALF_EVEN);
-
-    /**
-     * A {@code MathContext} for unlimited precision with
-     * {@link RoundingMode#HALF_UP} rounding.
-     */
-    public static final MathContext UNLIMITED = new MathContext(0, RoundingMode.HALF_UP);
-
-    /**
-     * The number of digits to be used for an operation; results are rounded to
-     * this precision.
-     */
-    private final int precision;
-
-    /**
-     * A {@code RoundingMode} object which specifies the algorithm to be used
-     * for rounding.
-     */
-    private final RoundingMode roundingMode;
-
-    /**
-     * Constructs a new {@code MathContext} with the specified precision and
-     * with the rounding mode {@link RoundingMode#HALF_UP HALF_UP}. If the
-     * precision passed is zero, then this implies that the computations have to
-     * be performed exact, the rounding mode in this case is irrelevant.
-     *
-     * @param precision
-     *            the precision for the new {@code MathContext}.
-     * @throws IllegalArgumentException
-     *             if {@code precision < 0}.
-     */
-    public MathContext(int precision) {
-        this(precision, RoundingMode.HALF_UP);
-    }
-
-    /**
-     * Constructs a new {@code MathContext} with the specified precision and
-     * with the specified rounding mode. If the precision passed is zero, then
-     * this implies that the computations have to be performed exact, the
-     * rounding mode in this case is irrelevant.
-     *
-     * @param precision
-     *            the precision for the new {@code MathContext}.
-     * @param roundingMode
-     *            the rounding mode for the new {@code MathContext}.
-     * @throws IllegalArgumentException
-     *             if {@code precision < 0}.
-     * @throws NullPointerException
-     *             if {@code roundingMode} is {@code null}.
-     */
-    public MathContext(int precision, RoundingMode roundingMode) {
-        this.precision = precision;
-        this.roundingMode = roundingMode;
-        checkValid();
-    }
-
-    /**
-     * Constructs a new {@code MathContext} from a string. The string has to
-     * specify the precision and the rounding mode to be used and has to follow
-     * the following syntax: "precision=&lt;precision&gt; roundingMode=&lt;roundingMode&gt;"
-     * This is the same form as the one returned by the {@link #toString}
-     * method.
-     *
-     * @throws IllegalArgumentException
-     *             if the string is not in the correct format or if the
-     *             precision specified is < 0.
-     */
-    public MathContext(String s) {
-        int precisionLength = "precision=".length();
-        int roundingModeLength = "roundingMode=".length();
-
-        int spaceIndex;
-        if (!s.startsWith("precision=") || (spaceIndex = s.indexOf(' ', precisionLength)) == -1) {
-            throw invalidMathContext("Missing precision", s);
-        }
-        String precisionString = s.substring(precisionLength, spaceIndex);
-        try {
-            this.precision = Integer.parseInt(precisionString);
-        } catch (NumberFormatException nfe) {
-            throw invalidMathContext("Bad precision", s);
-        }
-
-        int roundingModeStart = spaceIndex + 1;
-        if (!s.regionMatches(roundingModeStart, "roundingMode=", 0, roundingModeLength)) {
-            throw invalidMathContext("Missing rounding mode", s);
-        }
-        roundingModeStart += roundingModeLength;
-        this.roundingMode = RoundingMode.valueOf(s.substring(roundingModeStart));
-
-        checkValid();
-    }
-
-    private IllegalArgumentException invalidMathContext(String reason, String s) {
-        throw new IllegalArgumentException(reason + ": " + s);
-    }
-
-    private void checkValid() {
-        if (precision < 0) {
-            throw new IllegalArgumentException("Negative precision: " + precision);
-        }
-        if (roundingMode == null) {
-            throw new NullPointerException("roundingMode == null");
-        }
-    }
-
-    /**
-     * Returns the precision. The precision is the number of digits used for an
-     * operation. Results are rounded to this precision. The precision is
-     * guaranteed to be non negative. If the precision is zero, then the
-     * computations have to be performed exact, results are not rounded in this
-     * case.
-     *
-     * @return the precision.
-     */
-    public int getPrecision() {
-        return precision;
-    }
-
-    /**
-     * Returns the rounding mode. The rounding mode is the strategy to be used
-     * to round results.
-     * <p>
-     * The rounding mode is one of
-     * {@link RoundingMode#UP},
-     * {@link RoundingMode#DOWN},
-     * {@link RoundingMode#CEILING},
-     * {@link RoundingMode#FLOOR},
-     * {@link RoundingMode#HALF_UP},
-     * {@link RoundingMode#HALF_DOWN},
-     * {@link RoundingMode#HALF_EVEN}, or
-     * {@link RoundingMode#UNNECESSARY}.
-     *
-     * @return the rounding mode.
-     */
-    public RoundingMode getRoundingMode() {
-        return roundingMode;
-    }
-
-    /**
-     * Returns true if x is a {@code MathContext} with the same precision
-     * setting and the same rounding mode as this {@code MathContext} instance.
-     *
-     * @param x
-     *            object to be compared.
-     * @return {@code true} if this {@code MathContext} instance is equal to the
-     *         {@code x} argument; {@code false} otherwise.
-     */
-    @Override
-    public boolean equals(Object x) {
-        return ((x instanceof MathContext)
-                && (((MathContext) x).getPrecision() == precision) && (((MathContext) x)
-                .getRoundingMode() == roundingMode));
-    }
-
-    /**
-     * Returns the hash code for this {@code MathContext} instance.
-     *
-     * @return the hash code for this {@code MathContext}.
-     */
-    @Override
-    public int hashCode() {
-        // Make place for the necessary bits to represent 8 rounding modes
-        return ((precision << 3) | roundingMode.ordinal());
-    }
-
-    /**
-     * Returns the string representation for this {@code MathContext} instance.
-     * The string has the form
-     * {@code
-     * "precision=<precision> roundingMode=<roundingMode>"
-     * } where {@code <precision>} is an integer describing the number
-     * of digits used for operations and {@code <roundingMode>} is the
-     * string representation of the rounding mode.
-     *
-     * @return a string representation for this {@code MathContext} instance
-     */
-    @Override
-    public String toString() {
-        return "precision=" + precision + " roundingMode=" + roundingMode;
-    }
-
-    /**
-     * Makes checks upon deserialization of a {@code MathContext} instance.
-     * Checks whether {@code precision >= 0} and {@code roundingMode != null}
-     *
-     * @throws StreamCorruptedException
-     *             if {@code precision < 0}
-     * @throws StreamCorruptedException
-     *             if {@code roundingMode == null}
-     */
-    private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
-        s.defaultReadObject();
-        try {
-            checkValid();
-        } catch (Exception ex) {
-            throw new StreamCorruptedException(ex.getMessage());
-        }
-    }
-}
diff --git a/luni/src/main/java/java/math/Multiplication.java b/luni/src/main/java/java/math/Multiplication.java
deleted file mode 100644
index 2a4285b..0000000
--- a/luni/src/main/java/java/math/Multiplication.java
+++ /dev/null
@@ -1,187 +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 java.math;
-
-/**
- * Static library that provides all multiplication of {@link BigInteger} methods.
- */
-class Multiplication {
-
-    /** Just to denote that this class can't be instantiated. */
-    private Multiplication() {}
-
-    // BEGIN Android-removed
-    // /**
-    //  * Break point in digits (number of {@code int} elements)
-    //  * between Karatsuba and Pencil and Paper multiply.
-    //  */
-    // static final int whenUseKaratsuba = 63; // an heuristic value
-    // END Android-removed
-
-    /**
-     * An array with powers of ten that fit in the type {@code int}.
-     * ({@code 10^0,10^1,...,10^9})
-     */
-    static final int[] tenPows = {
-        1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
-    };
-
-    /**
-     * An array with powers of five that fit in the type {@code int}.
-     * ({@code 5^0,5^1,...,5^13})
-     */
-    static final int[] fivePows = {
-        1, 5, 25, 125, 625, 3125, 15625, 78125, 390625,
-        1953125, 9765625, 48828125, 244140625, 1220703125
-    };
-
-    /**
-     * An array with the first powers of ten in {@code BigInteger} version.
-     * ({@code 10^0,10^1,...,10^31})
-     */
-    static final BigInteger[] bigTenPows = new BigInteger[32];
-
-    /**
-     * An array with the first powers of five in {@code BigInteger} version.
-     * ({@code 5^0,5^1,...,5^31})
-     */
-    static final BigInteger bigFivePows[] = new BigInteger[32];
-
-
-
-    static {
-        int i;
-        long fivePow = 1L;
-
-        for (i = 0; i <= 18; i++) {
-            bigFivePows[i] = BigInteger.valueOf(fivePow);
-            bigTenPows[i] = BigInteger.valueOf(fivePow << i);
-            fivePow *= 5;
-        }
-        for (; i < bigTenPows.length; i++) {
-            bigFivePows[i] = bigFivePows[i - 1].multiply(bigFivePows[1]);
-            bigTenPows[i] = bigTenPows[i - 1].multiply(BigInteger.TEN);
-        }
-    }
-
-    // BEGIN android-note: multiply has been removed in favor of using OpenSSL BIGNUM
-    // END android-note
-
-    /**
-     * Multiplies a number by a positive integer.
-     * @param val an arbitrary {@code BigInteger}
-     * @param factor a positive {@code int} number
-     * @return {@code val * factor}
-     */
-    static BigInteger multiplyByPositiveInt(BigInteger val, int factor) {
-        BigInt bi = val.getBigInt().copy();
-        bi.multiplyByPositiveInt(factor);
-        return new BigInteger(bi);
-    }
-
-    /**
-     * Multiplies a number by a power of ten.
-     * This method is used in {@code BigDecimal} class.
-     * @param val the number to be multiplied
-     * @param exp a positive {@code long} exponent
-     * @return {@code val * 10<sup>exp</sup>}
-     */
-    static BigInteger multiplyByTenPow(BigInteger val, long exp) {
-        // PRE: exp >= 0
-        return ((exp < tenPows.length)
-        ? multiplyByPositiveInt(val, tenPows[(int)exp])
-        : val.multiply(powerOf10(exp)));
-    }
-
-    /**
-     * It calculates a power of ten, which exponent could be out of 32-bit range.
-     * Note that internally this method will be used in the worst case with
-     * an exponent equals to: {@code Integer.MAX_VALUE - Integer.MIN_VALUE}.
-     * @param exp the exponent of power of ten, it must be positive.
-     * @return a {@code BigInteger} with value {@code 10<sup>exp</sup>}.
-     */
-    static BigInteger powerOf10(long exp) {
-        // PRE: exp >= 0
-        int intExp = (int)exp;
-        // "SMALL POWERS"
-        if (exp < bigTenPows.length) {
-            // The largest power that fit in 'long' type
-            return bigTenPows[intExp];
-        } else if (exp <= 50) {
-            // To calculate:    10^exp
-            return BigInteger.TEN.pow(intExp);
-        }
-
-        BigInteger res = null;
-        try {
-            // "LARGE POWERS"
-            if (exp <= Integer.MAX_VALUE) {
-                // To calculate:    5^exp * 2^exp
-                res = bigFivePows[1].pow(intExp).shiftLeft(intExp);
-            } else {
-                /*
-                 * "HUGE POWERS"
-                 *
-                 * This branch probably won't be executed since the power of ten is too
-                 * big.
-                 */
-                // To calculate:    5^exp
-                BigInteger powerOfFive = bigFivePows[1].pow(Integer.MAX_VALUE);
-                res = powerOfFive;
-                long longExp = exp - Integer.MAX_VALUE;
-
-                intExp = (int) (exp % Integer.MAX_VALUE);
-                while (longExp > Integer.MAX_VALUE) {
-                    res = res.multiply(powerOfFive);
-                    longExp -= Integer.MAX_VALUE;
-                }
-                res = res.multiply(bigFivePows[1].pow(intExp));
-                // To calculate:    5^exp << exp
-                res = res.shiftLeft(Integer.MAX_VALUE);
-                longExp = exp - Integer.MAX_VALUE;
-                while (longExp > Integer.MAX_VALUE) {
-                    res = res.shiftLeft(Integer.MAX_VALUE);
-                    longExp -= Integer.MAX_VALUE;
-                }
-                res = res.shiftLeft(intExp);
-            }
-        } catch (OutOfMemoryError error) {
-            throw new ArithmeticException(error.getMessage());
-        }
-
-        return res;
-    }
-
-    /**
-     * Multiplies a number by a power of five.
-     * This method is used in {@code BigDecimal} class.
-     * @param val the number to be multiplied
-     * @param exp a positive {@code int} exponent
-     * @return {@code val * 5<sup>exp</sup>}
-     */
-    static BigInteger multiplyByFivePow(BigInteger val, int exp) {
-        // PRE: exp >= 0
-        if (exp < fivePows.length) {
-            return multiplyByPositiveInt(val, fivePows[exp]);
-        } else if (exp < bigFivePows.length) {
-            return val.multiply(bigFivePows[exp]);
-        } else {// Large powers of five
-            return val.multiply(bigFivePows[1].pow(exp));
-        }
-    }
-}
diff --git a/luni/src/main/java/java/math/NativeBN.java b/luni/src/main/java/java/math/NativeBN.java
deleted file mode 100644
index ab9b2e0..0000000
--- a/luni/src/main/java/java/math/NativeBN.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package java.math;
-
-final class NativeBN {
-
-    public static native long BN_new();
-    // BIGNUM *BN_new(void);
-
-    public static native void BN_free(long a);
-    // void BN_free(BIGNUM *a);
-
-    public static native int BN_cmp(long a, long b);
-    // int BN_cmp(const BIGNUM *a, const BIGNUM *b);
-
-    public static native void BN_copy(long to, long from);
-    // BIGNUM *BN_copy(BIGNUM *to, const BIGNUM *from);
-
-    public static native void putLongInt(long a, long dw);
-    public static native void putULongInt(long a, long dw, boolean neg);
-
-    public static native int BN_dec2bn(long a, String str);
-    // int BN_dec2bn(BIGNUM **a, const char *str);
-
-    public static native int BN_hex2bn(long a, String str);
-    // int BN_hex2bn(BIGNUM **a, const char *str);
-
-    public static native void BN_bin2bn(byte[] s, int len, boolean neg, long ret);
-    // BIGNUM * BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
-    // BN-Docu: s is taken as unsigned big endian;
-    // Additional parameter: neg.
-
-    public static native void litEndInts2bn(int[] ints, int len, boolean neg, long ret);
-
-    public static native void twosComp2bn(byte[] s, int len, long ret);
-
-
-    public static native long longInt(long a);
-    // unsigned long BN_get_word(BIGNUM *a);
-
-    public static native String BN_bn2dec(long a);
-    // char * BN_bn2dec(const BIGNUM *a);
-
-    public static native String BN_bn2hex(long a);
-    // char * BN_bn2hex(const BIGNUM *a);
-
-    public static native byte[] BN_bn2bin(long a);
-    // Returns result byte[] AND NOT length.
-    // int BN_bn2bin(const BIGNUM *a, unsigned char *to);
-
-    public static native int[] bn2litEndInts(long a);
-
-    public static native int sign(long a);
-    // Returns -1, 0, 1 AND NOT boolean.
-    // #define BN_is_negative(a) ((a)->neg != 0)
-
-    public static native void BN_set_negative(long b, int n);
-    // void BN_set_negative(BIGNUM *b, int n);
-
-    public static native int bitLength(long a);
-
-    public static native boolean BN_is_bit_set(long a, int n);
-    // int BN_is_bit_set(const BIGNUM *a, int n);
-
-    public static native void BN_shift(long r, long a, int n);
-    // int BN_shift(BIGNUM *r, const BIGNUM *a, int n);
-
-    public static native void BN_add_word(long a, int w);
-    // ATTENTION: w is treated as unsigned.
-    // int BN_add_word(BIGNUM *a, BN_ULONG w);
-
-    public static native void BN_mul_word(long a, int w);
-    // ATTENTION: w is treated as unsigned.
-    // int BN_mul_word(BIGNUM *a, BN_ULONG w);
-
-    public static native int BN_mod_word(long a, int w);
-    // ATTENTION: w is treated as unsigned.
-    // BN_ULONG BN_mod_word(BIGNUM *a, BN_ULONG w);
-
-    public static native void BN_add(long r, long a, long b);
-    // int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
-
-    public static native void BN_sub(long r, long a, long b);
-    // int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
-
-    public static native void BN_gcd(long r, long a, long b);
-    // int BN_gcd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
-
-    public static native void BN_mul(long r, long a, long b);
-    // int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
-
-    public static native void BN_exp(long r, long a, long p);
-    // int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx);
-
-    public static native void BN_div(long dv, long rem, long m, long d);
-    // int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx);
-
-    public static native void BN_nnmod(long r, long a, long m);
-    // int BN_nnmod(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
-
-    public static native void BN_mod_exp(long r, long a, long p, long m);
-    // int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx);
-
-    public static native void BN_mod_inverse(long ret, long a, long n);
-    // BIGNUM * BN_mod_inverse(BIGNUM *ret, const BIGNUM *a, const BIGNUM *n, BN_CTX *ctx);
-
-
-    public static native void BN_generate_prime_ex(long ret, int bits, boolean safe,
-                                                   long add, long rem);
-    // int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe,
-    //         const BIGNUM *add, const BIGNUM *rem, BN_GENCB *cb);
-
-    public static native boolean BN_primality_test(long candidate, int checks,
-                                                   boolean do_trial_division);
-    // int BN_primality_test(int *is_probably_prime, const BIGNUM *candidate, int checks,
-    //                       BN_CTX *ctx, int do_trial_division, BN_GENCB *cb);
-    // Returns *is_probably_prime on success and throws an exception on error.
-
-    public static native long getNativeFinalizer();
-    // &BN_free
-
-}
diff --git a/luni/src/main/java/java/math/Primality.java b/luni/src/main/java/java/math/Primality.java
deleted file mode 100644
index eacc893..0000000
--- a/luni/src/main/java/java/math/Primality.java
+++ /dev/null
@@ -1,145 +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 java.math;
-
-import java.util.Arrays;
-
-/**
- * Provides primality probabilistic methods.
- */
-class Primality {
-
-    /** Just to denote that this class can't be instantiated. */
-    private Primality() {}
-
-    /** All prime numbers with bit length lesser than 10 bits. */
-    private static final int[] primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
-            31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
-            103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
-            173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239,
-            241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
-            317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
-            401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467,
-            479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569,
-            571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643,
-            647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
-            739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823,
-            827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
-            919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009,
-            1013, 1019, 1021 };
-
-    /** All {@code BigInteger} prime numbers with bit length lesser than 10 bits. */
-    private static final BigInteger BIprimes[] = new BigInteger[primes.length];
-
-//    /**
-//     * It encodes how many iterations of Miller-Rabin test are need to get an
-//     * error bound not greater than {@code 2<sup>(-100)</sup>}. For example:
-//     * for a {@code 1000}-bit number we need {@code 4} iterations, since
-//     * {@code BITS[3] < 1000 <= BITS[4]}.
-//     */
-//    private static final int[] BITS = { 0, 0, 1854, 1233, 927, 747, 627, 543,
-//            480, 431, 393, 361, 335, 314, 295, 279, 265, 253, 242, 232, 223,
-//            216, 181, 169, 158, 150, 145, 140, 136, 132, 127, 123, 119, 114,
-//            110, 105, 101, 96, 92, 87, 83, 78, 73, 69, 64, 59, 54, 49, 44, 38,
-//            32, 26, 1 };
-//
-//    /**
-//     * It encodes how many i-bit primes there are in the table for
-//     * {@code i=2,...,10}. For example {@code offsetPrimes[6]} says that from
-//     * index {@code 11} exists {@code 7} consecutive {@code 6}-bit prime
-//     * numbers in the array.
-//     */
-//    private static final int[][] offsetPrimes = { null, null, { 0, 2 },
-//            { 2, 2 }, { 4, 2 }, { 6, 5 }, { 11, 7 }, { 18, 13 }, { 31, 23 },
-//            { 54, 43 }, { 97, 75 } };
-
-    static {// To initialize the dual table of BigInteger primes
-        for (int i = 0; i < primes.length; i++) {
-            BIprimes[i] = BigInteger.valueOf(primes[i]);
-        }
-    }
-
-    /**
-     * It uses the sieve of Eratosthenes to discard several composite numbers in
-     * some appropriate range (at the moment {@code [this, this + 1024]}). After
-     * this process it applies the Miller-Rabin test to the numbers that were
-     * not discarded in the sieve.
-     *
-     * @see BigInteger#nextProbablePrime()
-     */
-    static BigInteger nextProbablePrime(BigInteger n) {
-        // PRE: n >= 0
-        int i, j;
-//        int certainty;
-        int gapSize = 1024; // for searching of the next probable prime number
-        int[] modules = new int[primes.length];
-        boolean isDivisible[] = new boolean[gapSize];
-        BigInt ni = n.getBigInt();
-        // If n < "last prime of table" searches next prime in the table
-        if (ni.bitLength() <= 10) {
-            int l = (int)ni.longInt();
-            if (l < primes[primes.length - 1]) {
-                for (i = 0; l >= primes[i]; i++) {}
-                return BIprimes[i];
-            }
-        }
-
-        BigInt startPoint = ni.copy();
-        BigInt probPrime = new BigInt();
-
-        // Fix startPoint to "next odd number":
-        startPoint.addPositiveInt(BigInt.remainderByPositiveInt(ni, 2) + 1);
-
-//        // To set the improved certainty of Miller-Rabin
-//        j = startPoint.bitLength();
-//        for (certainty = 2; j < BITS[certainty]; certainty++) {
-//            ;
-//        }
-
-        // To calculate modules: N mod p1, N mod p2, ... for first primes.
-        for (i = 0; i < primes.length; i++) {
-            modules[i] = BigInt.remainderByPositiveInt(startPoint, primes[i]) - gapSize;
-        }
-        while (true) {
-            // At this point, all numbers in the gap are initialized as
-            // probably primes
-            Arrays.fill(isDivisible, false);
-            // To discard multiples of first primes
-            for (i = 0; i < primes.length; i++) {
-                modules[i] = (modules[i] + gapSize) % primes[i];
-                j = (modules[i] == 0) ? 0 : (primes[i] - modules[i]);
-                for (; j < gapSize; j += primes[i]) {
-                    isDivisible[j] = true;
-                }
-            }
-            // To execute Miller-Rabin for non-divisible numbers by all first
-            // primes
-            for (j = 0; j < gapSize; j++) {
-                if (!isDivisible[j]) {
-                    probPrime.putCopy(startPoint);
-                    probPrime.addPositiveInt(j);
-                    if (probPrime.isPrime(100)) {
-                        return new BigInteger(probPrime);
-                    }
-                }
-            }
-            startPoint.addPositiveInt(gapSize);
-        }
-    }
-
-}
diff --git a/luni/src/main/java/java/math/RoundingMode.java b/luni/src/main/java/java/math/RoundingMode.java
deleted file mode 100644
index f4c181e..0000000
--- a/luni/src/main/java/java/math/RoundingMode.java
+++ /dev/null
@@ -1,122 +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 java.math;
-
-/**
- * Specifies the rounding behavior for operations whose results cannot be
- * represented exactly.
- */
-public enum RoundingMode {
-
-    /**
-     * Rounding mode where positive values are rounded towards positive infinity
-     * and negative values towards negative infinity.
-     * <br>
-     * Rule: {@code x.round().abs() >= x.abs()}
-     */
-    UP(BigDecimal.ROUND_UP),
-
-    /**
-     * Rounding mode where the values are rounded towards zero.
-     * <br>
-     * Rule: {@code x.round().abs() <= x.abs()}
-     */
-    DOWN(BigDecimal.ROUND_DOWN),
-
-    /**
-     * Rounding mode to round towards positive infinity. For positive values
-     * this rounding mode behaves as {@link #UP}, for negative values as
-     * {@link #DOWN}.
-     * <br>
-     * Rule: {@code x.round() >= x}
-     */
-    CEILING(BigDecimal.ROUND_CEILING),
-
-    /**
-     * Rounding mode to round towards negative infinity. For positive values
-     * this rounding mode behaves as {@link #DOWN}, for negative values as
-     * {@link #UP}.
-     * <br>
-     * Rule: {@code x.round() <= x}
-     */
-    FLOOR(BigDecimal.ROUND_FLOOR),
-
-    /**
-     * Rounding mode where values are rounded towards the nearest neighbor. Ties
-     * are broken by rounding up.
-     */
-    HALF_UP(BigDecimal.ROUND_HALF_UP),
-
-    /**
-     * Rounding mode where values are rounded towards the nearest neighbor. Ties
-     * are broken by rounding down.
-     */
-    HALF_DOWN(BigDecimal.ROUND_HALF_DOWN),
-
-    /**
-     * Rounding mode where values are rounded towards the nearest neighbor. Ties
-     * are broken by rounding to the even neighbor.
-     */
-    HALF_EVEN(BigDecimal.ROUND_HALF_EVEN),
-
-    /**
-     * Rounding mode where the rounding operations throws an ArithmeticException
-     * for the case that rounding is necessary, i.e. for the case that the value
-     * cannot be represented exactly.
-     */
-    UNNECESSARY(BigDecimal.ROUND_UNNECESSARY);
-
-    /** The old constant of <code>BigDecimal</code>. */
-    private final int bigDecimalRM;
-
-    /** It sets the old constant. */
-    RoundingMode(int rm) {
-        bigDecimalRM = rm;
-    }
-
-    /**
-     * Converts rounding mode constants from class {@code BigDecimal} into
-     * {@code RoundingMode} values.
-     *
-     * @param mode
-     *            rounding mode constant as defined in class {@code BigDecimal}
-     * @return corresponding rounding mode object
-     */
-    public static RoundingMode valueOf(int mode) {
-        switch (mode) {
-            case BigDecimal.ROUND_CEILING:
-                return CEILING;
-            case BigDecimal.ROUND_DOWN:
-                return DOWN;
-            case BigDecimal.ROUND_FLOOR:
-                return FLOOR;
-            case BigDecimal.ROUND_HALF_DOWN:
-                return HALF_DOWN;
-            case BigDecimal.ROUND_HALF_EVEN:
-                return HALF_EVEN;
-            case BigDecimal.ROUND_HALF_UP:
-                return HALF_UP;
-            case BigDecimal.ROUND_UNNECESSARY:
-                return UNNECESSARY;
-            case BigDecimal.ROUND_UP:
-                return UP;
-            default:
-                throw new IllegalArgumentException("Invalid rounding mode");
-        }
-    }
-}
diff --git a/luni/src/main/java/java/nio/NIOAccess.java b/luni/src/main/java/java/nio/NIOAccess.java
index 7345379..13968fb 100644
--- a/luni/src/main/java/java/nio/NIOAccess.java
+++ b/luni/src/main/java/java/nio/NIOAccess.java
@@ -16,6 +16,10 @@
 
 package java.nio;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
@@ -24,9 +28,12 @@
  * @hide
  */
 // @VisibleForTesting : was default
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class NIOAccess {
 
+    private NIOAccess() {}
+
     /**
      * Returns the underlying native pointer to the data of the given
      * Buffer starting at the Buffer's current position, or 0 if the
@@ -46,9 +53,15 @@
     /**
      * 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 b  {@code Buffer} to get its underlying data array
+     * @return   underlying Java array
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static Object getBaseArray(Buffer b) {
         return b.hasArray() ? b.array() : null;
     }
@@ -58,10 +71,16 @@
      * Java array object containing the data of the given Buffer to
      * 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.
+     * meaningful if {@link #getBaseArray(Buffer)} returns non-null.
+     *
+     * @param b {@code Buffer} to get its underlying data array's base offset
+     * @return  underlying Java array's base offset
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int getBaseArrayOffset(Buffer b) {
         return b.hasArray() ? ((b.arrayOffset() + b.position) << b._elementSizeShift) : 0;
     }
diff --git a/luni/src/main/java/java/nio/NioUtils.java b/luni/src/main/java/java/nio/NioUtils.java
index aca91b0..cfac508 100644
--- a/luni/src/main/java/java/nio/NioUtils.java
+++ b/luni/src/main/java/java/nio/NioUtils.java
@@ -16,6 +16,9 @@
 
 package java.nio;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.Closeable;
@@ -32,13 +35,22 @@
 /**
  * @hide internal use only
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class NioUtils {
     private NioUtils() {
     }
 
+    /**
+     * Frees {@link DirectByteBuffer} running associated {@link sun.misc.Cleaner Cleaner}.
+     *
+     * @param buffer to free with associated {@code Cleaner}
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void freeDirectBuffer(ByteBuffer buffer) {
         if (buffer == null) {
             return;
@@ -55,6 +67,8 @@
 
     /**
      * Returns the int file descriptor from within the given FileChannel 'fc'.
+     *
+     * @hide
      */
     public static FileDescriptor getFD(FileChannel fc) {
         return ((FileChannelImpl) fc).fd;
@@ -62,6 +76,8 @@
 
     /**
      * Helps bridge between io and nio.
+     *
+     * @hide
      */
     public static FileChannel newFileChannel(Closeable ioObject, FileDescriptor fd, int mode) {
         boolean readable = (mode & O_ACCMODE) != O_WRONLY;
@@ -71,21 +87,34 @@
     }
 
     /**
-     * Exposes the array backing a non-direct ByteBuffer, even if the ByteBuffer is read-only.
+     * Exposes the array backing a non-direct {@link java.nio.ByteBuffer ByteBuffer}, even if
+     * the {@link java.nio.ByteBuffer ByteBuffer} is read-only.
      * Normally, attempting to access the array backing a read-only buffer throws.
+     *
+     * @param b  {@link java.nio.ByteBuffer ByteBuffer} to access its backing array.
+     * @return   buffer's underlying array.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static byte[] unsafeArray(ByteBuffer b) {
         return b.array();
     }
 
     /**
-     * Exposes the array offset for the array backing a non-direct ByteBuffer,
-     * even if the ByteBuffer is read-only.
+     * Exposes the array offset for the array backing a non-direct {@link java.nio.ByteBuffer ByteBuffer},
+     * even if the {@link java.nio.ByteBuffer ByteBuffer} is read-only.
+     *
+     * @param b  {@link java.nio.ByteBuffer ByteBuffer} to access its backing array offset.
+     * @return   buffer's underlying array data offset.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int unsafeArrayOffset(ByteBuffer b) {
         return b.arrayOffset();
     }
diff --git a/luni/src/main/java/libcore/api/CorePlatformApi.java b/luni/src/main/java/libcore/api/CorePlatformApi.java
index 99f1679..d1fc685 100644
--- a/luni/src/main/java/libcore/api/CorePlatformApi.java
+++ b/luni/src/main/java/libcore/api/CorePlatformApi.java
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
@@ -42,4 +43,27 @@
 @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE})
 @Retention(RetentionPolicy.SOURCE)
 public @interface CorePlatformApi {
+
+    /** Enumeration of the possible statuses of the API in the core/platform API surface. */
+    @IntraCoreApi
+    enum Status {
+
+        /**
+         * This API is considered stable, and so present in both the stable and legacy version of
+         * the API surface.
+        */
+        @IntraCoreApi
+        STABLE,
+
+        /**
+         * This API is not (yet) considered stable, and so only present in the legacy version of
+         * the API surface.
+         */
+        @IntraCoreApi
+        LEGACY_ONLY
+    }
+
+    /** The status of the API in the core/platform API surface. */
+    @IntraCoreApi
+    Status status() default libcore.api.CorePlatformApi.Status.LEGACY_ONLY;
 }
diff --git a/luni/src/main/java/libcore/content/type/MimeMap.java b/luni/src/main/java/libcore/content/type/MimeMap.java
index 51e792f..fcd08ec 100644
--- a/luni/src/main/java/libcore/content/type/MimeMap.java
+++ b/luni/src/main/java/libcore/content/type/MimeMap.java
@@ -16,6 +16,10 @@
 
 package libcore.content.type;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -34,16 +38,38 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = CorePlatformApi.Status.STABLE)
 public final class MimeMap {
 
-    @CorePlatformApi
-    public static Builder builder() {
+    /**
+     * Creates a MIME type map builder.
+     *
+     * @return builder
+     *
+     * @see MimeMap.Builder
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public static @NonNull Builder builder() {
         return new Builder();
     }
 
-    @CorePlatformApi
-    public Builder buildUpon() {
+    /**
+     * Creates a MIME type map builder with values based on {@code this} instance.
+     * This builder will contain all previously added MIMEs and extensions.
+     *
+     * @return builder
+     *
+     * @see MimeMap.Builder
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public @NonNull Builder buildUpon() {
         return new Builder(mimeToExt, extToMime);
     }
 
@@ -60,12 +86,12 @@
     private static volatile MemoizingSupplier<@NonNull MimeMap> instanceSupplier =
             new MemoizingSupplier<>(
                     () -> builder()
-                            .put("application/pdf", "pdf")
-                            .put("image/jpeg", "jpg")
-                            .put("image/x-ms-bmp", "bmp")
-                            .put("text/html", Arrays.asList("htm", "html"))
-                            .put("text/plain", Arrays.asList("text", "txt"))
-                            .put("text/x-java", "java")
+                            .addMimeMapping("application/pdf", "pdf")
+                            .addMimeMapping("image/jpeg", "jpg")
+                            .addMimeMapping("image/x-ms-bmp", "bmp")
+                            .addMimeMapping("text/html", Arrays.asList("htm", "html"))
+                            .addMimeMapping("text/plain", Arrays.asList("text", "txt"))
+                            .addMimeMapping("text/x-java", "java")
                             .build());
 
     private MimeMap(Map<String, String> mimeToExt, Map<String, String> extToMime) {
@@ -82,9 +108,14 @@
     }
 
     /**
+     * Gets system's current default {@link MimeMap}
+     *
      * @return The system's current default {@link MimeMap}.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public static @NonNull MimeMap getDefault() {
         return Objects.requireNonNull(instanceSupplier.get());
     }
@@ -99,8 +130,11 @@
      * {@link #getDefault()} without an intervening call to
      * {@link #setDefaultSupplier(Supplier)} will return that same instance
      * without consulting {@code mimeMapSupplier} a second time.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public static void setDefaultSupplier(@NonNull Supplier<@NonNull MimeMap> mimeMapSupplier) {
         instanceSupplier = new MemoizingSupplier<>(Objects.requireNonNull(mimeMapSupplier));
     }
@@ -111,8 +145,11 @@
      * @param extension A file extension without the leading '.'
      * @return Whether a MIME type has been registered for the given case insensitive file
      *         extension.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public final boolean hasExtension(@Nullable String extension) {
         return guessMimeTypeFromExtension(extension) != null;
     }
@@ -124,8 +161,11 @@
      * @param extension A file extension without the leading '.'
      * @return The lower-case MIME type registered for the given case insensitive file extension,
      *         or null if there is none.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public final @Nullable String guessMimeTypeFromExtension(@Nullable String extension) {
         if (extension == null) {
             return null;
@@ -135,11 +175,16 @@
     }
 
     /**
+     * Returns whether given case insensetive MIME type is mapped to a file extension.
+     *
      * @param mimeType A MIME type (i.e. {@code "text/plain")
      * @return Whether the given case insensitive MIME type is
      *         {@link #guessMimeTypeFromExtension(String) mapped} to a file extension.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public final boolean hasMimeType(@Nullable String mimeType) {
         return guessExtensionFromMimeType(mimeType) != null;
     }
@@ -151,8 +196,11 @@
      * @param mimeType A MIME type (i.e. text/plain)
      * @return The lower-case file extension (without the leading "." that has been registered for
      *         the given case insensitive MIME type, or null if there is none.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public final @Nullable String guessExtensionFromMimeType(@Nullable String mimeType) {
         if (mimeType == null) {
             return null;
@@ -166,9 +214,12 @@
      * {@link #hasMimeType(String) maps to some extension}. Note that the
      * reverse mapping might not exist.
      *
+     * @return unmodifiable {@link Set} of MIME types mapped to some extension
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public @NonNull Set<String> mimeTypes() {
         return Collections.unmodifiableSet(mimeToExt.keySet());
     }
@@ -178,9 +229,13 @@
      * {@link #hasExtension(String) maps to some MIME type}. Note that the
      * reverse mapping might not exist.
      *
+     * @return unmodifiable {@link Set} of extensions that this {@link MimeMap}
+     *         maps to some MIME type
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public @NonNull Set<String> extensions() {
         return Collections.unmodifiableSet(extToMime.keySet());
     }
@@ -194,6 +249,10 @@
 
     private volatile int hashCode = 0;
 
+    /**
+     *
+     * @hide
+     */
     @Override
     public int hashCode() {
         if (hashCode == 0) { // potentially uninitialized
@@ -202,6 +261,10 @@
         return hashCode;
     }
 
+    /**
+     *
+     * @hide
+     */
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof MimeMap)) {
@@ -214,15 +277,24 @@
         return mimeToExt.equals(that.mimeToExt) && extToMime.equals(that.extToMime);
     }
 
+    /**
+     *
+     * @hide
+     */
     @Override
     public String toString() {
         return "MimeMap[" + mimeToExt + ", " + extToMime + "]";
     }
 
     /**
+     * A builder for mapping of MIME types to extensions and back.
+     * Use {@link #addMimeMapping(String, List)} and {@link #addMimeMapping(String, String)} to add
+     * mapping entries and build final {@link MimeMap} with {@link #build()}.
+     *
      * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
     public static final class Builder {
         private final Map<String, String> mimeToExt;
         private final Map<String, String> extToMime;
@@ -323,9 +395,12 @@
          *                 are invalid (null, empty, contain ' ', or '?' after an initial '?' has
          *                 been stripped off).
          * @return This builder.
+         *
+         * @hide
          */
-        @CorePlatformApi
-        public Builder put(@NonNull String mimeSpec, @NonNull List<@NonNull String> extensionSpecs)
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        public @NonNull Builder addMimeMapping(@NonNull String mimeSpec, @NonNull List<@NonNull String> extensionSpecs)
         {
             Element mimeElement = Element.ofMimeSpec(mimeSpec); // validate mimeSpec unconditionally
             if (extensionSpecs.isEmpty()) {
@@ -346,15 +421,27 @@
          *
          * @hide
          */
-        public Builder put(@NonNull String mimeSpec, @NonNull String extensionSpec) {
-            return put(mimeSpec, Collections.singletonList(extensionSpec));
+        public @NonNull Builder addMimeMapping(@NonNull String mimeSpec, @NonNull String extensionSpec) {
+            return addMimeMapping(mimeSpec, Collections.singletonList(extensionSpec));
         }
 
-        @CorePlatformApi
-        public MimeMap build() {
+        /**
+         * Builds {@link MimeMap} containing all added MIME mappings.
+         *
+         * @return {@link MimeMap} containing previously added MIME mapping entries
+         *
+         * @hide
+         */
+        @SystemApi(client = MODULE_LIBRARIES)
+        @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+        public @NonNull MimeMap build() {
             return new MimeMap(mimeToExt, extToMime);
         }
 
+        /**
+         *
+         * @hide
+         */
         @Override
         public String toString() {
             return "MimeMap.Builder[" + mimeToExt + ", " + extToMime + "]";
diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
index 74aca08..3ab077f 100644
--- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java
+++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
@@ -22,27 +22,20 @@
 
 import java.text.FieldPosition;
 import java.util.TimeZone;
-import libcore.util.BasicLruCache;
 
 import static libcore.icu.DateUtilsBridge.FORMAT_UTC;
 
 /**
- * Exposes icu4j's DateIntervalFormat.
+ * This class is only kept for @UnsupportedAppUsage, and should not used by libcore/frameworks.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
 public final class DateIntervalFormat {
 
-  private static final BasicLruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS
-          = new BasicLruCache<>(8);
-
   private DateIntervalFormat() {
   }
 
-  // This is public DateUtils API in frameworks/base.
   @UnsupportedAppUsage
-  @libcore.api.CorePlatformApi
   public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) {
     if ((flags & FORMAT_UTC) != 0) {
       olsonId = "UTC";
@@ -55,7 +48,7 @@
     return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags);
   }
 
-  // This is our slightly more sensible internal API. (A truly sane replacement would take a
+  // This is our slightly more sensible internal API. (A better replacement would take a
   // skeleton instead of int flags.)
   public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone,
       long startMs, long endMs, int flags) {
@@ -88,25 +81,11 @@
     }
 
     String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
-    synchronized (CACHED_FORMATTERS) {
-      android.icu.text.DateIntervalFormat formatter =
-          getFormatter(skeleton, icuLocale, icuTimeZone);
-      return formatter.format(startCalendar, endCalendar, new StringBuffer(),
-          new FieldPosition(0)).toString();
-    }
-  }
-
-  private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale,
-      android.icu.util.TimeZone icuTimeZone) {
-    String key = skeleton + "\t" + locale + "\t" + icuTimeZone;
-    android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key);
-    if (formatter != null) {
-      return formatter;
-    }
-    formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale);
+    android.icu.text.DateIntervalFormat formatter =
+            android.icu.text.DateIntervalFormat.getInstance(skeleton, icuLocale);
     formatter.setTimeZone(icuTimeZone);
-    CACHED_FORMATTERS.put(key, formatter);
-    return formatter;
+    return formatter.format(startCalendar, endCalendar, new StringBuffer(),
+        new FieldPosition(0)).toString();
   }
 
   private static boolean isExactlyMidnight(Calendar c) {
diff --git a/luni/src/main/java/libcore/icu/DateTimeFormat.java b/luni/src/main/java/libcore/icu/DateTimeFormat.java
deleted file mode 100644
index 2809625..0000000
--- a/luni/src/main/java/libcore/icu/DateTimeFormat.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.icu;
-
-import android.icu.text.DateFormat;
-import android.icu.text.DateTimePatternGenerator;
-import android.icu.text.DisplayContext;
-import android.icu.text.SimpleDateFormat;
-import android.icu.util.Calendar;
-import android.icu.util.ULocale;
-
-import libcore.util.BasicLruCache;
-
-/**
- * A formatter that outputs a single date/time.
- */
-public class DateTimeFormat {
-  private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
-
-  static class FormatterCache extends BasicLruCache<String, DateFormat> {
-    FormatterCache() {
-      super(8);
-    }
-  }
-
-  private DateTimeFormat() {
-  }
-
-  public static String format(ULocale icuLocale, Calendar time, int flags,
-      DisplayContext displayContext) {
-    String skeleton = DateUtilsBridge.toSkeleton(time, flags);
-    String key = skeleton + "\t" + icuLocale + "\t" + time.getTimeZone();
-    synchronized(CACHED_FORMATTERS) {
-      DateFormat formatter = CACHED_FORMATTERS.get(key);
-      if (formatter == null) {
-        DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(icuLocale);
-        formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale);
-        CACHED_FORMATTERS.put(key, formatter);
-      }
-      formatter.setContext(displayContext);
-      return formatter.format(time);
-    }
-  }
-}
diff --git a/luni/src/main/java/libcore/icu/DateUtilsBridge.java b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
index ac7a036..3b90a1f 100644
--- a/luni/src/main/java/libcore/icu/DateUtilsBridge.java
+++ b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
@@ -16,14 +16,14 @@
 
 package libcore.icu;
 
-import android.icu.impl.JavaTimeZone;
 import android.icu.util.Calendar;
 import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
 import android.icu.util.ULocale;
 
 /**
- * Common methods and constants for the various ICU formatters used to support
- * android.text.format.DateUtils.
+ * This class is no longer used by android.text.format.DateUtils, but kept only for
+ * @UnsupportedAppUsage in {@link DateIntervalFormat}.
  */
 public final class DateUtilsBridge {
   // These are all public API in DateUtils. There are others, but they're either for use with
@@ -50,24 +50,24 @@
    * writing the libcore implementation is faster but restricted to 1902 - 2038.
    * Callers must not modify the {@code tz} after calling this method.
    */
-  public static android.icu.util.TimeZone icuTimeZone(java.util.TimeZone tz) {
-    JavaTimeZone javaTimeZone = new JavaTimeZone(tz, null);
-    javaTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
-    return javaTimeZone;
+  /* package */ static android.icu.util.TimeZone icuTimeZone(java.util.TimeZone tz) {
+    android.icu.util.TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID());
+    icuTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
+    return icuTimeZone;
   }
 
-  public static Calendar createIcuCalendar(android.icu.util.TimeZone icuTimeZone, ULocale icuLocale,
+  /* package */ static Calendar createIcuCalendar(android.icu.util.TimeZone icuTimeZone, ULocale icuLocale,
       long timeInMillis) {
     Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
     calendar.setTimeInMillis(timeInMillis);
     return calendar;
   }
 
-  public static String toSkeleton(Calendar calendar, int flags) {
+  /* package */ static String toSkeleton(Calendar calendar, int flags) {
     return toSkeleton(calendar, calendar, flags);
   }
 
-  public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
+  /* package */ static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
     if ((flags & FORMAT_ABBREV_ALL) != 0) {
       flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
     }
@@ -147,7 +147,7 @@
     return builder.toString();
   }
 
-  public static int dayDistance(Calendar c1, Calendar c2) {
+  /* package */ static int dayDistance(Calendar c1, Calendar c2) {
     return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
   }
 
@@ -155,7 +155,7 @@
    * Returns whether the argument will be displayed as if it were midnight, using any of the
    * skeletons provided by {@link #toSkeleton}.
    */
-  public static boolean isDisplayMidnightUsingSkeleton(Calendar c) {
+  /* package */ static boolean isDisplayMidnightUsingSkeleton(Calendar c) {
     // All the skeletons returned by toSkeleton have minute precision (they may abbreviate 4:00 PM
     // to 4 PM but will still show the following minute as 4:01 PM).
     return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0;
diff --git a/luni/src/main/java/libcore/icu/ICU.java b/luni/src/main/java/libcore/icu/ICU.java
index f6e5fed..e72d707 100644
--- a/luni/src/main/java/libcore/icu/ICU.java
+++ b/luni/src/main/java/libcore/icu/ICU.java
@@ -17,9 +17,16 @@
 package libcore.icu;
 
 import android.compat.annotation.UnsupportedAppUsage;
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.util.Currency;
+import android.icu.util.IllformedLocaleException;
 import android.icu.util.ULocale;
 
+import com.android.icu.util.ExtendedCalendar;
+import com.android.icu.util.LocaleNative;
+
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -33,7 +40,6 @@
  * Makes ICU data accessible to Java.
  * @hide
  */
-@libcore.api.CorePlatformApi
 public final class ICU {
 
   @UnsupportedAppUsage
@@ -43,12 +49,36 @@
   private static Locale[] availableLocalesCache;
 
   private static String[] isoCountries;
+  private static Set<String> isoCountriesSet;
 
   private static String[] isoLanguages;
 
+  /**
+   * Avoid initialization with many dependencies here, because when this is called,
+   * lower-level classes, e.g. java.lang.System, are not initialized and java.lang.System
+   * relies on getIcuVersion().
+   */
+  static {
+
+  }
+
   private ICU() {
   }
 
+  public static void initializeCacheInZygote() {
+    // Fill CACHED_PATTERNS with the patterns from default locale and en-US initially.
+    // This should be called in Zygote pre-fork process and the initial values in the cache
+    // can be shared among app. The cache was filled by LocaleData in the older Android platform,
+    // but moved here, due to an performance issue http://b/161846393.
+    // It initializes 2 x 4 = 8 values in the CACHED_PATTERNS whose max size should be >= 8.
+    for (Locale locale : new Locale[] {Locale.US, Locale.getDefault()}) {
+      getTimePattern(locale, false, false);
+      getTimePattern(locale, false, true);
+      getTimePattern(locale, true, false);
+      getTimePattern(locale, true, true);
+    }
+  }
+
   /**
    * Returns an array of two-letter ISO 639-1 language codes, either from ICU or our cache.
    */
@@ -63,12 +93,33 @@
    * Returns an array of two-letter ISO 3166 country codes, either from ICU or our cache.
    */
   public static String[] getISOCountries() {
+    return getISOCountriesInternal().clone();
+  }
+
+  /**
+   * Returns true if the string is a 2-letter ISO 3166 country code.
+   */
+  public static boolean isIsoCountry(String country) {
+    if (isoCountriesSet == null) {
+      String[] isoCountries = getISOCountriesInternal();
+      Set<String> newSet = new HashSet<>(isoCountries.length);
+      for (String isoCountry : isoCountries) {
+        newSet.add(isoCountry);
+      }
+      isoCountriesSet = newSet;
+    }
+    return country != null && isoCountriesSet.contains(country);
+  }
+
+  private static String[] getISOCountriesInternal() {
     if (isoCountries == null) {
       isoCountries = getISOCountriesNative();
     }
-    return isoCountries.clone();
+    return isoCountries;
   }
 
+
+
   private static final int IDX_LANGUAGE = 0;
   private static final int IDX_SCRIPT = 1;
   private static final int IDX_REGION = 2;
@@ -268,26 +319,41 @@
     return availableLocalesCache.clone();
   }
 
+  /* package */ static String getTimePattern(Locale locale, boolean is24Hour, boolean withSecond) {
+    final String skeleton;
+    if (withSecond) {
+      skeleton = is24Hour ? "Hms" : "hms";
+    } else {
+      skeleton = is24Hour ? "Hm" : "hm";
+    }
+    return getBestDateTimePattern(skeleton, locale);
+  }
+
   @UnsupportedAppUsage
-  @libcore.api.CorePlatformApi
   public static String getBestDateTimePattern(String skeleton, Locale locale) {
     String languageTag = locale.toLanguageTag();
     String key = skeleton + "\t" + languageTag;
     synchronized (CACHED_PATTERNS) {
       String pattern = CACHED_PATTERNS.get(key);
       if (pattern == null) {
-        pattern = getBestDateTimePatternNative(skeleton, languageTag);
+        pattern = getBestDateTimePattern0(skeleton, locale);
         CACHED_PATTERNS.put(key, pattern);
       }
       return pattern;
     }
   }
 
-  @UnsupportedAppUsage
-  private static native String getBestDateTimePatternNative(String skeleton, String languageTag);
+  private static String getBestDateTimePattern0(String skeleton, Locale locale) {
+      DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale);
+      return dtpg.getBestPattern(skeleton);
+  }
 
   @UnsupportedAppUsage
-  @libcore.api.CorePlatformApi
+  private static String getBestDateTimePatternNative(String skeleton, String languageTag) {
+    return getBestDateTimePattern0(skeleton, Locale.forLanguageTag(languageTag));
+  }
+
+  @UnsupportedAppUsage
   public static char[] getDateFormatOrder(String pattern) {
     char[] result = new char[3];
     int resultIndex = 0;
@@ -329,13 +395,152 @@
     return result;
   }
 
+  /**
+   * {@link java.time.format.DateTimeFormatter} does not handle some date symbols, e.g. 'B' / 'b',
+   * and thus we use a heuristic algorithm to remove the symbol. See http://b/174804526.
+   * See {@link #transformIcuDateTimePattern(String)} for documentation about the implementation.
+   */
+  public static String transformIcuDateTimePattern_forJavaTime(String pattern) {
+    return transformIcuDateTimePattern(pattern);
+  }
+
+  /**
+   * {@link java.text.SimpleDateFormat} does not handle some date symbols, e.g. 'B' / 'b',
+   * and simply ignore the symbol in formatting. Instead, we should avoid exposing the symbol
+   * entirely in all public APIs, e.g. {@link java.text.SimpleDateFormat#toPattern()},
+   * and thus we use a heuristic algorithm to remove the symbol. See http://b/174804526.
+   * See {@link #transformIcuDateTimePattern(String)} for documentation about the implementation.
+   */
+  public static String transformIcuDateTimePattern_forJavaText(String pattern) {
+    return transformIcuDateTimePattern(pattern);
+  }
+
+  /**
+   * Rewrite the date/time pattern coming ICU to be consumed by libcore classes.
+   * It's an ideal place to rewrite the pattern entirely when multiple symbols not digested
+   * by libcore need to be removed/processed. Rewriting in single place could be more efficient
+   * in a small or constant number of scans instead of scanning for every symbol.
+   *
+   * {@link LocaleData#initLocaleData(Locale)} also rewrites time format, but only a subset of
+   * patterns. In the future, that should migrate to this function in order to handle the symbols
+   * in one place, but now separate because java.text and java.time handles different sets of
+   * symbols.
+   */
+  private static String transformIcuDateTimePattern(String pattern) {
+    if (pattern == null) {
+      return null;
+    }
+
+    // For details about the different symbols, see
+    // http://cldr.unicode.org/translation/date-time-1/date-time-patterns#TOC-Day-period-patterns
+    // The symbols B means "Day periods with locale-specific ranges".
+    // English example: 2:00 at night, 10:00 in the morning, 12:00 in the afternoon.
+    boolean contains_B = pattern.indexOf('B') != -1;
+    // AM, PM, noon and midnight. English example: 10:00 AM, 12:00 noon, 7:00 PM
+    boolean contains_b = pattern.indexOf('b') != -1;
+
+    // Simply remove the symbol 'B' and 'b' if 24-hour 'H' exists because the 24-hour format
+    // provides enough information and the day periods are optional. See http://b/174804526.
+    // Don't handle symbol 'B'/'b' with 12-hour 'h' because it's much more complicated because
+    // we likely need to replace 'B'/'b' with 'a' inserted into a new right position or use other
+    // ways.
+    boolean remove_B_and_b = (contains_B || contains_b) && (pattern.indexOf('H') != -1);
+
+    if (remove_B_and_b) {
+      pattern = rewriteIcuDateTimePattern(pattern);
+    }
+    return pattern;
+  }
+
+  /**
+   * Rewrite pattern with heuristics. It's known to
+   *   - Remove 'b' and 'B' from simple patterns, e.g. "B H:mm" and "dd-MM-yy B HH:mm:ss" only.
+   *   - (Append the new heuristics)
+   */
+  private static String rewriteIcuDateTimePattern(String pattern) {
+    // The below implementation can likely be replaced by a regular expression via
+    // String.replaceAll(). However, it's known that libcore's regex implementation is more
+    // memory-intensive, and the below implementation is likely cheaper, but it's not yet measured.
+    StringBuilder sb = new StringBuilder(pattern.length());
+    char prev = ' '; // the initial value is not used.
+    for (int i = 0; i < pattern.length(); i++) {
+      char curr = pattern.charAt(i);
+      switch(curr) {
+        case 'B':
+        case 'b':
+          // Ignore 'B' and 'b'
+          break;
+        case ' ': // Ascii whitespace
+          // caveat: Ideally it's a case for all Unicode whitespaces by UCharacter.isUWhiteSpace(c)
+          // but checking ascii whitespace only is enough for the CLDR data when this is written.
+          if (i != 0 && (prev == 'B' || prev == 'b')) {
+            // Ignore the whitespace behind the symbol 'B'/'b' because it's likely a whitespace to
+            // separate the day period with the next text.
+          } else {
+            sb.append(curr);
+          }
+          break;
+        default:
+          sb.append(curr);
+          break;
+      }
+      prev = curr;
+    }
+
+    // Remove the trailing whitespace which is likely following the symbol 'B'/'b' in the original
+    // pattern, e.g. "hh:mm B" (12:00 in the afternoon).
+    int lastIndex = sb.length() - 1;
+    if (lastIndex >= 0 && sb.charAt(lastIndex) == ' ') {
+      sb.deleteCharAt(lastIndex);
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Returns the version of the CLDR data in use, such as "22.1.1".
+   *
+   */
+  public static native String getCldrVersion();
+
+  /**
+   * Returns the icu4c version in use, such as "50.1.1".
+   */
+  public static native String getIcuVersion();
+
+  /**
+   * Returns the Unicode version our ICU supports, such as "6.2".
+   */
+  public static native String getUnicodeVersion();
+
   // --- Errors.
 
   // --- Native methods accessing ICU's database.
 
   private static native String[] getAvailableLocalesNative();
 
-  public static native String getCurrencyCode(String countryCode);
+    /**
+     * Query ICU for the currency being used in the country right now.
+     * @param countryCode ISO 3166 two-letter country code
+     * @return ISO 4217 3-letter currency code if found, otherwise null.
+     */
+  public static String getCurrencyCode(String countryCode) {
+      // Fail fast when country code is not valid.
+      if (countryCode == null || countryCode.length() == 0) {
+          return null;
+      }
+      final ULocale countryLocale;
+      try {
+          countryLocale = new ULocale.Builder().setRegion(countryCode).build();
+      } catch (IllformedLocaleException e) {
+          return null; // Return null on invalid country code.
+      }
+      String[] isoCodes = Currency.getAvailableCurrencyCodes(countryLocale, new Date());
+      if (isoCodes == null || isoCodes.length == 0) {
+        return null;
+      }
+      return isoCodes[0];
+  }
+
 
   public static native String getISO3Country(String languageTag);
 
@@ -373,15 +578,25 @@
   private static native String[] getISOLanguagesNative();
   private static native String[] getISOCountriesNative();
 
-  static native boolean initLocaleDataNative(String languageTag, LocaleData result);
-
   /**
    * Takes a BCP-47 language tag (Locale.toLanguageTag()). e.g. en-US, not en_US
    */
-  public static native void setDefaultLocale(String languageTag);
+  public static void setDefaultLocale(String languageTag) {
+    LocaleNative.setDefault(languageTag);
+  }
 
   /**
    * Returns a locale name, not a BCP-47 language tag. e.g. en_US not en-US.
    */
   public static native String getDefaultLocale();
+
+
+  /**
+   * @param calendarType LDML-defined legacy calendar type. See keyTypeData.txt in ICU.
+   */
+  public static ExtendedCalendar getExtendedCalendar(Locale locale, String calendarType) {
+      ULocale uLocale = ULocale.forLocale(locale)
+              .setKeywordValue("calendar", calendarType);
+      return ExtendedCalendar.getInstance(uLocale);
+  }
 }
diff --git a/luni/src/main/java/libcore/icu/LocaleData.java b/luni/src/main/java/libcore/icu/LocaleData.java
index 12b978de..04170a6 100644
--- a/luni/src/main/java/libcore/icu/LocaleData.java
+++ b/luni/src/main/java/libcore/icu/LocaleData.java
@@ -20,17 +20,23 @@
 import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.compat.Compatibility;
-import android.icu.impl.ICUData;
-import android.icu.impl.ICUResourceBundle;
+import android.icu.text.DateFormatSymbols;
+import android.icu.text.DecimalFormat;
+import android.icu.text.DecimalFormatSymbols;
+import android.icu.text.NumberFormat;
 import android.icu.text.NumberingSystem;
-import android.icu.util.UResourceBundle;
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.ULocale;
+
+import com.android.icu.text.ExtendedDecimalFormatSymbols;
+import com.android.icu.util.ExtendedCalendar;
 
 import dalvik.system.VMRuntime;
 
 import java.text.DateFormat;
 import java.util.HashMap;
 import java.util.Locale;
-import java.util.MissingResourceException;
 import libcore.util.Objects;
 
 /**
@@ -41,7 +47,6 @@
  * them a clone rather than the original.
  * @hide
  */
-@libcore.api.CorePlatformApi
 public final class LocaleData {
 
     /**
@@ -49,6 +54,7 @@
      */
     private static final Locale LOCALE_EN_US_POSIX = new Locale("en", "US", "POSIX");
 
+
     // In Android Q or before, when this class tries to load {@link Locale#ROOT} data, en_US_POSIX
     // locale data is incorrectly loaded due to a bug b/159514442 (public bug b/159047832).
     //
@@ -95,51 +101,35 @@
 
     // Used by Calendar.
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public Integer firstDayOfWeek;
     @UnsupportedAppUsage
     public Integer minimalDaysInFirstWeek;
 
     // Used by DateFormatSymbols.
-    @libcore.api.CorePlatformApi
     public String[] amPm; // "AM", "PM".
     public String[] eras; // "BC", "AD".
 
-    @libcore.api.CorePlatformApi
     public String[] longMonthNames; // "January", ...
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public String[] shortMonthNames; // "Jan", ...
-    @libcore.api.CorePlatformApi
     public String[] tinyMonthNames; // "J", ...
-    @libcore.api.CorePlatformApi
     public String[] longStandAloneMonthNames; // "January", ...
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public String[] shortStandAloneMonthNames; // "Jan", ...
-    @libcore.api.CorePlatformApi
     public String[] tinyStandAloneMonthNames; // "J", ...
 
-    @libcore.api.CorePlatformApi
     public String[] longWeekdayNames; // "Sunday", ...
-    @libcore.api.CorePlatformApi
     public String[] shortWeekdayNames; // "Sun", ...
-    @libcore.api.CorePlatformApi
     public String[] tinyWeekdayNames; // "S", ...
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public String[] longStandAloneWeekdayNames; // "Sunday", ...
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public String[] shortStandAloneWeekdayNames; // "Sun", ...
-    @libcore.api.CorePlatformApi
     public String[] tinyStandAloneWeekdayNames; // "S", ...
 
-    // Used by frameworks/base DateSorter and DateUtils.
-    @libcore.api.CorePlatformApi
-    public String yesterday; // "Yesterday".
+    // today and tomorrow is only kept for @UnsupportedAppUsage.
+    // Their value is hard-coded, not localized.
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public String today; // "Today".
     @UnsupportedAppUsage
     public String tomorrow; // "Tomorrow".
@@ -154,28 +144,15 @@
     public String mediumDateFormat;
     public String shortDateFormat;
 
-    // Used by TimePicker. Not currently used by UTS#35.
-    @libcore.api.CorePlatformApi
-    public String narrowAm; // "a".
-    @libcore.api.CorePlatformApi
-    public String narrowPm; // "p".
-
-    // Used by DateFormat to implement 12- and 24-hour SHORT and MEDIUM.
-    // They are also used directly by frameworks code.
+    // timeFormat_hm and timeFormat_Hm are only kept for @UnsupportedAppUsage.
+    // Their value is hard-coded, not localized.
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public String timeFormat_hm;
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public String timeFormat_Hm;
-    @libcore.api.CorePlatformApi
-    public String timeFormat_hms;
-    @libcore.api.CorePlatformApi
-    public String timeFormat_Hms;
 
     // Used by DecimalFormatSymbols.
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public char zeroDigit;
     public char decimalSeparator;
     public char groupingSeparator;
@@ -187,9 +164,6 @@
     public String exponentSeparator;
     public String infinity;
     public String NaN;
-    // Also used by Currency.
-    public String currencySymbol;
-    public String internationalCurrencySymbol;
 
     // Used by DecimalFormat and NumberFormat.
     public String numberPattern;
@@ -197,7 +171,14 @@
     public String currencyPattern;
     public String percentPattern;
 
-    private LocaleData() {
+    private final Locale mLocale;
+
+    private LocaleData(Locale locale) {
+        mLocale = locale;
+        today = "Today";
+        tomorrow = "Tomorrow";
+        timeFormat_hm = "h:mm a";
+        timeFormat_Hm = "HH:mm";
     }
 
     @UnsupportedAppUsage
@@ -236,7 +217,6 @@
      * Returns a shared LocaleData for the given locale.
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
     public static LocaleData get(Locale locale) {
         if (locale == null) {
             throw new NullPointerException("locale == null");
@@ -266,7 +246,6 @@
         return Objects.toString(this);
     }
 
-    @libcore.api.CorePlatformApi
     public String getDateFormat(int style) {
         switch (style) {
         case DateFormat.SHORT:
@@ -282,18 +261,22 @@
     }
 
     public String getTimeFormat(int style) {
+        // Do not cache ICU.getTimePattern() return value in the LocaleData instance
+        // because most users do not enable this setting, hurts performance in critical path,
+        // e.g. b/161846393, and ICU.getBestDateTimePattern will cache it in  ICU.CACHED_PATTERNS
+        // on demand.
         switch (style) {
         case DateFormat.SHORT:
             if (DateFormat.is24Hour == null) {
                 return shortTimeFormat;
             } else {
-                return DateFormat.is24Hour ? timeFormat_Hm : timeFormat_hm;
+                return ICU.getTimePattern(mLocale, DateFormat.is24Hour, false);
             }
         case DateFormat.MEDIUM:
             if (DateFormat.is24Hour == null) {
                 return mediumTimeFormat;
             } else {
-                return DateFormat.is24Hour ? timeFormat_Hms : timeFormat_hms;
+                return ICU.getTimePattern(mLocale, DateFormat.is24Hour, true);
             }
         case DateFormat.LONG:
             // CLDR doesn't really have anything we can use to obey the 12-/24-hour preference.
@@ -305,21 +288,20 @@
         throw new AssertionError();
     }
 
-    private static LocaleData initLocaleData(Locale locale) {
-        LocaleData localeData = new LocaleData();
-        if (!ICU.initLocaleDataNative(locale.toLanguageTag(), localeData)) {
-            throw new AssertionError("couldn't initialize LocaleData for locale " + locale);
-        }
+    /*
+     * This method is made public for testing
+     */
+    public static LocaleData initLocaleData(Locale locale) {
+        LocaleData localeData = new LocaleData(locale);
+
+        localeData.initializeDateTimePatterns(locale);
+        localeData.initializeDateFormatData(locale);
+        localeData.initializeDecimalFormatData(locale);
+        localeData.initializeCalendarData(locale);
 
         // Libcore localizes pattern separator while ICU doesn't. http://b/112080617
         initializePatternSeparator(localeData, locale);
 
-        // Get the SHORT and MEDIUM 12- and 24-hour time format strings.
-        localeData.timeFormat_hm = ICU.getBestDateTimePattern("hm", locale);
-        localeData.timeFormat_Hm = ICU.getBestDateTimePattern("Hm", locale);
-        localeData.timeFormat_hms = ICU.getBestDateTimePattern("hms", locale);
-        localeData.timeFormat_Hms = ICU.getBestDateTimePattern("Hms", locale);
-
         // Fix up a couple of patterns.
         if (localeData.fullTimeFormat != null) {
             // There are some full time format patterns in ICU that use the pattern character 'v'.
@@ -343,36 +325,18 @@
 
     // Libcore localizes pattern separator while ICU doesn't. http://b/112080617
     private static void initializePatternSeparator(LocaleData localeData, Locale locale) {
-        NumberingSystem ns = NumberingSystem.getInstance(locale);
+        ULocale uLocale = ULocale.forLocale(locale);
+        NumberingSystem ns = NumberingSystem.getInstance(uLocale);
         // A numbering system could be numeric or algorithmic. DecimalFormat can only use
         // a numeric and decimal-based (radix == 10) system. Fallback to a Latin, a known numeric
         // and decimal-based if the default numbering system isn't. All locales should have data
         // for Latin numbering system after locale data fallback. See Numbering system section
         // in Unicode Technical Standard #35 for more details.
-        String nsName = ns != null && ns.getRadix() == 10 && !ns.isAlgorithmic()
-            ? ns.getName() : "latn";
-        ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(
-            ICUData.ICU_BASE_NAME, locale);
-        String patternSeparator = null;
-        // The fallback of number format data isn't well-specified in the spec.
-        // But the separator can't be null / empty, and ICU uses Latin numbering system
-        // as fallback.
-        if (!"latn".equals(nsName)) {
-            try {
-                patternSeparator = rb.getStringWithFallback(
-                    "NumberElements/" + nsName +"/symbols/list");
-            } catch (MissingResourceException e) {
-                // Try Latin numbering system later
-            }
+        if (ns == null || ns.getRadix() != 10 || ns.isAlgorithmic()) {
+            ns = NumberingSystem.LATIN;
         }
-
-        if (patternSeparator == null) {
-            try {
-                patternSeparator = rb.getStringWithFallback("NumberElements/latn/symbols/list");
-            } catch (MissingResourceException e) {
-                // Fallback to the default separator ';'.
-            }
-        }
+        String patternSeparator = ExtendedDecimalFormatSymbols.getInstance(uLocale, ns)
+                .getLocalizedPatternSeparator();
 
         if (patternSeparator == null || patternSeparator.isEmpty()) {
             patternSeparator = ";";
@@ -381,4 +345,95 @@
         // Pattern separator in libcore supports single java character only.
         localeData.patternSeparator = patternSeparator.charAt(0);
     }
+
+    private void initializeDateFormatData(Locale locale) {
+        DateFormatSymbols dfs = new DateFormatSymbols(GregorianCalendar.class, locale);
+
+        longMonthNames = dfs.getMonths(DateFormatSymbols.FORMAT, DateFormatSymbols.WIDE);
+        shortMonthNames = dfs.getMonths(DateFormatSymbols.FORMAT, DateFormatSymbols.ABBREVIATED);
+        tinyMonthNames = dfs.getMonths(DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW);
+        longWeekdayNames = dfs.getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.WIDE);
+        shortWeekdayNames = dfs
+            .getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.ABBREVIATED);
+        tinyWeekdayNames = dfs.getWeekdays(DateFormatSymbols.FORMAT, DateFormatSymbols.NARROW);
+
+        longStandAloneMonthNames = dfs
+            .getMonths(DateFormatSymbols.STANDALONE, DateFormatSymbols.WIDE);
+        shortStandAloneMonthNames = dfs
+            .getMonths(DateFormatSymbols.STANDALONE, DateFormatSymbols.ABBREVIATED);
+        tinyStandAloneMonthNames = dfs
+            .getMonths(DateFormatSymbols.STANDALONE, DateFormatSymbols.NARROW);
+        longStandAloneWeekdayNames = dfs
+            .getWeekdays(DateFormatSymbols.STANDALONE, DateFormatSymbols.WIDE);
+        shortStandAloneWeekdayNames = dfs
+            .getWeekdays(DateFormatSymbols.STANDALONE, DateFormatSymbols.ABBREVIATED);
+        tinyStandAloneWeekdayNames = dfs
+            .getWeekdays(DateFormatSymbols.STANDALONE, DateFormatSymbols.NARROW);
+
+        amPm = dfs.getAmPmStrings();
+        eras = dfs.getEras();
+
+    }
+
+    private void initializeDecimalFormatData(Locale locale) {
+        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(locale);
+
+        decimalSeparator = dfs.getDecimalSeparator();
+        groupingSeparator = dfs.getGroupingSeparator();
+        patternSeparator = dfs.getPatternSeparator();
+        percent = dfs.getPercentString();
+        perMill = dfs.getPerMillString();
+        monetarySeparator = dfs.getMonetaryDecimalSeparator();
+        minusSign = dfs.getMinusSignString();
+        exponentSeparator = dfs.getExponentSeparator();
+        infinity = dfs.getInfinity();
+        NaN = dfs.getNaN();
+        zeroDigit = dfs.getZeroDigit();
+
+        DecimalFormat df = (DecimalFormat) NumberFormat
+            .getInstance(locale, NumberFormat.NUMBERSTYLE);
+        numberPattern = df.toPattern();
+
+        df = (DecimalFormat) NumberFormat.getInstance(locale, NumberFormat.CURRENCYSTYLE);
+        currencyPattern = df.toPattern();
+
+        df = (DecimalFormat) NumberFormat.getInstance(locale, NumberFormat.PERCENTSTYLE);
+        percentPattern = df.toPattern();
+
+    }
+
+    private void initializeCalendarData(Locale locale) {
+        Calendar calendar = Calendar.getInstance(locale);
+
+        firstDayOfWeek = calendar.getFirstDayOfWeek();
+        minimalDaysInFirstWeek = calendar.getMinimalDaysInFirstWeek();
+    }
+
+    private void initializeDateTimePatterns(Locale locale) {
+        // libcore's java.text supports Gregorian calendar only.
+        ExtendedCalendar extendedCalendar = ICU.getExtendedCalendar(locale, "gregorian");
+
+        fullTimeFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.NONE, android.icu.text.DateFormat.FULL);
+        longTimeFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.NONE, android.icu.text.DateFormat.LONG);
+        mediumTimeFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.NONE, android.icu.text.DateFormat. MEDIUM);
+        shortTimeFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.NONE, android.icu.text.DateFormat.SHORT);
+        fullDateFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.FULL, android.icu.text.DateFormat.NONE);
+        longDateFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.LONG, android.icu.text.DateFormat.NONE);
+        mediumDateFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.MEDIUM, android.icu.text.DateFormat.NONE);
+        shortDateFormat = getDateTimeFormatString(extendedCalendar,
+            android.icu.text.DateFormat.SHORT, android.icu.text.DateFormat.NONE);
+    }
+
+    private static String getDateTimeFormatString(ExtendedCalendar extendedCalendar,
+            int dateStyle, int timeStyle) {
+        return ICU.transformIcuDateTimePattern_forJavaText(
+                extendedCalendar.getDateTimePattern(dateStyle, timeStyle));
+    }
 }
diff --git a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java b/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
deleted file mode 100644
index 0a53ef3..0000000
--- a/luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.icu;
-
-import java.util.Locale;
-import libcore.util.BasicLruCache;
-
-import android.icu.text.DisplayContext;
-import android.icu.util.Calendar;
-import android.icu.util.ULocale;
-
-import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_ALL;
-import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_MONTH;
-import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
-import static libcore.icu.DateUtilsBridge.FORMAT_NO_YEAR;
-import static libcore.icu.DateUtilsBridge.FORMAT_NUMERIC_DATE;
-import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_DATE;
-import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_TIME;
-import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_YEAR;
-
-/**
- * Exposes icu4j's RelativeDateTimeFormatter.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class RelativeDateTimeFormatter {
-
-  public static final long SECOND_IN_MILLIS = 1000;
-  public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
-  public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
-  public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
-  public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
-  // YEAR_IN_MILLIS considers 364 days as a year. However, since this
-  // constant comes from public API in DateUtils, it cannot be fixed here.
-  public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;
-
-  private static final int DAY_IN_MS = 24 * 60 * 60 * 1000;
-  private static final int EPOCH_JULIAN_DAY = 2440588;
-
-  private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
-
-  static class FormatterCache
-      extends BasicLruCache<String, android.icu.text.RelativeDateTimeFormatter> {
-    FormatterCache() {
-      super(8);
-    }
-  }
-
-  private RelativeDateTimeFormatter() {
-  }
-
-  /**
-   * This is the internal API that implements the functionality of
-   * DateUtils.getRelativeTimeSpanString(long, long, long, int), which is to
-   * return a string describing 'time' as a time relative to 'now' such as
-   * '5 minutes ago', or 'In 2 days'. More examples can be found in DateUtils'
-   * doc.
-   *
-   * In the implementation below, it selects the appropriate time unit based on
-   * the elapsed time between time' and 'now', e.g. minutes, days and etc.
-   * Callers may also specify the desired minimum resolution to show in the
-   * result. For example, '45 minutes ago' will become '0 hours ago' when
-   * minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to
-   * display, it calls icu4j's RelativeDateTimeFormatter to format the actual
-   * string according to the given locale.
-   *
-   * Note that when minResolution is set to DAY_IN_MILLIS, it returns the
-   * result depending on the actual date difference. For example, it will
-   * return 'Yesterday' even if 'time' was less than 24 hours ago but falling
-   * onto a different calendar day.
-   *
-   * It takes two additional parameters of Locale and TimeZone than the
-   * DateUtils' API. Caller must specify the locale and timezone.
-   * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
-   * the abbreviated forms when available. When 'time' equals to 'now', it
-   * always // returns a string like '0 seconds/minutes/... ago' according to
-   * minResolution.
-   */
-  @libcore.api.CorePlatformApi
-  public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
-      long now, long minResolution, int flags) {
-    // Android has been inconsistent about capitalization in the past. e.g. bug http://b/20247811.
-    // Now we capitalize everything consistently.
-    final DisplayContext displayContext = DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
-    return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags, displayContext);
-  }
-
-  public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
-      long now, long minResolution, int flags, DisplayContext displayContext) {
-    if (locale == null) {
-      throw new NullPointerException("locale == null");
-    }
-    if (tz == null) {
-      throw new NullPointerException("tz == null");
-    }
-    ULocale icuLocale = ULocale.forLocale(locale);
-    android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
-    return getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution, flags,
-        displayContext);
-  }
-
-  private static String getRelativeTimeSpanString(ULocale icuLocale,
-      android.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution, int flags,
-      DisplayContext displayContext) {
-
-    long duration = Math.abs(now - time);
-    boolean past = (now >= time);
-
-    android.icu.text.RelativeDateTimeFormatter.Style style;
-    if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
-      style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT;
-    } else {
-      style = android.icu.text.RelativeDateTimeFormatter.Style.LONG;
-    }
-
-    android.icu.text.RelativeDateTimeFormatter.Direction direction;
-    if (past) {
-      direction = android.icu.text.RelativeDateTimeFormatter.Direction.LAST;
-    } else {
-      direction = android.icu.text.RelativeDateTimeFormatter.Direction.NEXT;
-    }
-
-    // 'relative' defaults to true as we are generating relative time span
-    // string. It will be set to false when we try to display strings without
-    // a quantity, such as 'Yesterday', etc.
-    boolean relative = true;
-    int count;
-    android.icu.text.RelativeDateTimeFormatter.RelativeUnit unit;
-    android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit aunit = null;
-
-    if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {
-      count = (int)(duration / SECOND_IN_MILLIS);
-      unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.SECONDS;
-    } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {
-      count = (int)(duration / MINUTE_IN_MILLIS);
-      unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.MINUTES;
-    } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {
-      // Even if 'time' actually happened yesterday, we don't format it as
-      // "Yesterday" in this case. Unless the duration is longer than a day,
-      // or minResolution is specified as DAY_IN_MILLIS by user.
-      count = (int)(duration / HOUR_IN_MILLIS);
-      unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.HOURS;
-    } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
-      count = Math.abs(dayDistance(icuTimeZone, time, now));
-      unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.DAYS;
-
-      if (count == 2) {
-        // Some locales have special terms for "2 days ago". Return them if
-        // available. Note that we cannot set up direction and unit here and
-        // make it fall through to use the call near the end of the function,
-        // because for locales that don't have special terms for "2 days ago",
-        // icu4j returns an empty string instead of falling back to strings
-        // like "2 days ago".
-        String str;
-        if (past) {
-          synchronized (CACHED_FORMATTERS) {
-            str = getFormatter(icuLocale, style, displayContext)
-                .format(
-                    android.icu.text.RelativeDateTimeFormatter.Direction.LAST_2,
-                    android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
-          }
-        } else {
-          synchronized (CACHED_FORMATTERS) {
-            str = getFormatter(icuLocale, style, displayContext)
-                .format(
-                    android.icu.text.RelativeDateTimeFormatter.Direction.NEXT_2,
-                    android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
-          }
-        }
-        if (str != null && !str.isEmpty()) {
-          return str;
-        }
-        // Fall back to show something like "2 days ago".
-      } else if (count == 1) {
-        // Show "Yesterday / Tomorrow" instead of "1 day ago / In 1 day".
-        aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
-        relative = false;
-      } else if (count == 0) {
-        // Show "Today" if time and now are on the same day.
-        aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
-        direction = android.icu.text.RelativeDateTimeFormatter.Direction.THIS;
-        relative = false;
-      }
-    } else if (minResolution == WEEK_IN_MILLIS) {
-      count = (int)(duration / WEEK_IN_MILLIS);
-      unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.WEEKS;
-    } else {
-      Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
-      // The duration is longer than a week and minResolution is not
-      // WEEK_IN_MILLIS. Return the absolute date instead of relative time.
-
-      // Bug 19822016:
-      // If user doesn't supply the year display flag, we need to explicitly
-      // set that to show / hide the year based on time and now. Otherwise
-      // formatDateRange() would determine that based on the current system
-      // time and may give wrong results.
-      if ((flags & (FORMAT_NO_YEAR | FORMAT_SHOW_YEAR)) == 0) {
-        Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
-
-        if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
-          flags |= FORMAT_SHOW_YEAR;
-        } else {
-          flags |= FORMAT_NO_YEAR;
-        }
-      }
-      return DateTimeFormat.format(icuLocale, timeCalendar, flags, displayContext);
-    }
-
-    synchronized (CACHED_FORMATTERS) {
-      android.icu.text.RelativeDateTimeFormatter formatter =
-          getFormatter(icuLocale, style, displayContext);
-      if (relative) {
-        return formatter.format(count, direction, unit);
-      } else {
-        return formatter.format(direction, aunit);
-      }
-    }
-  }
-
-  /**
-   * This is the internal API that implements
-   * DateUtils.getRelativeDateTimeString(long, long, long, long, int), which is
-   * to return a string describing 'time' as a time relative to 'now', formatted
-   * like '[relative time/date], [time]'. More examples can be found in
-   * DateUtils' doc.
-   *
-   * The function is similar to getRelativeTimeSpanString, but it always
-   * appends the absolute time to the relative time string to return
-   * '[relative time/date clause], [absolute time clause]'. It also takes an
-   * extra parameter transitionResolution to determine the format of the date
-   * clause. When the elapsed time is less than the transition resolution, it
-   * displays the relative time string. Otherwise, it gives the absolute
-   * numeric date string as the date clause. With the date and time clauses, it
-   * relies on icu4j's RelativeDateTimeFormatter::combineDateAndTime() to
-   * concatenate the two.
-   *
-   * It takes two additional parameters of Locale and TimeZone than the
-   * DateUtils' API. Caller must specify the locale and timezone.
-   * FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set in 'flags' to get
-   * the abbreviated forms when they are available.
-   *
-   * Bug 5252772: Since the absolute time will always be part of the result,
-   * minResolution will be set to at least DAY_IN_MILLIS to correctly indicate
-   * the date difference. For example, when it's 1:30 AM, it will return
-   * 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null,
-   * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2
-   * hours ago, 11:30 PM' even with minResolution being HOUR_IN_MILLIS.
-   */
-  @libcore.api.CorePlatformApi
-  public static String getRelativeDateTimeString(Locale locale, java.util.TimeZone tz, long time,
-      long now, long minResolution, long transitionResolution, int flags) {
-
-    if (locale == null) {
-      throw new NullPointerException("locale == null");
-    }
-    if (tz == null) {
-      throw new NullPointerException("tz == null");
-    }
-    ULocale icuLocale = ULocale.forLocale(locale);
-    android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
-
-    long duration = Math.abs(now - time);
-    // It doesn't make much sense to have results like: "1 week ago, 10:50 AM".
-    if (transitionResolution > WEEK_IN_MILLIS) {
-        transitionResolution = WEEK_IN_MILLIS;
-    }
-    android.icu.text.RelativeDateTimeFormatter.Style style;
-    if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
-        style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT;
-    } else {
-        style = android.icu.text.RelativeDateTimeFormatter.Style.LONG;
-    }
-
-    Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
-    Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
-
-    int days = Math.abs(DateUtilsBridge.dayDistance(timeCalendar, nowCalendar));
-
-    // Now get the date clause, either in relative format or the actual date.
-    String dateClause;
-    if (duration < transitionResolution) {
-      // This is to fix bug 5252772. If there is any date difference, we should
-      // promote the minResolution to DAY_IN_MILLIS so that it can display the
-      // date instead of "x hours/minutes ago, [time]".
-      if (days > 0 && minResolution < DAY_IN_MILLIS) {
-         minResolution = DAY_IN_MILLIS;
-      }
-      dateClause = getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution,
-          flags, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
-    } else {
-      // We always use fixed flags to format the date clause. User-supplied
-      // flags are ignored.
-      if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
-        // Different years
-        flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
-      } else {
-        // Default
-        flags = FORMAT_SHOW_DATE | FORMAT_NO_YEAR | FORMAT_ABBREV_MONTH;
-      }
-
-      dateClause = DateTimeFormat.format(icuLocale, timeCalendar, flags,
-          DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
-    }
-
-    String timeClause = DateTimeFormat.format(icuLocale, timeCalendar, FORMAT_SHOW_TIME,
-        DisplayContext.CAPITALIZATION_NONE);
-
-    // icu4j also has other options available to control the capitalization. We are currently using
-    // the _NONE option only.
-    DisplayContext capitalizationContext = DisplayContext.CAPITALIZATION_NONE;
-
-    // Combine the two clauses, such as '5 days ago, 10:50 AM'.
-    synchronized (CACHED_FORMATTERS) {
-      return getFormatter(icuLocale, style, capitalizationContext)
-              .combineDateAndTime(dateClause, timeClause);
-    }
-  }
-
-  /**
-   * getFormatter() caches the RelativeDateTimeFormatter instances based on
-   * the combination of localeName, sytle and capitalizationContext. It
-   * should always be used along with the action of the formatter in a
-   * synchronized block, because otherwise the formatter returned by
-   * getFormatter() may have been evicted by the time of the call to
-   * formatter->action().
-   */
-  private static android.icu.text.RelativeDateTimeFormatter getFormatter(
-      ULocale locale, android.icu.text.RelativeDateTimeFormatter.Style style,
-      DisplayContext displayContext) {
-    String key = locale + "\t" + style + "\t" + displayContext;
-    android.icu.text.RelativeDateTimeFormatter formatter = CACHED_FORMATTERS.get(key);
-    if (formatter == null) {
-      formatter = android.icu.text.RelativeDateTimeFormatter.getInstance(
-          locale, null, style, displayContext);
-      CACHED_FORMATTERS.put(key, formatter);
-    }
-    return formatter;
-  }
-
-  // Return the date difference for the two times in a given timezone.
-  private static int dayDistance(android.icu.util.TimeZone icuTimeZone, long startTime,
-      long endTime) {
-    return julianDay(icuTimeZone, endTime) - julianDay(icuTimeZone, startTime);
-  }
-
-  private static int julianDay(android.icu.util.TimeZone icuTimeZone, long time) {
-    long utcMs = time + icuTimeZone.getOffset(time);
-    return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
-  }
-}
diff --git a/luni/src/main/java/libcore/icu/TimeZoneNames.java b/luni/src/main/java/libcore/icu/TimeZoneNames.java
index ec0fb8e..8e27ca9 100644
--- a/luni/src/main/java/libcore/icu/TimeZoneNames.java
+++ b/luni/src/main/java/libcore/icu/TimeZoneNames.java
@@ -16,6 +16,7 @@
 
 package libcore.icu;
 
+import com.android.icu.text.TimeZoneNamesNative;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -51,14 +52,8 @@
         @Override protected String[][] create(Locale locale) {
             long start = System.nanoTime();
 
-            // Set up the 2D array used to hold the names. The first column contains the Olson ids.
-            String[][] result = new String[availableTimeZoneIds.length][5];
-            for (int i = 0; i < availableTimeZoneIds.length; ++i) {
-                result[i][0] = availableTimeZoneIds[i];
-            }
-
             long nativeStart = System.nanoTime();
-            fillZoneStrings(locale.toLanguageTag(), result);
+            String[][] result = TimeZoneNamesNative.getFilledZoneStrings(locale, availableTimeZoneIds);
             long nativeEnd = System.nanoTime();
 
             addOffsetStrings(result);
@@ -151,5 +146,16 @@
         return cachedZoneStrings.get(locale);
     }
 
-    private static native void fillZoneStrings(String locale, String[][] result);
+    /**
+     * A utility method to get display names in various {@param namesTypes} from
+     * ICU4J's {@param timeZoneNames}.
+     */
+    public static void getDisplayNames(android.icu.text.TimeZoneNames timeZoneNames, String tzId,
+            android.icu.text.TimeZoneNames.NameType[] nameTypes, long date, String[] names,
+            int namesOffset) {
+        for (int i = 0; i < nameTypes.length; i++) {
+            android.icu.text.TimeZoneNames.NameType nameType = nameTypes[i];
+            names[namesOffset + i] = timeZoneNames.getDisplayName(tzId, nameType, date);
+        }
+    }
 }
diff --git a/luni/src/main/java/libcore/internal/Java9LanguageFeatures.java b/luni/src/main/java/libcore/internal/Java9LanguageFeatures.java
index 7e201d0..7942307 100644
--- a/luni/src/main/java/libcore/internal/Java9LanguageFeatures.java
+++ b/luni/src/main/java/libcore/internal/Java9LanguageFeatures.java
@@ -27,7 +27,7 @@
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
- * Proof of concept / dummy code whose only purpose is to demonstrate that Java 9
+ * Proof of concept / fake code whose only purpose is to demonstrate that Java 9
  * language features are supported in libcore.
  */
 public class Java9LanguageFeatures {
diff --git a/luni/src/main/java/libcore/internal/StringPool.java b/luni/src/main/java/libcore/internal/StringPool.java
index 546a404..fccc354 100644
--- a/luni/src/main/java/libcore/internal/StringPool.java
+++ b/luni/src/main/java/libcore/internal/StringPool.java
@@ -23,12 +23,10 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
 public final class StringPool {
 
     private final String[] pool = new String[512];
 
-    @libcore.api.CorePlatformApi
     public StringPool() {
     }
 
@@ -47,7 +45,6 @@
     /**
      * Returns a string equal to {@code new String(array, start, length)}.
      */
-    @libcore.api.CorePlatformApi
     public String get(char[] array, int start, int length) {
         // Compute an arbitrary hash of the content
         int hashCode = 0;
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
old mode 100644
new mode 100755
index f3793d6..5b331b1
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -23,6 +23,7 @@
 import android.system.OsConstants;
 import android.system.StructAddrinfo;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPollfd;
 import android.system.StructStat;
 import android.system.StructStatVfs;
@@ -59,9 +60,11 @@
     }
 
     @Override public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException {
-        BlockGuard.getThreadPolicy().onNetwork();
+        if (!(isUnixSocket(fd) && isNonBlockingFile(fd))) {
+            BlockGuard.getThreadPolicy().onNetwork();
+        }
         final FileDescriptor acceptFd = super.accept(fd, peerAddress);
-        if (isInetSocket(acceptFd)) {
+        if (acceptFd != null && isInetSocket(acceptFd)) {
             tagSocket(acceptFd);
         }
         return acceptFd;
@@ -110,6 +113,22 @@
         super.close(fd);
     }
 
+    public static boolean isNonBlockingFile(FileDescriptor fd) throws ErrnoException {
+        int flag = android.system.Os.fcntlInt(fd, F_GETFL, 0);
+        if ((flag & O_NONBLOCK) != 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean isUnixSocket(FileDescriptor fd) throws ErrnoException {
+        return isUnixDomain(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_DOMAIN));
+    }
+
+    private static boolean isUnixDomain(int domain) {
+        return (domain == AF_UNIX);
+    }
+
     private static boolean isInetSocket(FileDescriptor fd) throws ErrnoException{
         return isInetDomain(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_DOMAIN));
     }
@@ -335,6 +354,11 @@
         return super.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
     }
 
+    @Override public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+        BlockGuard.getThreadPolicy().onNetwork();
+        return super.recvmsg(fd, msg, flags);
+    }
+
     @UnsupportedAppUsage
     @Override public void remove(String path) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
@@ -355,6 +379,11 @@
         return super.sendfile(outFd, inFd, offset, byteCount);
     }
 
+    @Override public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+        BlockGuard.getThreadPolicy().onNetwork();
+        return super.sendmsg(fd, msg, flags);
+    }
+
     @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
         BlockGuard.getThreadPolicy().onNetwork();
         return super.sendto(fd, buffer, flags, inetAddress, port);
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
old mode 100644
new mode 100755
index f9fb2d4..8320be0
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -16,6 +16,8 @@
 
 package libcore.io;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.system.ErrnoException;
 import android.system.GaiException;
 import android.system.Int32Ref;
@@ -26,6 +28,7 @@
 import android.system.StructGroupReq;
 import android.system.StructIfaddrs;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPasswd;
 import android.system.StructPollfd;
 import android.system.StructRlimit;
@@ -35,6 +38,7 @@
 import android.system.StructUcred;
 import android.system.StructUtsname;
 
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
@@ -45,200 +49,915 @@
 import java.nio.ByteBuffer;
 import java.util.Objects;
 
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
 /**
  * Subclass this if you want to override some {@link Os} methods but otherwise delegate.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public class ForwardingOs implements Os {
     @UnsupportedAppUsage
     private final Os os;
 
+    /**
+     * Constructs new {@link ForwardingOs}.
+     *
+     * @param os {@link Os} delegate for not overridden methods
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    protected ForwardingOs(Os os) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    protected ForwardingOs(@NonNull Os os) {
         this.os = Objects.requireNonNull(os);
     }
 
-    /** @return the delegate object passed to the constructor. */
+    /**
+     * @return the delegate object passed to the constructor.
+     *
+     * @hide
+     */
     protected final Os delegate() {
         return os;
     }
 
+    /**
+     *
+     * @hide
+     */
     public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
+
+    /**
+     * Checks whether the calling process can access the file
+     * {@code path}. If {@code path} is a symbolic link, it is dereferenced.
+     *
+     * The mode specifies the accessibility check(s) to be performed,
+     * and is either the value {@link android.system.OsConstants#F_OK},
+     * or a mask consisting of the bitwise OR of one or more of
+     * {@link android.system.OsConstants#R_OK}, {@link android.system.OsConstants#W_OK},
+     * and {@link android.system.OsConstants#X_OK}.
+     *
+     * {@link android.system.OsConstants#F_OK} tests for the
+     * existence of the file. {@link android.system.OsConstants#R_OK},
+     * {@link android.system.OsConstants#W_OK}, and {@link android.system.OsConstants#X_OK}
+     * test whether the file exists and grants read, write, and execute permissions, respectively.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/access.2.html">access(2)</a>.
+     *
+     * @param path path of the file to check access for
+     * @param mode accessibility checks mask
+     * @return {@code true} if file is accessible (all requested permissions granted,
+     *         or mode is {@link android.system.OsConstants#F_OK} and the file exists));
+     *         and throws otherwise
+     * @throws ErrnoException if at least one bit in mode asked for a permission that is denied,
+     *                        or mode is {@link android.system.OsConstants#F_OK} and the file
+     *                        does not exist, or some other error occurred. See the full list
+     *                        of errors in the "See Also" list.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+
+    /**
+     * @hide
+     */
+    public boolean access(@Nullable String path, int mode) throws ErrnoException { return os.access(path, mode); }
+
+    /**
+     * @hide
+     */
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); }
+
+    /**
+     * @hide
+     */
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
+
+    /**
+     * @hide
+     */
     public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.bind(fd, address); }
+
+    /**
+     * @hide
+     */
     @Override
     public StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException {
         return os.capget(hdr);
     }
+
+    /**
+     * @hide
+     */
     @Override
     public void capset(StructCapUserHeader hdr, StructCapUserData[] data) throws ErrnoException {
         os.capset(hdr, data);
     }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void chown(String path, int uid, int gid) throws ErrnoException { os.chown(path, uid, gid); }
+
+    /**
+     * @hide
+     */
     public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); }
+
+    /**
+     * @hide
+     */
     public void android_fdsan_exchange_owner_tag(FileDescriptor fd, long previousOwnerId, long newOwnerId) { os.android_fdsan_exchange_owner_tag(fd, previousOwnerId, newOwnerId); }
+
+    /**
+     * @hide
+     */
     public long android_fdsan_get_owner_tag(FileDescriptor fd) { return os.android_fdsan_get_owner_tag(fd); }
+
+    /**
+     * @hide
+     */
     public String android_fdsan_get_tag_type(long tag) { return os.android_fdsan_get_tag_type(tag); }
+
+    /**
+     * @hide
+     */
     public long android_fdsan_get_tag_value(long tag) { return os.android_fdsan_get_tag_value(tag); }
 
+    /**
+     * @hide
+     */
     public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.connect(fd, address, port); }
+
+    /**
+     * @hide
+     */
     public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException { os.connect(fd, address); }
+
+    /**
+     * @hide
+     */
     public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return os.dup(oldFd); }
+
+    /**
+     * @hide
+     */
     public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return os.dup2(oldFd, newFd); }
+
+    /**
+     * @hide
+     */
     public String[] environ() { return os.environ(); }
+
+    /**
+     * @hide
+     */
     public void execv(String filename, String[] argv) throws ErrnoException { os.execv(filename, argv); }
+
+    /**
+     * @hide
+     */
     public void execve(String filename, String[] argv, String[] envp) throws ErrnoException { os.execve(filename, argv, envp); }
+
+    /**
+     * @hide
+     */
     public void fchmod(FileDescriptor fd, int mode) throws ErrnoException { os.fchmod(fd, mode); }
+
+    /**
+     * @hide
+     */
     public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException { os.fchown(fd, uid, gid); }
+
+    /**
+     * @hide
+     */
     public int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException { return os.fcntlInt(fd, cmd, arg); }
+
+    /**
+     * @hide
+     */
     public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
+
+    /**
+     * @hide
+     */
     public void fdatasync(FileDescriptor fd) throws ErrnoException { os.fdatasync(fd); }
+
+    /**
+     * @hide
+     */
     public StructStat fstat(FileDescriptor fd) throws ErrnoException { return os.fstat(fd); }
+
+    /**
+     * @hide
+     */
     public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException { return os.fstatvfs(fd); }
+
+    /**
+     * @hide
+     */
     public void fsync(FileDescriptor fd) throws ErrnoException { os.fsync(fd); }
+
+    /**
+     * @hide
+     */
     public void ftruncate(FileDescriptor fd, long length) throws ErrnoException { os.ftruncate(fd, length); }
+
+    /**
+     * @hide
+     */
     public String gai_strerror(int error) { return os.gai_strerror(error); }
+
+    /**
+     * @hide
+     */
     public int getegid() { return os.getegid(); }
+
+    /**
+     * @hide
+     */
     public int geteuid() { return os.geteuid(); }
+
+    /**
+     * @hide
+     */
     public int getgid() { return os.getgid(); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public String getenv(String name) { return os.getenv(name); }
+
+    /**
+     * @hide
+     */
     public String getnameinfo(InetAddress address, int flags) throws GaiException { return os.getnameinfo(address, flags); }
+
+    /**
+     * @hide
+     */
     public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return os.getpeername(fd); }
+
+    /**
+     * @hide
+     */
     public int getpgid(int pid) throws ErrnoException { return os.getpgid(pid); }
+
+    /**
+     * @hide
+     */
     public int getpid() { return os.getpid(); }
+
+    /**
+     * @hide
+     */
     public int getppid() { return os.getppid(); }
+
+    /**
+     * @hide
+     */
     public StructPasswd getpwnam(String name) throws ErrnoException { return os.getpwnam(name); }
+
+    /**
+     * @hide
+     */
     public StructPasswd getpwuid(int uid) throws ErrnoException { return os.getpwuid(uid); }
+
+    /**
+     * @hide
+     */
     public StructRlimit getrlimit(int resource) throws ErrnoException { return os.getrlimit(resource); }
+
+    /**
+     * @hide
+     */
     public SocketAddress getsockname(FileDescriptor fd) throws ErrnoException { return os.getsockname(fd); }
+
+    /**
+     * @hide
+     */
     public int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptByte(fd, level, option); }
+
+    /**
+     * @hide
+     */
     public InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptInAddr(fd, level, option); }
+
+    /**
+     * @hide
+     */
     public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptInt(fd, level, option); }
+
+    /**
+     * @hide
+     */
     public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptLinger(fd, level, option); }
+
+    /**
+     * @hide
+     */
     public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptTimeval(fd, level, option); }
+
+    /**
+     * @hide
+     */
     public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptUcred(fd, level, option); }
+
+    /**
+     * @hide
+     */
     public int gettid() { return os.gettid(); }
+
+    /**
+     * @hide
+     */
     public int getuid() { return os.getuid(); }
+
+    /**
+     * @hide
+     */
     public byte[] getxattr(String path, String name) throws ErrnoException { return os.getxattr(path, name); }
+
+    /**
+     * @hide
+     */
     public StructIfaddrs[] getifaddrs() throws ErrnoException { return os.getifaddrs(); }
+
+    /**
+     * @hide
+     */
     public String if_indextoname(int index) { return os.if_indextoname(index); }
+
+    /**
+     * @hide
+     */
     public int if_nametoindex(String name) { return os.if_nametoindex(name); }
+
+    /**
+     * @hide
+     */
     public InetAddress inet_pton(int family, String address) { return os.inet_pton(family, address); }
-    public int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlFlags(fd, interfaceName); };
+
+    /**
+     * @hide
+     */
+    public int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlFlags(fd, interfaceName); }
+
+    /**
+     * @hide
+     */
     public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException { return os.ioctlInetAddress(fd, cmd, interfaceName); }
-    public int ioctlInt(FileDescriptor fd, int cmd, Int32Ref arg) throws ErrnoException { return os.ioctlInt(fd, cmd, arg); }
-    public int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlMTU(fd, interfaceName); };
+
+    /**
+     * @hide
+     */
+    public int ioctlInt(FileDescriptor fd, int cmd) throws ErrnoException { return os.ioctlInt(fd, cmd); }
+
+    /**
+     * @hide
+     */
+    public int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlMTU(fd, interfaceName); }
+
+    /**
+     * @hide
+     */
     public boolean isatty(FileDescriptor fd) { return os.isatty(fd); }
+
+    /**
+     * @hide
+     */
     public void kill(int pid, int signal) throws ErrnoException { os.kill(pid, signal); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void lchown(String path, int uid, int gid) throws ErrnoException { os.lchown(path, uid, gid); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void link(String oldPath, String newPath) throws ErrnoException { os.link(oldPath, newPath); }
+
+    /**
+     * @hide
+     */
     public void listen(FileDescriptor fd, int backlog) throws ErrnoException { os.listen(fd, backlog); }
+
+    /**
+     * @hide
+     */
     public String[] listxattr(String path) throws ErrnoException { return os.listxattr(path); }
+
+    /**
+     * @hide
+     */
     public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return os.lseek(fd, offset, whence); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); }
+
+    /**
+     * @hide
+     */
     public FileDescriptor memfd_create(String name, int flags) throws ErrnoException { return os.memfd_create(name, flags); }
+
+    /**
+     * @hide
+     */
     public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException { os.mincore(address, byteCount, vector); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void mkdir(String path, int mode) throws ErrnoException { os.mkdir(path, mode); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void mkfifo(String path, int mode) throws ErrnoException { os.mkfifo(path, mode); }
+
+    /**
+     * @hide
+     */
     public void mlock(long address, long byteCount) throws ErrnoException { os.mlock(address, byteCount); }
+
+    /**
+     * @hide
+     */
     public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException { return os.mmap(address, byteCount, prot, flags, fd, offset); }
+
+    /**
+     * @hide
+     */
     public void msync(long address, long byteCount, int flags) throws ErrnoException { os.msync(address, byteCount, flags); }
+
+    /**
+     * @hide
+     */
     public void munlock(long address, long byteCount) throws ErrnoException { os.munlock(address, byteCount); }
+
+    /**
+     * @hide
+     */
     public void munmap(long address, long byteCount) throws ErrnoException { os.munmap(address, byteCount); }
+
+    /**
+     * Opens the file specified by {@code path}.
+     *
+     * If the specified file does not exist, it may optionally (if
+     * {@link android.system.OsConstants#O_CREAT} is specified in flags)
+     * be created by {@link #open(String, int, int)}.
+     *
+     * The argument flags must include one of the following access
+     * modes: {@link android.system.OsConstants#O_RDONLY},
+     * {@link android.system.OsConstants#O_WRONLY}, or
+     * {@link android.system.OsConstants#O_RDWR}. These request opening the
+     * file read-only, write-only, or read/write, respectively.
+     *
+     * In addition, zero or more file creation flags and file status
+     * flags can be bitwise-or'd in flags. The file creation flags are
+     * {@link android.system.OsConstants#O_CLOEXEC}, {@link android.system.OsConstants#O_CREAT},
+     * {@link android.system.OsConstants#O_DIRECTORY}, {@link android.system.OsConstants#O_EXCL},
+     * {@link android.system.OsConstants#O_NOCTTY}, {@link android.system.OsConstants#O_NOFOLLOW},
+     * {@link android.system.OsConstants#O_TMPFILE}, and {@link android.system.OsConstants#O_TRUNC}.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>.
+     *
+     * @param path  path of the file to be opened
+     * @param flags bitmask of the access, file creation and file status flags
+     * @param mode  specifies the file mode bits to be applied when a new file is
+     *              created. If neither {@link android.system.OsConstants#O_CREAT}
+     *              nor {@link android.system.OsConstants#O_TMPFILE} is specified in
+     *              flags, then mode is ignored (and can thus be specified as 0, or simply omitted).
+     * @return {@link FileDescriptor} of an opened file
+     * @throws ErrnoException if requested access to the file is not allowed, or search
+     *                        permission is denied for one of the directories in the
+     *                        path prefix of {@code path}, or the file did not exist yet and
+     *                        write access to the parent directory is not allowed, or other error.
+     *                        See the full list of errors in the "See Also" list.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { return os.open(path, flags, mode); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public FileDescriptor open(@Nullable String path, int flags, int mode) throws ErrnoException { return os.open(path, flags, mode); }
+
+    /**
+     * @hide
+     */
     public FileDescriptor[] pipe2(int flags) throws ErrnoException { return os.pipe2(flags); }
+
+    /**
+     * @hide
+     */
     public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return os.poll(fds, timeoutMs); }
+
+    /**
+     * @hide
+     */
     public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { os.posix_fallocate(fd, offset, length); }
-    public int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException { return os.prctl(option, arg2, arg3, arg4, arg5); };
+
+    /**
+     * @hide
+     */
+    public int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException { return os.prctl(option, arg2, arg3, arg4, arg5); }
+
+    /**
+     * @hide
+     */
     public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, buffer, offset); }
+
+    /**
+     * @hide
+     */
     public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, bytes, byteOffset, byteCount, offset); }
+
+    /**
+     * @hide
+     */
     public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, buffer, offset); }
+
+    /**
+     * @hide
+     */
     public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
+
+    /**
+     * @hide
+     */
     public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.read(fd, buffer); }
+
+    /**
+     * @hide
+     */
     public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.read(fd, bytes, byteOffset, byteCount); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public String readlink(String path) throws ErrnoException { return os.readlink(path); }
+
+    /**
+     * @hide
+     */
     public String realpath(String path) throws ErrnoException { return os.realpath(path); }
+
+    /**
+     * @hide
+     */
     public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.readv(fd, buffers, offsets, byteCounts); }
+
+    /**
+     * @hide
+     */
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
+
+    /**
+     * @hide
+     */
     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); }
+
+    /**
+     * @hide
+     */
+    public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException { return os.recvmsg(fd, msg, flags); }
+
+    /**
+     * Deletes a name from the filesystem.
+     *
+     * If the removed name was the last link to a file and no processes
+     * have the file open, the file is deleted and the space it was
+     * using is made available for reuse.
+     *
+     * If the name was the last link to a file, but any processes still
+     * have the file open, the file will remain in existence until the
+     * last file descriptor referring to it is closed.
+     *
+     * If the name referred to a symbolic link, the link is removed.
+     *
+     * If the name referred to a socket, FIFO, or device, the name is
+     * removed, but processes which have the object open may continue to
+     * use it.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man3/remove.3.html">remove(3)</a>.
+     *
+     * @param path file to delete
+     * @throws ErrnoException if access to {@code path} is not allowed, an I/O error occurred.
+     *                        See the full list of errors in the "See Also" list.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public void remove(String path) throws ErrnoException { os.remove(path); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public void remove(@Nullable String path) throws ErrnoException { os.remove(path); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void removexattr(String path, String name) throws ErrnoException { os.removexattr(path, name); }
+
+    /**
+     * Renames a file, moving it between directories if required.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/rename.2.html">rename(2)</a>.
+     *
+     * @param oldPath file to be moved to a new location {@code newPath}
+     * @param newPath destination to move file {@code oldPath}
+     * @throws ErrnoException if write permission is denied for the directory containing
+     *                        {@code oldPath} or {@code newPath}, or, search permission is denied for
+     *                        one of the directories in the path prefix of {@code oldPath} or
+     *                        {@code newPath}, or {@code oldPath} is a directory and does not allow
+     *                        write permission. See the full list of errors in the "See Also" list.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public void rename(@Nullable String oldPath, @Nullable String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
+
+    /**
+     * @hide
+     */
     public long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, offset, byteCount); }
+
+    /**
+     * @hide
+     */
+    public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException { return os.sendmsg(fd, msg, flags); }
+
+    /**
+     * @hide
+     */
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
+
+    /**
+     * @hide
+     */
     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); }
+
+    /**
+     * @hide
+     */
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
+
+    /**
+     * @hide
+     */
     public void setegid(int egid) throws ErrnoException { os.setegid(egid); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void setenv(String name, String value, boolean overwrite) throws ErrnoException { os.setenv(name, value, overwrite); }
+
+    /**
+     * @hide
+     */
     public void seteuid(int euid) throws ErrnoException { os.seteuid(euid); }
+
+    /**
+     * @hide
+     */
     public void setgid(int gid) throws ErrnoException { os.setgid(gid); }
+
+    /**
+     * @hide
+     */
     public void setpgid(int pid, int pgid) throws ErrnoException { os.setpgid(pid, pgid); }
+
+    /**
+     * @hide
+     */
     public void setregid(int rgid, int egid) throws ErrnoException { os.setregid(rgid, egid); }
+
+    /**
+     * @hide
+     */
     public void setreuid(int ruid, int euid) throws ErrnoException { os.setreuid(ruid, euid); }
+
+    /**
+     * @hide
+     */
     public int setsid() throws ErrnoException { return os.setsid(); }
+
+    /**
+     * @hide
+     */
     public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptByte(fd, level, option, value); }
+
+    /**
+     * @hide
+     */
     public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { os.setsockoptIfreq(fd, level, option, value); }
+
+    /**
+     * @hide
+     */
     public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptInt(fd, level, option, value); }
+
+    /**
+     * @hide
+     */
     public void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptIpMreqn(fd, level, option, value); }
+
+    /**
+     * @hide
+     */
     public void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException { os.setsockoptGroupReq(fd, level, option, value); }
+
+    /**
+     * @hide
+     */
     public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException { os.setsockoptLinger(fd, level, option, value); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException { os.setsockoptTimeval(fd, level, option, value); }
+
+    /**
+     * @hide
+     */
     public void setuid(int uid) throws ErrnoException { os.setuid(uid); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException { os.setxattr(path, name, value, flags); }
+
+    /**
+     * @hide
+     */
     public void shutdown(FileDescriptor fd, int how) throws ErrnoException { os.shutdown(fd, how); }
+
+    /**
+     * @hide
+     */
     public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { return os.socket(domain, type, protocol); }
+
+    /**
+     * @hide
+     */
     public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException { os.socketpair(domain, type, protocol, fd1, fd2); }
+
+    /**
+     * @hide
+     */
     public long splice(FileDescriptor fdIn, Int64Ref offIn, FileDescriptor fdOut, Int64Ref offOut, long len, int flags) throws ErrnoException { return os.splice(fdIn, offIn, fdOut, offOut, len, flags); }
+
+    /**
+     * Returns information about a file.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/lstat.2.html">stat(2)</a>.
+     *
+     * @param path path to file to get info about
+     * @return {@link StructStat} containing information about the file
+     * @throws ErrnoException See the full list of errors in the "See Also" list.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public StructStat stat(String path) throws ErrnoException { return os.stat(path); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public @Nullable StructStat stat(@Nullable String path) throws ErrnoException { return os.stat(path); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public StructStatVfs statvfs(String path) throws ErrnoException { return os.statvfs(path); }
+
+    /**
+     * @hide
+     */
     public String strerror(int errno) { return os.strerror(errno); }
+
+    /**
+     * @hide
+     */
     public String strsignal(int signal) { return os.strsignal(signal); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void symlink(String oldPath, String newPath) throws ErrnoException { os.symlink(oldPath, newPath); }
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public long sysconf(int name) { return os.sysconf(name); }
+
+    /**
+     * @hide
+     */
     public void tcdrain(FileDescriptor fd) throws ErrnoException { os.tcdrain(fd); }
+
+    /**
+     * @hide
+     */
     public void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException { os.tcsendbreak(fd, duration); }
+
+    /**
+     * @hide
+     */
     public int umask(int mask) { return os.umask(mask); }
+
+    /**
+     * @hide
+     */
     public StructUtsname uname() { return os.uname(); }
+
+    /**
+     * Deletes a name from the filesystem.
+     *
+     * If the removed name was the last link to a file and no processes
+     * have the file open, the file is deleted and the space it was
+     * using is made available for reuse.
+     *
+     * If the name was the last link to a file, but any processes still
+     * have the file open, the file will remain in existence until the
+     * last file descriptor referring to it is closed.
+     *
+     * If the name referred to a symbolic link, the link is removed.
+     *
+     * If the name referred to a socket, FIFO, or device, the name is
+     * removed, but processes which have the object open may continue to
+     * use it.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/unlink.2.html">unlink(2)</a>.
+     *
+     * @param pathname file to unlink
+     * @throws ErrnoException if access to {@code pathname} is not allowed, an I/O error occurred.
+     *                        See the full list of errors in the "See Also" list.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public void unlink(String pathname) throws ErrnoException { os.unlink(pathname); }
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public void unlink(@Nullable String pathname) throws ErrnoException { os.unlink(pathname); }
+
+    /**
+     * @hide
+     */
     public void unsetenv(String name) throws ErrnoException { os.unsetenv(name); }
+
+    /**
+     * @hide
+     */
     public int waitpid(int pid, Int32Ref status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
+
+    /**
+     * @hide
+     */
     public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.write(fd, buffer); }
+
+    /**
+     * @hide
+     */
     public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.write(fd, bytes, byteOffset, byteCount); }
+
+    /**
+     * @hide
+     */
     public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.writev(fd, buffers, offsets, byteCounts); }
 
+
+    /**
+     * @hide
+     */
     public String toString() { return "ForwardingOs{os=" + os + "}"; }
 }
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index 0554d6d..2fb6d58 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -16,9 +16,9 @@
 
 package libcore.io;
 
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.system.ErrnoException;
-import android.system.Int32Ref;
 import android.system.StructGroupReq;
 import android.system.StructLinger;
 import android.system.StructPollfd;
@@ -37,6 +37,7 @@
 import java.net.NetworkInterface;
 import java.net.NoRouteToHostException;
 import java.net.PortUnreachableException;
+import java.net.Socket;
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.SocketOptions;
@@ -44,35 +45,76 @@
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.TimeUnit;
-import libcore.util.ArrayUtils;
 
+import libcore.util.ArrayUtils;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 import static android.system.OsConstants.*;
 
 /**
- * Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
+ * Collection of utility methods to work with blocking and non-blocking I/O that wrap raw POSIX
+ * system calls, e.g. {@link android.system.Os}. These wrappers are to signal other blocked I/O
+ * threads and avoid boilerplate code of routine error checks when using raw system calls.
+ *
+ * <p>
+ * For example, when using {@link Os#read(FileDescriptor, byte[], int, int)}, return value can
+ * contain:
+ * <ul>
+ *   <li>{@code 0} which means EOF</li>
+ *   <li>{@code N > 0} which means number of bytes read</li>
+ *   <li>{@code -1} which means error, and {@link ErrnoException} is thrown</li>
+ * </ul>
+ *
+ * <p>
+ * {@link ErrnoException} in its turn can be one of:
+ * <ul>
+ *   <li>{@link android.system.OsConstants#EAGAIN} which means the file descriptor refers to a file
+ *       or a socket, which has been marked nonblocking
+ *       ({@link android.system.OsConstants#O_NONBLOCK}), and the read would block</li>
+ *   <li>{@link android.system.OsConstants#EBADF} which means the file descriptor is not a valid
+ *       file descriptor or is not open for reading</li>
+ *   <li>{@link android.system.OsConstants#EFAULT} which means given buffer is outside accessible
+ *       address space</li>
+ *   <li>{@link android.system.OsConstants#EINTR} which means the call was interrupted by a signal
+ *       before any data was read</li>
+ *   <li>{@link android.system.OsConstants#EINVAL} which means the file descriptor is attached to
+ *       an object which is unsuitable for reading; or the file was opened with the
+ *       {@link android.system.OsConstants#O_DIRECT} flag, and either the address specified in
+ *       {@code buffer}, the value specified in {@code count}, or the file {@code offset} is not
+ *       suitably aligned</li>
+ *   <li>{@link android.system.OsConstants#EIO} which means I/O error happened</li>
+ *   <li>{@link android.system.OsConstants#EISDIR} which means the file descriptor refers to a
+ *       directory</li>
+ * </ul>
+ *
+ * All these errors require handling, and this class contains some wrapper methods that handle most
+ * common cases, making usage of system calls more user friendly.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class IoBridge {
 
     private IoBridge() {
     }
 
+    /** @hide */
     public static int available(FileDescriptor fd) throws IOException {
         try {
-            Int32Ref available = new Int32Ref(0);
-            Libcore.os.ioctlInt(fd, FIONREAD, available);
-            if (available.value < 0) {
+            int available = Libcore.os.ioctlInt(fd, FIONREAD);
+            if (available < 0) {
                 // If the fd refers to a regular file, the result is the difference between
                 // the file size and the file position. This may be negative if the position
                 // is past the end of the file. If the fd refers to a special file masquerading
                 // as a regular file, the result may be negative because the special file
                 // may appear to have zero size and yet a previous read call may have
                 // read some amount of data and caused the file position to be advanced.
-                available.value = 0;
+                available = 0;
             }
-            return available.value;
+            return available;
         } catch (ErrnoException errnoException) {
             if (errnoException.errno == ENOTTY) {
                 // The fd is unwilling to opine about its read buffer.
@@ -82,7 +124,7 @@
         }
     }
 
-
+    /** @hide */
     public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
         if (address instanceof Inet6Address) {
             Inet6Address inet6Address = (Inet6Address) address;
@@ -116,6 +158,8 @@
     /**
      * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
      * means this method won't throw SocketTimeoutException.
+     *
+     * @hide
      */
     public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
         try {
@@ -128,6 +172,8 @@
     /**
      * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
      * Use timeoutMs == 0 for a blocking connect with no timeout.
+     *
+     * @hide
      */
     public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
         try {
@@ -195,6 +241,8 @@
 
     /**
      * Constructs the message for an exception that the caller is about to throw.
+     *
+     * @hide
      */
     private static String createMessageForException(FileDescriptor fd, InetAddress inetAddress,
             int port, int timeoutMs, Exception causeOrNull) {
@@ -235,18 +283,31 @@
      * Closes the Unix file descriptor associated with the supplied file descriptor, resets the
      * internal int to -1, and sends a signal to any threads are currently blocking. In order for
      * the signal to be sent the blocked threads must have registered with the
-     * AsynchronousCloseMonitor before they entered the blocking operation. {@code fd} will be
+     * {@link AsynchronousCloseMonitor} before they entered the blocking operation. {@code fd} will be
      * invalid after this call.
      *
      * <p>This method is a no-op if passed a {@code null} or already-closed file descriptor.
+     *
+     * @param fd file descriptor to be closed
+     * @throws IOException if underlying system call fails with {@link ErrnoException}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void closeAndSignalBlockedThreads(FileDescriptor fd) throws IOException {
-        if (fd == null || !fd.valid()) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void closeAndSignalBlockedThreads(@NonNull FileDescriptor fd) throws IOException {
+        if (fd == null) {
             return;
         }
-        // fd is invalid after we call release.
+
+        // fd is invalid after we call release$.
+        // If multiple threads reach this point simultaneously, release$ is synchronized, so one
+        // of them will receive the old fd, and the rest will get an empty FileDescriptor.
         FileDescriptor oldFd = fd.release$();
+        if (!oldFd.valid()) {
+            return;
+        }
+
         AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
         try {
             Libcore.os.close(oldFd);
@@ -255,6 +316,7 @@
         }
     }
 
+    /** @hide */
     @UnsupportedAppUsage
     public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port,
             int timeoutMs, int remainingTimeoutMs) throws IOException {
@@ -288,14 +350,19 @@
     }
 
     // Socket options used by java.net but not exposed in SocketOptions.
+    /** @hide */
     public static final int JAVA_MCAST_JOIN_GROUP = 19;
+    /** @hide */
     public static final int JAVA_MCAST_LEAVE_GROUP = 20;
+    /** @hide */
     public static final int JAVA_IP_MULTICAST_TTL = 17;
+    /** @hide */
     public static final int JAVA_IP_TTL = 25;
 
     /**
      * java.net has its own socket options similar to the underlying Unix ones. We paper over the
      * differences here.
+     * @hide
      */
     public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
         try {
@@ -368,6 +435,8 @@
     /**
      * java.net has its own socket options similar to the underlying Unix ones. We paper over the
      * differences here.
+     *
+     * @hide
      */
     public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
         try {
@@ -467,17 +536,31 @@
     }
 
     /**
-     * java.io only throws FileNotFoundException when opening files, regardless of what actually
-     * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
-     * directories: POSIX says read-only is okay, but java.io doesn't even allow that.
+     * Wrapper for {@link Os#open(String, int, int)} that behaves similar to {@link java.io.File}.
+     * When a {@link java.io.File} is opened and there is an error, it throws
+     * {@link java.io.FileNotFoundException} regardless of what went wrong, when POSIX
+     * {@link Os#open(String, int, int)} throws more grained exceptions of what went wrong.
+     *
+     * <p>Additionally, attempt to open directory using {@link java.io.File} is also error, however
+     * POSIX {@link Os#open(String, int, int)} for read-only directories is not error.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>.
+     *
+     * @param path  path of the file to be opened
+     * @param flags bitmask of the access, file creation and file status flags
+     * @return {@link FileDescriptor} of an opened file
+     * @throws FileNotFoundException if there was error opening file under {@code path}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull FileDescriptor open(@NonNull String path, int flags) throws FileNotFoundException {
         FileDescriptor fd = null;
         try {
             fd = Libcore.os.open(path, flags, 0666);
             // Posix open(2) fails with EISDIR only if you ask for write permission.
-            // Java disallows reading directories too.
+            // Java disallows reading directories too.f
             if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
                 throw new ErrnoException("open", EISDIR);
             }
@@ -496,11 +579,25 @@
     }
 
     /**
-     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
-     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
+     * Wrapper for {@link Os#read(FileDescriptor, byte[], int, int)} that behaves similar to
+     * {@link java.io.FileInputStream#read(byte[], int, int)} and
+     * {@link java.io.FileReader#read(char[], int, int)} which interpret reading at {@code EOF} as
+     * error, when POSIX system call returns {@code 0} (and future reads return {@code -1}).
+     *
+     * <p>@see <a href="https://man7.org/linux/man-pages/man2/read.2.html">read(2)</a>.
+     *
+     * @param fd         file descriptor to read from
+     * @param bytes      buffer to put data read from {@code fd}
+     * @param byteOffset offset in {@code bytes} buffer to put read data at
+     * @param byteCount  number of bytes to read from {@code fd}
+     * @return           number of bytes read, if read operation was successful
+     * @throws IOException if underlying system call returned error
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static int read(@NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount) throws IOException {
         ArrayUtils.throwsIfOutOfBounds(bytes.length, byteOffset, byteCount);
         if (byteCount == 0) {
             return 0;
@@ -521,11 +618,28 @@
     }
 
     /**
-     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
-     * Unix it never just writes as many bytes as happens to be convenient.)
+     * Wrapper for {@link Os#write(FileDescriptor, byte[], int, int)} that behaves similar to
+     * {@link java.io.FileOutputStream#write(byte[], int, int)} and
+     * {@link java.io.FileWriter#write(char[], int, int)} which always either write all requested
+     * bytes, or fail with error; as opposed to POSIX write, when the number of bytes written may
+     * be less than {@code bytes}. This may happen, for example, if there is insufficient space on
+     * the underlying  physical medium, or the {@code RLIMIT_FSIZE} resource limit is encountered,
+     * or the call was interrupted by a signal handler after having written less than {@code bytes}
+     * bytes.
+     *
+     * <p>@see <a href="https://man7.org/linux/man-pages/man2/write.2.html">write(2)</a>.
+     *
+     * @param fd         file descriptor to write to
+     * @param bytes      buffer containing the data to be written
+     * @param byteOffset offset in {@code bytes} buffer to read written data from
+     * @param byteCount  number of bytes to write to {@code fd}
+     * @throws IOException if underlying system call returned error
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void write(@NonNull FileDescriptor fd,@NonNull  byte[] bytes, int byteOffset, int byteCount) throws IOException {
         ArrayUtils.throwsIfOutOfBounds(bytes.length, byteOffset, byteCount);
         if (byteCount == 0) {
             return;
@@ -541,8 +655,31 @@
         }
     }
 
-    @libcore.api.CorePlatformApi
-    public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws IOException {
+    /**
+     * Wrapper around {@link Os#sendto(FileDescriptor, byte[], int, int, int, InetAddress, int)}
+     * that allows sending data over both TCP and UDP socket; handles
+     * {@link android.system.OsConstants#EAGAIN} and {@link android.system.OsConstants#ECONNREFUSED}
+     * and behaves similar to and behaves similar to
+     * {@link java.net.DatagramSocket#send(DatagramPacket)} and
+     * {@link Socket#getOutputStream()#write(FileDescriptor, byte[], int, int)}.
+     *
+     * <p>See {@link android.system.OsConstants} for available flags.
+     *
+     * <p>@see <a href="https://man7.org/linux/man-pages/man2/send.2.html">send(2)</a>.
+     *
+     * @param fd          {@link FileDescriptor} of the socket to send data over
+     * @param bytes       byte buffer containing the data to be sent
+     * @param byteOffset  offset in {@code bytes} at which data to be sent starts
+     * @param byteCount   number of bytes to be sent
+     * @param flags       bitwise OR of zero or more of flags, like {@link android.system.OsConstants#MSG_DONTROUTE}
+     * @param inetAddress destination address
+     * @param port        destination port
+     * @return            number of bytes sent on success
+     * @throws IOException if underlying system call returned error
+     *
+     * @hide
+     */
+    public static int sendto(@NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount, int flags, @Nullable InetAddress inetAddress, int port) throws IOException {
         boolean isDatagram = (inetAddress != null);
         if (!isDatagram && byteCount <= 0) {
             return 0;
@@ -556,6 +693,7 @@
         return result;
     }
 
+    /** @hide */
     public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws IOException {
         boolean isDatagram = (inetAddress != null);
         if (!isDatagram && buffer.remaining() == 0) {
@@ -586,8 +724,32 @@
         throw errnoException.rethrowAsIOException();
     }
 
-    @libcore.api.CorePlatformApi
-    public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
+    /**
+     * Wrapper around {@link Os#recvfrom(FileDescriptor, byte[], int, int, int, InetSocketAddress)}
+     * that receives a message from both TCP and UDP socket; handles
+     * {@link android.system.OsConstants#EAGAIN} and {@link android.system.OsConstants#ECONNREFUSED}
+     * and behaves similar to {@link java.net.DatagramSocket#receive(DatagramPacket)} and
+     * {@link Socket#getInputStream()#recvfrom(boolean, FileDescriptor, byte[], int, int, int, DatagramPacket, boolean)}.
+     *
+     * <p>If {@code packet} is not {@code null}, and the underlying protocol provides the source
+     * address of the message, that source address is placed in the {@code packet}.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/recv.2.html">recv(2)</a>.
+     *
+     * @param isRead      {@code true} if some data been read already from {@code fd}
+     * @param fd          socket to receive data from
+     * @param bytes       buffer to put data read from {@code fd}
+     * @param byteOffset  offset in {@code bytes} buffer to put read data at
+     * @param byteCount   number of bytes to read from {@code fd}
+     * @param flags       bitwise OR of zero or more of flags, like {@link android.system.OsConstants#MSG_DONTROUTE}
+     * @param packet      {@link DatagramPacket} to fill with source address
+     * @param isConnected {@code true} if socket {@code fd} is connected
+     * @return            number of bytes read, if read operation was successful
+     * @throws IOException if underlying system call returned error
+     *
+     * @hide
+     */
+    public static int recvfrom(boolean isRead, @NonNull FileDescriptor fd, @NonNull byte[] bytes, int byteOffset, int byteCount, int flags, @Nullable DatagramPacket packet, boolean isConnected) throws IOException {
         int result;
         try {
             InetSocketAddress srcAddress = packet != null ? new InetSocketAddress() : null;
@@ -599,6 +761,7 @@
         return result;
     }
 
+    /** @hide */
     public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
         int result;
         try {
@@ -647,8 +810,34 @@
         }
     }
 
-    @libcore.api.CorePlatformApi
-    public static FileDescriptor socket(int domain, int type, int protocol) throws SocketException {
+    /**
+     * Creates an endpoint for communication and returns a file descriptor that refers
+     * to that endpoint.
+     *
+     * <p>The {@code domain} specifies a communication domain; this selects the protocol
+     * family which will be used for communication, e.g. {@link android.system.OsConstants#AF_UNIX}
+     * {@link android.system.OsConstants#AF_INET}.
+     *
+     * <p>The socket has the indicated type, which specifies the communication semantics,
+     * e.g. {@link android.system.OsConstants#SOCK_STREAM} or
+     * {@link android.system.OsConstants#SOCK_DGRAM}.
+     *
+     * <p>The protocol specifies a particular protocol to be used with the
+     * socket. Normally only a single protocol exists to support a
+     * particular socket type within a given protocol family, in which
+     * case protocol can be specified as {@code 0}.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/socket.2.html">socket(2)</a>.
+     *
+     * @param domain   socket domain
+     * @param type     socket type
+     * @param protocol socket protocol
+     * @return {@link FileDescriptor} of an opened socket
+     * @throws SocketException if underlying system call returned error
+     *
+     * @hide
+     */
+    public static @NonNull FileDescriptor socket(int domain, int type, int protocol) throws SocketException {
         FileDescriptor fd;
         try {
             fd = Libcore.os.socket(domain, type, protocol);
@@ -665,6 +854,8 @@
      *
      * @throws SocketException if poll(2) fails.
      * @throws SocketTimeoutException if the event has not happened before timeout period has passed.
+     *
+     * @hide
      */
     public static void poll(FileDescriptor fd, int events, int timeout)
             throws SocketException, SocketTimeoutException {
@@ -683,10 +874,17 @@
     }
 
     /**
-     * @throws SocketException if fd is not currently bound to an InetSocketAddress
+     * Returns the current address to which the socket {@code fd} is bound.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man2/getsockname.2.html">getsockname(2)</a>.
+     *
+     * @param fd socket to get the bounded address of
+     * @return current address to which the socket {@code fd} is bound
+     * @throws SocketException if {@code fd} is not currently bound to an {@link InetSocketAddress}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static InetSocketAddress getLocalInetSocketAddress(FileDescriptor fd)
+    public static @NonNull InetSocketAddress getLocalInetSocketAddress(@NonNull FileDescriptor fd)
             throws SocketException {
         try {
             SocketAddress socketAddress = Libcore.os.getsockname(fd);
diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java
index 4b4870b..6820868 100644
--- a/luni/src/main/java/libcore/io/IoUtils.java
+++ b/luni/src/main/java/libcore/io/IoUtils.java
@@ -16,6 +16,9 @@
 
 package libcore.io;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.system.ErrnoException;
 import android.system.StructStat;
@@ -29,7 +32,9 @@
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.Objects;
+
 import libcore.util.NonNull;
+import libcore.util.Nullable;
 
 import static android.system.OsConstants.F_GETFL;
 import static android.system.OsConstants.F_SETFL;
@@ -37,25 +42,30 @@
 import static android.system.OsConstants.O_RDONLY;
 
 /** @hide */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class IoUtils {
     private IoUtils() {
     }
 
     /**
-     * Acquires ownership of an integer file descriptor from a FileDescriptor.
+     * Acquires ownership of an integer file descriptor from a {@link FileDescriptor}.
      *
-     * This method invalidates the FileDescriptor passed in.
+     * This method invalidates the {@link FileDescriptor} passed in.
      *
      * The important part of this function is that you are taking ownership of a resource that you
      * must either clean up yourself, or hand off to some other object that does that for you.
      *
      * See bionic/include/android/fdsan.h for more details.
      *
-     * @param fd FileDescriptor to take ownership from, must be non-null.
+     * @param fd {@link FileDescriptor} to take ownership from, must be non-{@code null}.
+     * @return raw file descriptor
      * @throws NullPointerException if fd is null
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int acquireRawFd(@NonNull FileDescriptor fd) {
         Objects.requireNonNull(fd);
 
@@ -113,25 +123,35 @@
         // The owner ID is not required to be unique but should be stable and attempt to avoid
         // collision with identifiers generated both here and in native code (which are simply the
         // address of the owning object). identityHashCode(Object) meets these requirements.
-        long tagValue = System.identityHashCode(owner);
+        //
+        // If identityHashCode returns a negative int, it'll be sign-extended, so we need to apply
+        // a mask. fdsan uses bits 48-56 to distinguish between a generic native pointer and a
+        // generic Java type, but since we're only inserting 32-bits of data, we might as well mask
+        // off the entire upper 32 bits.
+        long mask = (1L << 32) - 1;
+        long tagValue = System.identityHashCode(owner) & mask;
         return tagType << 56 | tagValue;
     }
 
     /**
-     * Assigns ownership of an unowned FileDescriptor.
+     * Assigns ownership of an unowned {@link FileDescriptor}.
      *
-     * Associates the supplied FileDescriptor and the underlying Unix file descriptor with an owner
-     * ID derived from the supplied {@code owner} object. If the FileDescriptor already has an
+     * Associates the supplied {@link FileDescriptor} and the underlying Unix file descriptor with an owner
+     * ID derived from the supplied {@code owner} object. If the {@link FileDescriptor} already has an
      * associated owner an {@link IllegalStateException} will be thrown. If the underlying Unix
      * file descriptor already has an associated owner, the process will abort.
      *
      * See bionic/include/android/fdsan.h for more details.
      *
-     * @param fd FileDescriptor to take ownership from, must be non-null.
-     * @throws NullPointerException if fd or owner are null
-     * @throws IllegalStateException if fd is already owned
+     * @param fd    {@link FileDescriptor} to take ownership from, must be non-{@code null}.
+     * @param owner owner object
+     * @throws NullPointerException if {@code fd} or {@code owner} are {@code null}
+     * @throws IllegalStateException if {@code fd} is already owned
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setFdOwner(@NonNull FileDescriptor fd, @NonNull Object owner) {
         Objects.requireNonNull(fd);
         Objects.requireNonNull(owner);
@@ -150,20 +170,31 @@
     }
 
     /**
-     * Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null
-     * or invalid.
+     * Closes a file descriptor, so that it no longer refers to any file and may
+     * be reused. Also resets the internal int to -1.
+     *
+     * @param fd is {@link FileDescriptor} instance, invalid value is ignored.
+     * @throws IOException if an I/O error occurred.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void close(FileDescriptor fd) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void close(@Nullable FileDescriptor fd) throws IOException {
         IoBridge.closeAndSignalBlockedThreads(fd);
     }
 
     /**
-     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
+     * Closes {@link AutoClosable} instance, ignoring any checked exceptions.
+     *
+     * @param close is AutoClosable instance, null value is ignored.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static void closeQuietly(AutoCloseable closeable) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void closeQuietly(@Nullable AutoCloseable closeable) {
         if (closeable != null) {
             try {
                 closeable.close();
@@ -175,11 +206,16 @@
     }
 
     /**
-     * Closes 'fd', ignoring any exceptions. Does nothing if 'fd' is null or invalid.
+     * Calls {@link #close(FileDescriptor)}, ignoring any exceptions.
+     *
+     * @param fd is {@link FileDescriptor} instance, invalid value is ignored.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static void closeQuietly(FileDescriptor fd) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void closeQuietly(@Nullable FileDescriptor fd) {
         try {
             IoUtils.close(fd);
         } catch (IOException ignored) {
@@ -187,25 +223,39 @@
     }
 
     /**
-     * Closes 'socket', ignoring any exceptions. Does nothing if 'socket' is null.
+     * Closes socket, ignoring any exceptions.
+     *
+     * @param socket is {@link Socket} instance, {@code null} value is ignored.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static void closeQuietly(Socket socket) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void closeQuietly(@Nullable Socket socket) {
         if (socket != null) {
             try {
                 socket.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
             } catch (Exception ignored) {
             }
         }
     }
 
     /**
-     * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'.
+     * Sets file descriptor to be blocking or non-blocking.
+     *
+     * @param fd is {@link FileDescriptor} instance
+     * @param blocking is a boolean that defines whether fd should be blocking or non-blocking
+     * @throws IOException if system API call fails
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void setBlocking(@NonNull FileDescriptor fd, boolean blocking) throws IOException {
         try {
             int flags = Libcore.os.fcntlVoid(fd, F_GETFL);
             if (!blocking) {
@@ -220,20 +270,34 @@
     }
 
     /**
-     * Returns the contents of 'path' as a byte array.
+     * Returns the contents of {@code absolutePath} as a byte array.
+     *
+     * @param absolutePath path to a file to read
+     * @return contents of the file at {@code absolutePath} as byte array
+     * @throws IOException if there was I/O error
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static byte[] readFileAsByteArray(String absolutePath) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull byte[] readFileAsByteArray(@NonNull String absolutePath) throws IOException {
         return new FileReader(absolutePath).readFully().toByteArray();
     }
 
     /**
-     * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8.
+     * Returns the contents of {@code absolutePath} as a {@link String}. The contents are assumed to be UTF-8.
+     *
+     * @param absolutePath path to a file to read
+     * @return contents of the file at {@code absolutePath} as {@link String}
+     * @throws IOException if there was I/O error
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static String readFileAsString(String absolutePath) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull String readFileAsString(@NonNull String absolutePath) throws IOException {
         return new FileReader(absolutePath).readFully().toString(StandardCharsets.UTF_8);
     }
 
@@ -245,10 +309,10 @@
      * supposed to be best-effort.
      *
      * @deprecated Use {@link TestIoUtils#createTemporaryDirectory} instead.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    @Deprecated
-    public static void deleteContents(File dir) throws IOException {
+    public static void deleteContents(@NonNull File dir) throws IOException {
         File[] files = dir.listFiles();
         if (files != null) {
             for (File file : files) {
@@ -268,6 +332,8 @@
      * remove read permission from more directories. Everyone else should just open(2) and then
      * use the fd, but the loadLibrary API is broken by its need to ask ClassLoaders where to
      * find a .so rather than just calling dlopen(3).
+     *
+     * @hide
      */
     public static boolean canOpenReadOnly(String path) {
         try {
@@ -280,6 +346,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public static void throwInterruptedIoException() throws InterruptedIOException {
         // This is typically thrown in response to an
         // InterruptedException which does not leave the thread in an
diff --git a/luni/src/main/java/libcore/io/Linux.java b/luni/src/main/java/libcore/io/Linux.java
old mode 100644
new mode 100755
index 74608d3..4ecb785
--- a/luni/src/main/java/libcore/io/Linux.java
+++ b/luni/src/main/java/libcore/io/Linux.java
@@ -26,6 +26,7 @@
 import android.system.StructGroupReq;
 import android.system.StructIfaddrs;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPasswd;
 import android.system.StructPollfd;
 import android.system.StructRlimit;
@@ -111,7 +112,7 @@
     public native InetAddress inet_pton(int family, String address);
     public native int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException;
     public native InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
-    public native int ioctlInt(FileDescriptor fd, int cmd, Int32Ref arg) throws ErrnoException;
+    public native int ioctlInt(FileDescriptor fd, int cmd) throws ErrnoException;
     public native int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException;
     public native boolean isatty(FileDescriptor fd);
     public native void kill(int pid, int signal) throws ErrnoException;
@@ -210,10 +211,12 @@
         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, SocketException;
+    public native int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
     public native void remove(String path) throws ErrnoException;
     public native void removexattr(String path, String name) throws ErrnoException;
     public native void rename(String oldPath, String newPath) throws ErrnoException;
     public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException;
+    public native int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
         final int bytesSent;
         final int position = buffer.position();
diff --git a/luni/src/main/java/libcore/io/Memory.java b/luni/src/main/java/libcore/io/Memory.java
index 9e7f4ea..ac8ccb9 100644
--- a/luni/src/main/java/libcore/io/Memory.java
+++ b/luni/src/main/java/libcore/io/Memory.java
@@ -17,24 +17,33 @@
 
 package libcore.io;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
 
 import java.nio.ByteOrder;
 
 import dalvik.annotation.optimization.FastNative;
 
+import libcore.api.CorePlatformApi;
+import libcore.util.NonNull;
+
 /**
  * Unsafe access to memory.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+@SystemApi(client = MODULE_LIBRARIES)
 public final class Memory {
     private Memory() { }
 
     /**
      * Used to optimize nio heap buffer bulk get operations. 'dst' must be a primitive array.
      * 'dstOffset' is measured in units of 'sizeofElements' bytes.
+     *
+     * @hide
      */
     public static native void unsafeBulkGet(Object dst, int dstOffset, int byteCount,
             byte[] src, int srcOffset, int sizeofElements, boolean swap);
@@ -42,12 +51,25 @@
     /**
      * Used to optimize nio heap buffer bulk put operations. 'src' must be a primitive array.
      * 'srcOffset' is measured in units of 'sizeofElements' bytes.
+     * @hide
      */
     public static native void unsafeBulkPut(byte[] dst, int dstOffset, int byteCount,
             Object src, int srcOffset, int sizeofElements, boolean swap);
 
-    @libcore.api.CorePlatformApi
-    public static int peekInt(byte[] src, int offset, ByteOrder order) {
+    /**
+     * Gets int value from a byte buffer {@code src} at offset {@code offset} using
+     * {@code order} byte order.
+     *
+     * @param src    source byte buffer
+     * @param offset offset in {@code src} to get bytes from
+     * @param order  byte order
+     * @return int value
+     *
+     * @hide
+     */
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static int peekInt(@NonNull byte[] src, int offset, @NonNull ByteOrder order) {
         if (order == ByteOrder.BIG_ENDIAN) {
             return (((src[offset++] & 0xff) << 24) |
                     ((src[offset++] & 0xff) << 16) |
@@ -61,6 +83,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     public static long peekLong(byte[] src, int offset, ByteOrder order) {
         if (order == ByteOrder.BIG_ENDIAN) {
             int h = ((src[offset++] & 0xff) << 24) |
@@ -85,8 +110,20 @@
         }
     }
 
-    @libcore.api.CorePlatformApi
-    public static short peekShort(byte[] src, int offset, ByteOrder order) {
+    /**
+     * Gets short value from a byte buffer {@code src} at offset {@code offset} using
+     * {@code order} byte order.
+     *
+     * @param src    source byte buffer
+     * @param offset offset in {@code src} to get bytes from
+     * @param order  byte order
+     * @return short value
+     *
+     * @hide
+     */
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static short peekShort(@NonNull byte[] src, int offset, @NonNull ByteOrder order) {
         if (order == ByteOrder.BIG_ENDIAN) {
             return (short) ((src[offset] << 8) | (src[offset + 1] & 0xff));
         } else {
@@ -94,8 +131,20 @@
         }
     }
 
-    @libcore.api.CorePlatformApi
-    public static void pokeInt(byte[] dst, int offset, int value, ByteOrder order) {
+    /**
+     * Writes given int value {@code value} to a byte buffer {@code dst} using
+     * {@code order} byte order.
+     *
+     * @param dst    byte buffer where to write {@code value}
+     * @param offset offset in {@code dst} to put value to
+     * @param value  int value to write
+     * @param order  byte order
+     *
+     * @hide
+     */
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void pokeInt(@NonNull byte[] dst, int offset, int value, @NonNull ByteOrder order) {
         if (order == ByteOrder.BIG_ENDIAN) {
             dst[offset++] = (byte) ((value >> 24) & 0xff);
             dst[offset++] = (byte) ((value >> 16) & 0xff);
@@ -109,8 +158,20 @@
         }
     }
 
-    @libcore.api.CorePlatformApi
-    public static void pokeLong(byte[] dst, int offset, long value, ByteOrder order) {
+    /**
+     * Writes given long value {@code value} to a byte buffer {@code dst} using
+     * {@code order} byte order.
+     *
+     * @param dst    byte buffer where to write {@code value}
+     * @param offset offset in {@code dst} to put value to
+     * @param value  long value to write
+     * @param order  byte order
+     *
+     * @hide
+     */
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void pokeLong(@NonNull byte[] dst, int offset, long value, @NonNull ByteOrder order) {
         if (order == ByteOrder.BIG_ENDIAN) {
             int i = (int) (value >> 32);
             dst[offset++] = (byte) ((i >> 24) & 0xff);
@@ -136,8 +197,20 @@
         }
     }
 
-    @libcore.api.CorePlatformApi
-    public static void pokeShort(byte[] dst, int offset, short value, ByteOrder order) {
+    /**
+     * Writes given short value {@code value} to a byte buffer {@code dst} using
+     * {@code order} byte order.
+     *
+     * @param dst    byte buffer where to write {@code value}
+     * @param offset offset in {@code dst} to put value to
+     * @param value  short value to write
+     * @param order  byte order
+     *
+     * @hide
+     */
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static void pokeShort(@NonNull byte[] dst, int offset, short value, @NonNull ByteOrder order) {
         if (order == ByteOrder.BIG_ENDIAN) {
             dst[offset++] = (byte) ((value >> 8) & 0xff);
             dst[offset  ] = (byte) ((value >> 0) & 0xff);
@@ -148,22 +221,38 @@
     }
 
     /**
-     * Copies 'byteCount' bytes from the source to the destination. The objects are either
-     * instances of DirectByteBuffer or byte[]. The offsets in the byte[] case must include
-     * the Buffer.arrayOffset if the array came from a Buffer.array call. We could make this
-     * private and provide the four type-safe variants, but then ByteBuffer.put(ByteBuffer)
-     * would need to work out which to call based on whether the source and destination buffers
-     * are direct or not.
+     * Copies {@code byteCount} bytes from the source {@code srcObject} to the
+     * destination {@code dstObject}. The objects are either instances of
+     * {@code DirectByteBuffer} or {@code byte[]}. The offsets in the {@code byte[]}
+     * case must include the {@link Buffer#arrayOffset()} if the array came from a
+     * {@link Buffer#array()} call.
+     *
+     * <p>We could make this private and provide the four type-safe variants, but then
+     * {@link ByteBuffer#put(ByteBuffer)} would need to work out which to call based on
+     * whether the source and destination buffers are direct or not.
+     *
+     * @param dstObject destination buffer
+     * @param dstOffset offset in the destination buffer
+     * @param srcObject source buffer
+     * @param srcOffset offset in the source buffer
+     * @param byteCount number of bytes to copy
      *
      * @hide make type-safe before making public?
      */
-    @libcore.api.CorePlatformApi
-    public static native void memmove(Object dstObject, int dstOffset, Object srcObject, int srcOffset, long byteCount);
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static native void memmove(@NonNull Object dstObject, int dstOffset, @NonNull Object srcObject, int srcOffset, long byteCount);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     @FastNative
     public static native byte peekByte(long address);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static int peekInt(long address, boolean swap) {
         int result = peekIntNative(address);
@@ -175,6 +264,9 @@
     @FastNative
     private static native int peekIntNative(long address);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static long peekLong(long address, boolean swap) {
         long result = peekLongNative(address);
@@ -186,6 +278,9 @@
     @FastNative
     private static native long peekLongNative(long address);
 
+    /**
+     * @hide
+     */
     public static short peekShort(long address, boolean swap) {
         short result = peekShortNative(address);
         if (swap) {
@@ -196,19 +291,52 @@
     @FastNative
     private static native short peekShortNative(long address);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static native void peekByteArray(long address, byte[] dst, int dstOffset, int byteCount);
+
+    /**
+     * @hide
+     */
     public static native void peekCharArray(long address, char[] dst, int dstOffset, int charCount, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void peekDoubleArray(long address, double[] dst, int dstOffset, int doubleCount, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void peekFloatArray(long address, float[] dst, int dstOffset, int floatCount, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void peekIntArray(long address, int[] dst, int dstOffset, int intCount, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void peekLongArray(long address, long[] dst, int dstOffset, int longCount, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void peekShortArray(long address, short[] dst, int dstOffset, int shortCount, boolean swap);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     @FastNative
     public static native void pokeByte(long address, byte value);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void pokeInt(long address, int value, boolean swap) {
         if (swap) {
@@ -219,6 +347,9 @@
     @FastNative
     private static native void pokeIntNative(long address, int value);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void pokeLong(long address, long value, boolean swap) {
         if (swap) {
@@ -229,6 +360,9 @@
     @FastNative
     private static native void pokeLongNative(long address, long value);
 
+    /**
+     * @hide
+     */
     public static void pokeShort(long address, short value, boolean swap) {
         if (swap) {
             value = Short.reverseBytes(value);
@@ -238,12 +372,39 @@
     @FastNative
     private static native void pokeShortNative(long address, short value);
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static native void pokeByteArray(long address, byte[] src, int offset, int count);
+
+    /**
+     * @hide
+     */
     public static native void pokeCharArray(long address, char[] src, int offset, int count, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void pokeDoubleArray(long address, double[] src, int offset, int count, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void pokeFloatArray(long address, float[] src, int offset, int count, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void pokeIntArray(long address, int[] src, int offset, int count, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void pokeLongArray(long address, long[] src, int offset, int count, boolean swap);
+
+    /**
+     * @hide
+     */
     public static native void pokeShortArray(long address, short[] src, int offset, int count, boolean swap);
 }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
old mode 100644
new mode 100755
index 4953186..0c58455
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -16,6 +16,8 @@
 
 package libcore.io;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.system.ErrnoException;
 import android.system.GaiException;
 import android.system.Int32Ref;
@@ -26,6 +28,7 @@
 import android.system.StructGroupReq;
 import android.system.StructIfaddrs;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPasswd;
 import android.system.StructPollfd;
 import android.system.StructRlimit;
@@ -35,6 +38,7 @@
 import android.system.StructUcred;
 import android.system.StructUtsname;
 
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
@@ -44,164 +48,735 @@
 import java.net.SocketException;
 import java.nio.ByteBuffer;
 
-/** @hide */
-@libcore.api.CorePlatformApi
+/**
+ * Linux-like operating system. The user of this interface has access to various methods
+ * that expose basic operating system functionality, like file and file descriptors operations
+ * (open, close, read, write), socket operations (connect, bind, send*, recv*), process
+ * operations (exec*, getpid), filesystem operations (mkdir, unlink, chmod, chown) and others.
+ *
+ * @see Linux
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public interface Os {
+
+    /**
+     * @hide
+     */
     public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public boolean access(String path, int mode) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
+
+    /**
+     * @hide
+     */
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public void bind(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public StructCapUserData[] capget(StructCapUserHeader hdr) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void capset(StructCapUserHeader hdr, StructCapUserData[] data) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void chmod(String path, int mode) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void chown(String path, int uid, int gid) throws ErrnoException;
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void close(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void android_fdsan_exchange_owner_tag(FileDescriptor fd, long previousOwnerId, long newOwnerId);
+
+    /**
+     * @hide
+     */
     public long android_fdsan_get_owner_tag(FileDescriptor fd);
+
+    /**
+     * @hide
+     */
     public String android_fdsan_get_tag_type(long tag);
+
+    /**
+     * @hide
+     */
     public long android_fdsan_get_tag_value(long tag);
 
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public void connect(FileDescriptor fd, SocketAddress address) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public String[] environ();
+
+    /**
+     * @hide
+     */
     public void execv(String filename, String[] argv) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void execve(String filename, String[] argv, String[] envp) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void fchmod(FileDescriptor fd, int mode) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void fchown(FileDescriptor fd, int uid, int gid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void fdatasync(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructStat fstat(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void fsync(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void ftruncate(FileDescriptor fd, long length) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public String gai_strerror(int error);
+
+    /**
+     * @hide
+     */
     public int getegid();
+
+    /**
+     * @hide
+     */
     public int geteuid();
+
+    /**
+     * @hide
+     */
     public int getgid();
+
+    /**
+     * @hide
+     */
     public String getenv(String name);
+
     /* TODO: break into getnameinfoHost and getnameinfoService? */
+    /**
+     * @hide
+     */
     public String getnameinfo(InetAddress address, int flags) throws GaiException;
+
+    /**
+     * @hide
+     */
     public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int getpgid(int pid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int getpid();
+
+    /**
+     * @hide
+     */
     public int getppid();
+
+    /**
+     * @hide
+     */
     public StructPasswd getpwnam(String name) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructPasswd getpwuid(int uid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructRlimit getrlimit(int resource) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public SocketAddress getsockname(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int gettid();
+
+    /**
+     * @hide
+     */
     public int getuid();
+
+    /**
+     * @hide
+     */
     public byte[] getxattr(String path, String name) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructIfaddrs[] getifaddrs() throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public String if_indextoname(int index);
+
+    /**
+     * @hide
+     */
     public int if_nametoindex(String name);
+
+    /**
+     * @hide
+     */
     public InetAddress inet_pton(int family, String address);
+
+    /**
+     * @hide
+     */
     public int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
-    public int ioctlInt(FileDescriptor fd, int cmd, Int32Ref arg) throws ErrnoException;
+
+    /**
+     * @hide
+     */
+    public int ioctlInt(FileDescriptor fd, int cmd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public boolean isatty(FileDescriptor fd);
+
+    /**
+     * @hide
+     */
     public void kill(int pid, int signal) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void lchown(String path, int uid, int gid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void link(String oldPath, String newPath) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void listen(FileDescriptor fd, int backlog) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public String[] listxattr(String path) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructStat lstat(String path) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public FileDescriptor memfd_create(String name, int flags) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void mkdir(String path, int mode) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void mkfifo(String path, int mode) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void mlock(long address, long byteCount) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void msync(long address, long byteCount, int flags) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void munlock(long address, long byteCount) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void munmap(long address, long byteCount) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public FileDescriptor[] pipe2(int flags) throws ErrnoException;
+
     /* TODO: if we used the non-standard ppoll(2) behind the scenes, we could take a long timeout. */
+    /**
+     * @hide
+     */
     public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int prctl(int option, long arg2, long arg3, long arg4, long arg5) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public String readlink(String path) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public String realpath(String path) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
+    public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void remove(String path) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void removexattr(String path, String name) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void rename(String oldPath, String newPath) throws ErrnoException;
+
+    /**
+     * @hide
+     */
+    public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException;
+
+    /**
+     * @hide
+     */
     public long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setegid(int egid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void setenv(String name, String value, boolean overwrite) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void seteuid(int euid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setgid(int gid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setpgid(int pid, int pgid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setregid(int rgid, int egid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setreuid(int ruid, int euid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int setsid() throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setuid(int uid) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void setxattr(String path, String name, byte[] value, int flags) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void shutdown(FileDescriptor fd, int how) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void socketpair(int domain, int type, int protocol, FileDescriptor fd1, FileDescriptor fd2) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public long splice(FileDescriptor fdIn, Int64Ref offIn, FileDescriptor fdOut, Int64Ref offOut, long len, int flags) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public StructStat stat(String path) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public StructStatVfs statvfs(String path) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public String strerror(int errno);
+
+    /**
+     * @hide
+     */
     public String strsignal(int signal);
+
+    /**
+     * @hide
+     */
     public void symlink(String oldPath, String newPath) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public long sysconf(int name);
+
+    /**
+     * @hide
+     */
     public void tcdrain(FileDescriptor fd) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void tcsendbreak(FileDescriptor fd, int duration) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int umask(int mask);
+
+    /**
+     * @hide
+     */
     public StructUtsname uname();
+
+    /**
+     * @hide
+     */
     public void unlink(String pathname) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public void unsetenv(String name) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int waitpid(int pid, Int32Ref status, int options) throws ErrnoException;
+
+    /**
+     * @hide
+     */
     public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
+
+    /**
+     * @hide
+     */
     public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
 
     /**
@@ -211,16 +786,22 @@
      * @param expect the expected value.
      * @param update the new value to set; must not be null.
      * @return whether the update was successful.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean compareAndSetDefault(Os expect, Os update) {
         return Libcore.compareAndSetOs(expect, update);
     }
 
     /**
      * @return the system's default {@link Os} implementation currently in use.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static Os getDefault() {
         return Libcore.getOs();
     }
diff --git a/luni/src/main/java/libcore/io/Streams.java b/luni/src/main/java/libcore/io/Streams.java
index f83f5c1..6002118 100644
--- a/luni/src/main/java/libcore/io/Streams.java
+++ b/luni/src/main/java/libcore/io/Streams.java
@@ -16,6 +16,9 @@
 
 package libcore.io;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.ByteArrayOutputStream;
@@ -27,46 +30,75 @@
 import java.io.StringWriter;
 import java.util.concurrent.atomic.AtomicReference;
 import libcore.util.ArrayUtils;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
 
-/** @hide */
-@libcore.api.CorePlatformApi
+/**
+ * Convenience class for reading and writing streams of bytes.
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final class Streams {
     private static AtomicReference<byte[]> skipBuffer = new AtomicReference<byte[]>();
 
     private Streams() {}
 
     /**
-     * Implements InputStream.read(int) in terms of InputStream.read(byte[], int, int).
-     * InputStream assumes that you implement InputStream.read(int) and provides default
+     * Implements {@link InputStream#read(int)} in terms of {@link InputStream#read(byte[], int, int)}.
+     * {@link InputStream} assumes that you implement {@link InputStream#read(int)} and provides default
      * implementations of the others, but often the opposite is more efficient.
+     *
+     * @param in {@link InputStream} to read byte from
+     * @return singlge byte read from {@code in}
+     * @throws IOException in case of I/O error
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static int readSingleByte(InputStream in) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static int readSingleByte(@NonNull InputStream in) throws IOException {
         byte[] buffer = new byte[1];
         int result = in.read(buffer, 0, 1);
         return (result != -1) ? buffer[0] & 0xff : -1;
     }
 
     /**
-     * Implements OutputStream.write(int) in terms of OutputStream.write(byte[], int, int).
-     * OutputStream assumes that you implement OutputStream.write(int) and provides default
+     * Implements {@link OutputStream#write(int)} in terms of {@link OutputStream#write(byte[], int, int)}.
+     * {@link OutputStream} assumes that you implement {@link OutputStream#write(int)} and provides default
      * implementations of the others, but often the opposite is more efficient.
+     *
+     * @param out {@link OutputStream} to write byte to
+     * @param b byte to write to stream {@code out}
+     * @throws IOException in case of I/O error
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static void writeSingleByte(OutputStream out, int b) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void writeSingleByte(@NonNull OutputStream out, int b) throws IOException {
         byte[] buffer = new byte[1];
         buffer[0] = (byte) (b & 0xff);
         out.write(buffer);
     }
 
     /**
-     * Fills 'dst' with bytes from 'in', throwing EOFException if insufficient bytes are available.
+     * Fills byte buffer {@code dst} with bytes from {@link InputStream} {@code in}, throwing
+     * {@link EOFException} if insufficient bytes are available.
+     *
+     * @param in {@link InputStream} to read data from
+     * @param dst byte buffer to write data to
+     * @throws IOException in case of I/O error
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static void readFully(InputStream in, byte[] dst) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void readFully(@NonNull InputStream in, @NonNull byte[] dst) throws IOException {
         readFully(in, dst, 0, dst.length);
     }
 
@@ -75,6 +107,8 @@
      * EOFException if insufficient bytes are available.
      *
      * Used to implement {@link java.io.DataInputStream#readFully(byte[], int, int)}.
+     *
+     * @hide
      */
     public static void readFully(InputStream in, byte[] dst, int offset, int byteCount) throws IOException {
         if (byteCount == 0) {
@@ -98,11 +132,19 @@
     }
 
     /**
-     * Returns a byte[] containing the remainder of 'in', closing it when done.
+     * Returns a {@code byte[]} containing the remainder of {@code in} stream and
+     * closes it. Also see {@link #readFullyNoClose(InputStream)}.
+     *
+     * @param in {@link InputStream} to read data from
+     * @return remaining bytes in {@code in} stream.
+     * @throws IOException thrown by {@link InputStream#read(byte[])}.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static byte[] readFully(InputStream in) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull byte[] readFully(@NonNull InputStream in) throws IOException {
         try {
             return readFullyNoClose(in);
         } finally {
@@ -111,10 +153,18 @@
     }
 
     /**
-     * Returns a byte[] containing the remainder of 'in'.
+     * Returns a {@code byte[]} containing the remainder of {@code in} stream, without
+     * closing it.
+     *
+     * @param in {@link InputStream} to read data from
+     * @return remaining bytes in {@code in} stream.
+     * @throws IOException thrown by {@link InputStream#read(byte[])}.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static byte[] readFullyNoClose(InputStream in) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull byte[] readFullyNoClose(@NonNull InputStream in) throws IOException {
         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
         byte[] buffer = new byte[1024];
         int count;
@@ -125,10 +175,17 @@
     }
 
     /**
-     * Returns the remainder of 'reader' as a string, closing it when done.
+     * Reads the remainder of {@code reader} as a string, closing it when done.
+     *
+     * @param reader {@link Reader} instance.
+     * @return remainder of {@code reader} as {@link String}.
+     * @throws IOException thrown by {@link Reader#read(java.nio.CharBuffer)}.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static String readFully(Reader reader) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull String readFully(@NonNull Reader reader) throws IOException {
         try {
             StringWriter writer = new StringWriter();
             char[] buffer = new char[1024];
@@ -142,6 +199,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
     public static void skipAll(InputStream in) throws IOException {
         do {
@@ -160,9 +220,17 @@
      * other threads. A thread-local buffer is also insufficient because some
      * streams may call other streams in their skip() method, also clobbering the
      * buffer.
+     *
+     * @param in {@link InputStream} to skip data from
+     * @param byteCount number of bytes to skip from {@code in}
+     * @return number of bytes skipped from {@code in}
+     * @throws IOException in case of I/O error
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static long skipByReading(InputStream in, long byteCount) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static long skipByReading(@NonNull InputStream in, long byteCount) throws IOException {
         // acquire the shared skip buffer.
         byte[] buffer = skipBuffer.getAndSet(null);
         if (buffer == null) {
@@ -189,12 +257,20 @@
     }
 
     /**
-     * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
-     * Returns the total number of bytes transferred.
+     * Copies all of the bytes from {@code in} to {@code out}. Neither stream is
+     * closed.
+     *
+     * @param in {@link InputStream} to copy data from
+     * @param out {@link InputStream} to write copied data to
+     * @return the total number of bytes transferred.
+     * @throws IOException reading from {@link InputStream} or writing to {@link OutputStream}.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
-    public static int copy(InputStream in, OutputStream out) throws IOException {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static int copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
         int total = 0;
         byte[] buffer = new byte[8192];
         int c;
@@ -211,6 +287,8 @@
      *
      * @throws java.io.EOFException if the stream is exhausted before the next newline
      *     character.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public static String readAsciiLine(InputStream in) throws IOException {
diff --git a/luni/src/main/java/libcore/math/NativeBN.java b/luni/src/main/java/libcore/math/NativeBN.java
new file mode 100644
index 0000000..8b2ea0f
--- /dev/null
+++ b/luni/src/main/java/libcore/math/NativeBN.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.math;
+
+/**
+ * @hide
+ */
+public final class NativeBN {
+
+    public static native long BN_new();
+    // BIGNUM *BN_new(void);
+
+    public static native void BN_free(long a);
+    // void BN_free(BIGNUM *a);
+
+    public static native void litEndInts2bn(int[] ints, int len, boolean neg, long ret);
+
+    // Generates a minimal length representation of |a| in a sequence of integers, least-significant
+    // word at index 0.
+    public static native int[] bn2litEndInts(long a);
+
+    public static native void BN_mul(long r, long a, long b);
+    // int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx);
+
+    public static native void BN_div(long dv, long rem, long num, long divisor);
+    // int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *num, const BIGNUM *divisor, BN_CTX *ctx);
+
+    public static native void BN_mod_exp(long r, long a, long p, long m);
+    // int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx);
+}
diff --git a/luni/src/main/java/libcore/net/InetAddressUtils.java b/luni/src/main/java/libcore/net/InetAddressUtils.java
index 9da4a70..a3860b5 100644
--- a/luni/src/main/java/libcore/net/InetAddressUtils.java
+++ b/luni/src/main/java/libcore/net/InetAddressUtils.java
@@ -15,6 +15,10 @@
  */
 package libcore.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import android.system.GaiException;
 import android.system.StructAddrinfo;
 import java.net.Inet4Address;
@@ -30,7 +34,8 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public class InetAddressUtils {
 
     private static final int NETID_UNSET = 0;
@@ -50,8 +55,11 @@
      *
      * @param address the address to parse.
      * @return true if the supplied address is numeric, false otherwise.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean isNumericAddress(String address) {
         return parseNumericAddressNoThrow(address) != null;
     }
@@ -68,8 +76,11 @@
      * @param address the address to parse, must be numeric.
      * @return an {@link InetAddress} instance corresponding to the address.
      * @throws IllegalArgumentException if {@code address} is not a numeric address.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static InetAddress parseNumericAddress(String address) {
         InetAddress result = parseNumericAddressNoThrow(address);
         if (result == null) {
@@ -78,6 +89,9 @@
         return result;
     }
 
+    /**
+     * @hide
+     */
     public static InetAddress parseNumericAddressNoThrow(String address) {
         StructAddrinfo hints = new StructAddrinfo();
         hints.ai_flags = AI_NUMERICHOST;
@@ -95,6 +109,8 @@
     /**
      * Like {@link #parseNumericAddressNoThrow(String)}}, but strips optional []
      * around a numeric IPv6 address.
+     *
+     * @hide
      */
     public static InetAddress parseNumericAddressNoThrowStripOptionalBrackets(String address) {
         // Accept IPv6 addresses (only) in square brackets for compatibility.
diff --git a/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java
index 1ce58d9..ab4678e 100644
--- a/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java
+++ b/luni/src/main/java/libcore/net/NetworkSecurityPolicy.java
@@ -16,6 +16,9 @@
 
 package libcore.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
@@ -25,29 +28,55 @@
  * Android framework API should be accessing this policy via the framework's
  * {@code android.security.NetworkSecurityPolicy} instead of via this class.
  *
- * <p>The policy currently consists of a single flag: whether cleartext network traffic is
- * permitted. See {@link #isCleartextTrafficPermitted()}.
+ * <p>The policy can be determined by the {@link #isCleartextTrafficPermitted()},
+ * {@link #isCleartextTrafficPermitted(String)} and
+ * {@link #isCertificateTransparencyVerificationRequired(String)} methods.
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.IntraCoreApi
 public abstract class NetworkSecurityPolicy {
 
     private static volatile NetworkSecurityPolicy instance = new DefaultNetworkSecurityPolicy();
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Constructs a default {@code NetworkSecurityPolicy}.
+     *
+     * @see {@link #DefaultNetworkSecurityPolicy}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public NetworkSecurityPolicy() {
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Gets current singleton {@code NetworkSecurityPolicy} instance.
+     *
+     * @return the current {@code NetworkSecurityPolicy}.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public static NetworkSecurityPolicy getInstance() {
         return instance;
     }
 
-    @libcore.api.CorePlatformApi
+    /**
+     * Sets current singleton instance
+     *
+     * @param policy new {@code NetworlSecurityPolicy} instance.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static void setInstance(NetworkSecurityPolicy policy) {
         if (policy == null) {
             throw new NullPointerException("policy == null");
@@ -73,9 +102,14 @@
      * can be made to honor this flag. Platform-provided network stacks (e.g. HTTP and FTP) honor
      * this flag from day one, and well-established third-party network stacks will eventually
      * honor it.
+     *
+     * @return {@code true} if cleartext traffic is permitted and {@code false} otherwise.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public abstract boolean isCleartextTrafficPermitted();
 
     /**
@@ -84,8 +118,14 @@
      * process.
      *
      * <p>See {@link #isCleartextTrafficPermitted} for more details.
+     *
+     * @param hostname hostname to check if cleartext traffic is permitted for
+     * @return {@code true} if cleartext traffic is permitted and {@code false} otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public abstract boolean isCleartextTrafficPermitted(String hostname);
 
     /**
@@ -93,11 +133,25 @@
      * the server and verified by the client in TLS connections to {@code hostname}.
      *
      * <p>See RFC6962 section 3.3 for more details.
+     *
+     * @param hostname hostname to check whether certificate transparency verification
+     *                 is required
+     * @return {@code true} if certificate transparency verification is required and
+     *         {@code false} otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public abstract boolean isCertificateTransparencyVerificationRequired(String hostname);
 
+    /**
+     * Default network security policy that allows cleartext traffic and does not require
+     * certificate transparency verification.
+     *
+     * @hide
+     */
     public static final class DefaultNetworkSecurityPolicy extends NetworkSecurityPolicy {
         @Override
         public boolean isCleartextTrafficPermitted() {
diff --git a/luni/src/main/java/libcore/net/event/NetworkEventDispatcher.java b/luni/src/main/java/libcore/net/event/NetworkEventDispatcher.java
index 4c2acee..4324a61 100644
--- a/luni/src/main/java/libcore/net/event/NetworkEventDispatcher.java
+++ b/luni/src/main/java/libcore/net/event/NetworkEventDispatcher.java
@@ -16,6 +16,9 @@
 
 package libcore.net.event;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -25,8 +28,9 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
-public class NetworkEventDispatcher {
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+public final class NetworkEventDispatcher {
 
   private static final NetworkEventDispatcher instance = new NetworkEventDispatcher();
 
@@ -35,20 +39,27 @@
 
   /**
    * Returns the shared {@link NetworkEventDispatcher} instance.
+   *
+   * @return singleton instance of {@link NetworkEventDispatcher}
+   *
+   * @hide
    */
   @UnsupportedAppUsage
-  @libcore.api.CorePlatformApi
+  @SystemApi(client = MODULE_LIBRARIES)
+  @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
   public static NetworkEventDispatcher getInstance() {
     return instance;
   }
 
-  /** Visible for testing. Use {@link #getInstance()} instead. */
-  protected NetworkEventDispatcher() {
+  /** @hide Visible for testing. Use {@link #getInstance()} instead. */
+  public NetworkEventDispatcher() {
   }
 
   /**
    * Registers a listener to be notified when network events occur.
    * It can be deregistered using {@link #removeListener(NetworkEventListener)}
+   *
+   * @hide
    */
   @UnsupportedAppUsage
   public void addListener(NetworkEventListener toAdd) {
@@ -61,6 +72,8 @@
   /**
    * De-registers a listener previously added with {@link #addListener(NetworkEventListener)}. If
    * the listener was not previously registered this is a no-op.
+   *
+   * @hide
    */
   public void removeListener(NetworkEventListener toRemove) {
     for (NetworkEventListener listener : listeners) {
@@ -73,9 +86,12 @@
 
   /**
    * Notifies registered listeners of a network configuration change.
+   *
+   * @hide
    */
-  @libcore.api.CorePlatformApi
-  public void onNetworkConfigurationChanged() {
+  @SystemApi(client = MODULE_LIBRARIES)
+  @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+  public void dispatchNetworkConfigurationChange() {
     for (NetworkEventListener listener : listeners) {
       try {
         listener.onNetworkConfigurationChanged();
diff --git a/luni/src/main/java/libcore/net/http/Dns.java b/luni/src/main/java/libcore/net/http/Dns.java
new file mode 100644
index 0000000..a5bb423
--- /dev/null
+++ b/luni/src/main/java/libcore/net/http/Dns.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.net.http;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
+import libcore.api.CorePlatformApi;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
+/**
+ * A domain name service that resolves IP addresses for host names.
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+public interface Dns extends com.android.okhttp.internalandroidapi.Dns {
+    /**
+     * Returns the IP addresses of {@code hostname}, in the order they should
+     * be attempted. Returns loopback addresses for {@code null} host.
+     *
+     * @param hostname The host name will be looked up.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @NonNull List<@NonNull InetAddress> lookup(@Nullable String hostname) throws UnknownHostException;
+}
\ No newline at end of file
diff --git a/luni/src/main/java/libcore/net/http/HttpURLConnectionFactory.java b/luni/src/main/java/libcore/net/http/HttpURLConnectionFactory.java
new file mode 100644
index 0000000..9a713bd
--- /dev/null
+++ b/luni/src/main/java/libcore/net/http/HttpURLConnectionFactory.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 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.net.http;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
+import libcore.api.CorePlatformApi;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.SocketFactory;
+import libcore.util.NonNull;
+
+/**
+ * A HttpURLConnectionFactory that supports some configuration on a per-factory or per-connection
+ * basis. The per-factory configuration is <b>optional</b>; if not set, global configuration or
+ * default behavior is used.
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+@CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+public class HttpURLConnectionFactory {
+    private final com.android.okhttp.internalandroidapi.HttpURLConnectionFactory mFactory;
+
+    /**
+     * Create a new {@link HttpURLConnectionFactory} instance.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    @NonNull public static HttpURLConnectionFactory createInstance() {
+        return new HttpURLConnectionFactory();
+    }
+
+    HttpURLConnectionFactory() {
+        mFactory = new com.android.okhttp.internalandroidapi.HttpURLConnectionFactory();
+    }
+
+    /**
+     * Sets a new ConnectionPool, specific to this HttpURLConnectionFactory and for all future
+     * connections created by {@link #openConnection}, with the given configuration.
+     *
+     * @param maxIdleConnections The maximum number of idle connections to each to keep in the pool.
+     * @param keepAliveDuration Time to keep the connection alive in the pool before closing it.
+     * @param timeUnit The time unit of keep alive duration.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public void setNewConnectionPool(int maxIdleConnections, long keepAliveDuration,
+            @NonNull TimeUnit timeUnit) {
+        mFactory.setNewConnectionPool(maxIdleConnections, keepAliveDuration, timeUnit);
+    }
+
+    /**
+     * Sets a new dns resolver.
+     *
+     * @param dns the dns resolver for looking up.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public void setDns(@NonNull Dns dns) {
+        mFactory.setDns(dns);
+    }
+
+    /**
+     * Opens a connection using the specified SocketFactory and the specified proxy
+     * settings, overriding any system wide configuration.
+     *
+     * @param url The target URL that connection opens on.
+     * @param socketFactory The socket factory used to create connections.
+     * @param proxy The proxy settings used to create connections.
+     *
+     * @return An {@link java.net.URLConnection} using given SocketFactory, proxy settings and
+     *         configuration.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @CorePlatformApi(status = CorePlatformApi.Status.STABLE)
+    public URLConnection openConnection(@NonNull URL url, @NonNull SocketFactory socketFactory,
+            @NonNull Proxy proxy) throws IOException {
+        return mFactory.openConnection(url, socketFactory, proxy);
+    }
+}
diff --git a/luni/src/main/java/libcore/timezone/CountryTimeZones.java b/luni/src/main/java/libcore/timezone/CountryTimeZones.java
deleted file mode 100644
index 4ff8470..0000000
--- a/luni/src/main/java/libcore/timezone/CountryTimeZones.java
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import android.icu.util.TimeZone;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
-/**
- * Information about a country's time zones.
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class CountryTimeZones {
-
-    /**
-     * The result of lookup up a time zone using offset information (and possibly more).
-     * @hide
-     */
-    @libcore.api.CorePlatformApi
-    public static final class OffsetResult {
-
-        /** A zone that matches the supplied criteria. See also {@link #isOnlyMatch}. */
-        private final TimeZone timeZone;
-
-        /** True if there is one match for the supplied criteria */
-        private final boolean isOnlyMatch;
-
-        public OffsetResult(TimeZone timeZone, boolean isOnlyMatch) {
-            this.timeZone = java.util.Objects.requireNonNull(timeZone);
-            this.isOnlyMatch = isOnlyMatch;
-        }
-
-        @libcore.api.CorePlatformApi
-        public TimeZone getTimeZone() {
-            return timeZone;
-        }
-
-        @libcore.api.CorePlatformApi
-        public boolean isOnlyMatch() {
-            return isOnlyMatch;
-        }
-
-        @Override
-        public String toString() {
-            return "Result{"
-                    + "timeZone='" + timeZone + '\''
-                    + ", isOnlyMatch=" + isOnlyMatch
-                    + '}';
-        }
-    }
-
-    /**
-     * A mapping to a time zone ID with some associated metadata.
-     *
-     * @hide
-     */
-    @libcore.api.CorePlatformApi
-    public static final class TimeZoneMapping {
-        private final String timeZoneId;
-        private final boolean shownInPicker;
-        private final Long notUsedAfter;
-
-        /** Memoized TimeZone object for {@link #timeZoneId}. */
-        private TimeZone timeZone;
-
-        TimeZoneMapping(String timeZoneId, boolean shownInPicker, Long notUsedAfter) {
-            this.timeZoneId = Objects.requireNonNull(timeZoneId);
-            this.shownInPicker = shownInPicker;
-            this.notUsedAfter = notUsedAfter;
-        }
-
-        @libcore.api.CorePlatformApi
-        public String getTimeZoneId() {
-            return timeZoneId;
-        }
-
-        @libcore.api.CorePlatformApi
-        public boolean isShownInPicker() {
-            return shownInPicker;
-        }
-
-        @libcore.api.CorePlatformApi
-        public Long getNotUsedAfter() {
-            return notUsedAfter;
-        }
-
-        /**
-         * Returns a {@link TimeZone} object for this mapping, or {@code null} if the ID is unknown.
-         */
-        @libcore.api.CorePlatformApi
-        public TimeZone getTimeZone() {
-            synchronized (this) {
-                if (timeZone == null) {
-                    TimeZone tz = TimeZone.getFrozenTimeZone(timeZoneId);
-                    timeZone = tz;
-                    if (TimeZone.UNKNOWN_ZONE_ID.equals(timeZone.getID())) {
-                        // This shouldn't happen given the validation that takes place in
-                        // createValidatedCountryTimeZones().
-                        throw new IllegalStateException("Invalid zone in TimeZoneMapping: " + this);
-                    }
-                }
-            }
-
-            return TimeZone.UNKNOWN_ZONE_ID.equals(timeZone.getID()) ? null : timeZone;
-        }
-
-        /**
-         * Returns {@code true} if the mapping is "effective" after {@code whenMillis}, i.e.
-         * it is distinct from other "effective" times zones used in the country at/after that
-         * time. This uses the {@link #notUsedAfter} metadata which ensures there is one time
-         * zone remaining when there are multiple candidate zones with the same rules. The one
-         * kept is based on country specific factors like population covered.
-         */
-        boolean isEffectiveAt(long whenMillis) {
-            return notUsedAfter == null || whenMillis <= notUsedAfter;
-        }
-
-        // VisibleForTesting
-        @libcore.api.CorePlatformApi
-        public static TimeZoneMapping createForTests(
-                String timeZoneId, boolean showInPicker, Long notUsedAfter) {
-            return new TimeZoneMapping(timeZoneId, showInPicker, notUsedAfter);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            TimeZoneMapping that = (TimeZoneMapping) o;
-            return shownInPicker == that.shownInPicker &&
-                    Objects.equals(timeZoneId, that.timeZoneId) &&
-                    Objects.equals(notUsedAfter, that.notUsedAfter);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(timeZoneId, shownInPicker, notUsedAfter);
-        }
-
-        @Override
-        public String toString() {
-            return "TimeZoneMapping{"
-                    + "timeZoneId='" + timeZoneId + '\''
-                    + ", shownInPicker=" + shownInPicker
-                    + ", notUsedAfter=" + notUsedAfter
-                    + '}';
-        }
-
-        /**
-         * Returns {@code true} if one of the supplied {@link TimeZoneMapping} objects is for the
-         * specified time zone ID.
-         */
-        static boolean containsTimeZoneId(
-                List<TimeZoneMapping> timeZoneMappings, String timeZoneId) {
-            for (TimeZoneMapping timeZoneMapping : timeZoneMappings) {
-                if (timeZoneMapping.timeZoneId.equals(timeZoneId)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    private final String countryIso;
-    private final String defaultTimeZoneId;
-    /**
-     * {@code true} indicates the default time zone for a country is a good choice if a time zone
-     * cannot be determined by other means.
-     */
-    private final boolean defaultTimeZoneBoosted;
-    private final List<TimeZoneMapping> timeZoneMappings;
-    private final boolean everUsesUtc;
-
-    /**
-     * Memoized frozen ICU TimeZone object for the default. Can be {@link TimeZone#UNKNOWN_ZONE} if
-     * the {@link #defaultTimeZoneId} is missing or unrecognized.
-     */
-    private TimeZone defaultTimeZone;
-
-    private CountryTimeZones(String countryIso, String defaultTimeZoneId,
-            boolean defaultTimeZoneBoosted, boolean everUsesUtc,
-            List<TimeZoneMapping> timeZoneMappings) {
-        this.countryIso = java.util.Objects.requireNonNull(countryIso);
-        this.defaultTimeZoneId = defaultTimeZoneId;
-        this.defaultTimeZoneBoosted = defaultTimeZoneBoosted;
-        this.everUsesUtc = everUsesUtc;
-        // Create a defensive copy of the mapping list.
-        this.timeZoneMappings = Collections.unmodifiableList(new ArrayList<>(timeZoneMappings));
-    }
-
-    /**
-     * Creates a {@link CountryTimeZones} object containing only known time zone IDs.
-     */
-    public static CountryTimeZones createValidated(String countryIso, String defaultTimeZoneId,
-            boolean defaultTimeZoneBoosted, boolean everUsesUtc,
-            List<TimeZoneMapping> timeZoneMappings, String debugInfo) {
-
-        // We rely on ZoneInfoDB to tell us what the known valid time zone IDs are. ICU may
-        // recognize more but we want to be sure that zone IDs can be used with java.util as well as
-        // android.icu and ICU is expected to have a superset.
-        String[] validTimeZoneIdsArray = ZoneInfoDb.getInstance().getAvailableIDs();
-        HashSet<String> validTimeZoneIdsSet = new HashSet<>(Arrays.asList(validTimeZoneIdsArray));
-        List<TimeZoneMapping> validCountryTimeZoneMappings = new ArrayList<>();
-        for (TimeZoneMapping timeZoneMapping : timeZoneMappings) {
-            String timeZoneId = timeZoneMapping.timeZoneId;
-            if (!validTimeZoneIdsSet.contains(timeZoneId)) {
-                System.logW("Skipping invalid zone: " + timeZoneId + " at " + debugInfo);
-            } else {
-                validCountryTimeZoneMappings.add(timeZoneMapping);
-            }
-        }
-
-        // We don't get too strict at runtime about whether the defaultTimeZoneId must be
-        // one of the country's time zones because this is the data we have to use (we also
-        // assume the data was validated by earlier steps). The default time zone ID must just
-        // be a recognized zone ID: if it's not valid we leave it null.
-        if (!validTimeZoneIdsSet.contains(defaultTimeZoneId)) {
-            System.logW("Invalid default time zone ID: " + defaultTimeZoneId
-                    + " at " + debugInfo);
-            defaultTimeZoneId = null;
-        }
-
-        String normalizedCountryIso = normalizeCountryIso(countryIso);
-        return new CountryTimeZones(
-                normalizedCountryIso, defaultTimeZoneId, defaultTimeZoneBoosted, everUsesUtc,
-                validCountryTimeZoneMappings);
-    }
-
-    /**
-     * Returns the ISO code for the country.
-     */
-    @libcore.api.CorePlatformApi
-    public String getCountryIso() {
-        return countryIso;
-    }
-
-    /**
-     * Returns true if the ISO code for the country is a match for the one specified.
-     */
-    @libcore.api.CorePlatformApi
-    public boolean isForCountryCode(String countryIso) {
-        return this.countryIso.equals(normalizeCountryIso(countryIso));
-    }
-
-    /**
-     * Returns the default time zone for the country. Can return null in cases when no data is
-     * available or the time zone ID provided to
-     * {@link #createValidated(String, String, boolean, boolean, List, String)} was not recognized.
-     */
-    @libcore.api.CorePlatformApi
-    public synchronized TimeZone getDefaultTimeZone() {
-        if (defaultTimeZone == null) {
-            TimeZone timeZone;
-            if (defaultTimeZoneId == null) {
-                timeZone = TimeZone.UNKNOWN_ZONE;
-            } else {
-                timeZone = TimeZone.getFrozenTimeZone(defaultTimeZoneId);
-            }
-            this.defaultTimeZone = timeZone;
-        }
-        return TimeZone.UNKNOWN_ZONE_ID.equals(defaultTimeZone.getID()) ? null : defaultTimeZone;
-    }
-
-    /**
-     * Returns the default time zone ID for the country. Can return null in cases when no data is
-     * available or the time zone ID provided to
-     * {@link #createValidated(String, String, boolean, boolean, List, String)} was not recognized.
-     */
-    @libcore.api.CorePlatformApi
-    public String getDefaultTimeZoneId() {
-        return defaultTimeZoneId;
-    }
-
-    /**
-     * Qualifier for a country's default time zone. {@code true} indicates whether the default
-     * would be a good choice <em>generally</em> when there's no other information available.
-     */
-    @libcore.api.CorePlatformApi
-    public boolean isDefaultTimeZoneBoosted() {
-        return defaultTimeZoneBoosted;
-    }
-
-    /**
-     * Returns an immutable, ordered list of time zone mappings for the country in an undefined but
-     * "priority" order. The list can be empty if there were no zones configured or the configured
-     * zone IDs were not recognized.
-     */
-    @libcore.api.CorePlatformApi
-    public List<TimeZoneMapping> getTimeZoneMappings() {
-        return timeZoneMappings;
-    }
-
-    /**
-     * Returns an immutable, ordered list of time zone mappings for the country in an undefined but
-     * "priority" order, filtered so that only "effective" time zone IDs are returned. An
-     * "effective" time zone is one that differs from another time zone used in the country after
-     * {@code whenMillis}. The list can be empty if there were no zones configured or the configured
-     * zone IDs were not recognized.
-     */
-    @libcore.api.CorePlatformApi
-    public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) {
-        ArrayList<TimeZoneMapping> filteredList = new ArrayList<>(timeZoneMappings.size());
-        for (TimeZoneMapping timeZoneMapping : timeZoneMappings) {
-            if (timeZoneMapping.isEffectiveAt(whenMillis)) {
-                filteredList.add(timeZoneMapping);
-            }
-        }
-        return Collections.unmodifiableList(filteredList);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        CountryTimeZones that = (CountryTimeZones) o;
-        return defaultTimeZoneBoosted == that.defaultTimeZoneBoosted
-                && everUsesUtc == that.everUsesUtc
-                && countryIso.equals(that.countryIso)
-                && Objects.equals(defaultTimeZoneId, that.defaultTimeZoneId)
-                && timeZoneMappings.equals(that.timeZoneMappings);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(
-                countryIso, defaultTimeZoneId, defaultTimeZoneBoosted, timeZoneMappings,
-                everUsesUtc);
-    }
-
-    @Override
-    public String toString() {
-        return "CountryTimeZones{"
-                + "countryIso='" + countryIso + '\''
-                + ", defaultTimeZoneId='" + defaultTimeZoneId + '\''
-                + ", defaultTimeZoneBoosted=" + defaultTimeZoneBoosted
-                + ", timeZoneMappings=" + timeZoneMappings
-                + ", everUsesUtc=" + everUsesUtc
-                + '}';
-    }
-
-    /**
-     * Returns true if the country has at least one zone that is the same as UTC at the given time.
-     */
-    @libcore.api.CorePlatformApi
-    public boolean hasUtcZone(long whenMillis) {
-        // If the data tells us the country never uses UTC we don't have to check anything.
-        if (!everUsesUtc) {
-            return false;
-        }
-
-        for (TimeZoneMapping timeZoneMapping : getEffectiveTimeZoneMappingsAt(whenMillis)) {
-            TimeZone timeZone = timeZoneMapping.getTimeZone();
-            if (timeZone != null && timeZone.getOffset(whenMillis) == 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns a time zone for the country, if there is one, that matches the supplied properties.
-     * If there are multiple matches and the {@code bias} is one of them then it is returned,
-     * otherwise an arbitrary match is returned based on the {@link
-     * #getEffectiveTimeZoneMappingsAt(long)} ordering.
-     *
-     * @param whenMillis the UTC time to match against
-     * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference
-     * @param totalOffsetMillis the offset from UTC at {@code whenMillis}
-     * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST,
-     *     {@code false} means not DST
-     * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if
-     *     there is no match
-     */
-    @libcore.api.CorePlatformApi
-    public OffsetResult lookupByOffsetWithBias(long whenMillis, TimeZone bias,
-            int totalOffsetMillis, boolean isDst) {
-        return lookupByOffsetWithBiasInternal(whenMillis, bias, totalOffsetMillis, isDst);
-    }
-
-    /**
-     * Returns a time zone for the country, if there is one, that matches the supplied properties.
-     * If there are multiple matches and the {@code bias} is one of them then it is returned,
-     * otherwise an arbitrary match is returned based on the {@link
-     * #getEffectiveTimeZoneMappingsAt(long)} ordering.
-     *
-     * @param whenMillis the UTC time to match against
-     * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference
-     * @param totalOffsetMillis the offset from UTC at {@code whenMillis}
-     * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if
-     *     there is no match
-     */
-    @libcore.api.CorePlatformApi
-    public OffsetResult lookupByOffsetWithBias(long whenMillis, TimeZone bias,
-            int totalOffsetMillis) {
-        final Boolean isDst = null;
-        return lookupByOffsetWithBiasInternal(whenMillis, bias, totalOffsetMillis, isDst);
-    }
-
-    /**
-     * Returns a time zone for the country, if there is one, that matches the supplied properties.
-     * If there are multiple matches and the {@code bias} is one of them then it is returned,
-     * otherwise an arbitrary match is returned based on the {@link
-     * #getEffectiveTimeZoneMappingsAt(long)} ordering.
-     *
-     * @param whenMillis the UTC time to match against
-     * @param bias the time zone to prefer, can be {@code null}
-     * @param totalOffsetMillis the offset from UTC at {@code whenMillis}
-     * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST,
-     *     {@code false} means not DST, {@code null} means unknown
-     */
-    private OffsetResult lookupByOffsetWithBiasInternal(long whenMillis, TimeZone bias,
-            int totalOffsetMillis, Boolean isDst) {
-        List<TimeZoneMapping> timeZoneMappings = getEffectiveTimeZoneMappingsAt(whenMillis);
-        if (timeZoneMappings.isEmpty()) {
-            return null;
-        }
-
-        TimeZone firstMatch = null;
-        boolean biasMatched = false;
-        boolean oneMatch = true;
-        for (TimeZoneMapping timeZoneMapping : timeZoneMappings) {
-            TimeZone match = timeZoneMapping.getTimeZone();
-            if (match == null
-                    || !offsetMatchesAtTime(whenMillis, match, totalOffsetMillis, isDst)) {
-                continue;
-            }
-
-            if (firstMatch == null) {
-                firstMatch = match;
-            } else {
-                oneMatch = false;
-            }
-            if (bias != null && match.getID().equals(bias.getID())) {
-                biasMatched = true;
-            }
-            if (firstMatch != null && !oneMatch && (bias == null || biasMatched)) {
-                break;
-            }
-        }
-        if (firstMatch == null) {
-            return null;
-        }
-
-        TimeZone toReturn = biasMatched ? bias : firstMatch;
-        return new OffsetResult(toReturn, oneMatch);
-    }
-
-    /**
-     * Returns {@code true} if the specified {@code totalOffset} and {@code isDst} would be valid in
-     * the {@code timeZone} at time {@code whenMillis}.
-     * {@code totalOffetMillis} is always matched.
-     * If {@code isDst} is {@code null}, this means the DST state is unknown.
-     * If {@code isDst} is {@code false}, this means the zone must not be in DST.
-     * If {@code isDst} is {@code true}, this means the zone must be in DST.
-     */
-    private static boolean offsetMatchesAtTime(long whenMillis, TimeZone timeZone,
-            int totalOffsetMillis, Boolean isDst) {
-        int[] offsets = new int[2];
-        timeZone.getOffset(whenMillis, false /* local */, offsets);
-
-        if (totalOffsetMillis != (offsets[0] + offsets[1])) {
-            return false;
-        }
-
-        return isDst == null || (isDst == (offsets[1] != 0));
-    }
-
-    private static String normalizeCountryIso(String countryIso) {
-        // Lowercase ASCII is normalized for the purposes of the code in this class.
-        return countryIso.toLowerCase(Locale.US);
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/CountryZonesFinder.java b/luni/src/main/java/libcore/timezone/CountryZonesFinder.java
deleted file mode 100644
index 1a9d6a6..0000000
--- a/luni/src/main/java/libcore/timezone/CountryZonesFinder.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2018 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.timezone;
-
-import static libcore.timezone.XmlUtils.normalizeCountryIso;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-
-/**
- * An in-memory representation of country &lt;-&gt; time zone mapping data.
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class CountryZonesFinder {
-
-    private final List<CountryTimeZones> countryTimeZonesList;
-
-    CountryZonesFinder(List<CountryTimeZones> countryTimeZonesList) {
-        this.countryTimeZonesList = new ArrayList<>(countryTimeZonesList);
-    }
-
-    // VisibleForTesting
-    public static CountryZonesFinder createForTests(List<CountryTimeZones> countryTimeZonesList) {
-        return new CountryZonesFinder(countryTimeZonesList);
-    }
-
-    /**
-     * Returns an immutable list of country ISO codes with time zones. The codes can be passed to
-     * {@link #lookupCountryTimeZones(String)} and similar methods.
-     */
-    @libcore.api.CorePlatformApi
-    public List<String> lookupAllCountryIsoCodes() {
-        List<String> isoCodes = new ArrayList<>(countryTimeZonesList.size());
-        for (CountryTimeZones countryTimeZones : countryTimeZonesList) {
-            isoCodes.add(countryTimeZones.getCountryIso());
-        }
-        return Collections.unmodifiableList(isoCodes);
-    }
-
-    /**
-     * Returns an immutable list of {@link CountryTimeZones} for countries that use the specified
-     * time zone. An exact, case-sensitive match is performed on the zone ID. This method never
-     * returns null.
-     */
-    @libcore.api.CorePlatformApi
-    public List<CountryTimeZones> lookupCountryTimeZonesForZoneId(String zoneId) {
-        List<CountryTimeZones> matches = new ArrayList<>(2);
-        for (CountryTimeZones countryTimeZones : countryTimeZonesList) {
-            boolean match = TimeZoneMapping.containsTimeZoneId(
-                    countryTimeZones.getTimeZoneMappings(), zoneId);
-            if (match) {
-                matches.add(countryTimeZones);
-            }
-        }
-        return Collections.unmodifiableList(matches);
-    }
-
-    /**
-     * Returns a {@link CountryTimeZones} object associated with the specified country code. If one
-     * cannot be found this method returns {@code null}.
-     */
-    @libcore.api.CorePlatformApi
-    public CountryTimeZones lookupCountryTimeZones(String countryIso) {
-        String normalizedCountryIso = normalizeCountryIso(countryIso);
-        for (CountryTimeZones countryTimeZones : countryTimeZonesList) {
-            if (countryTimeZones.getCountryIso().equals(normalizedCountryIso)) {
-                return countryTimeZones;
-            }
-        }
-        return null;
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/TEST_MAPPING b/luni/src/main/java/libcore/timezone/TEST_MAPPING
deleted file mode 100644
index 7f731bb..0000000
--- a/luni/src/main/java/libcore/timezone/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "CtsLibcoreTestCases",
-      "options": [
-        {
-          "include-filter": "libcore.libcore.timezone"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/luni/src/main/java/libcore/timezone/TelephonyLookup.java b/luni/src/main/java/libcore/timezone/TelephonyLookup.java
deleted file mode 100644
index 10d9a35..0000000
--- a/luni/src/main/java/libcore/timezone/TelephonyLookup.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import static libcore.timezone.XmlUtils.checkOnEndTag;
-import static libcore.timezone.XmlUtils.consumeUntilEndTag;
-import static libcore.timezone.XmlUtils.findNextStartTagOrEndTagNoRecurse;
-import static libcore.timezone.XmlUtils.findNextStartTagOrThrowNoRecurse;
-import static libcore.timezone.XmlUtils.normalizeCountryIso;
-
-import libcore.timezone.TelephonyNetwork.MccMnc;
-import libcore.timezone.XmlUtils.ReaderSupplier;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * A class that can find time zone-related information about telephony networks by loading data from
- * the telephonylookup.xml file.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class TelephonyLookup {
-
-    // VisibleForTesting
-    public static final String TELEPHONYLOOKUP_FILE_NAME = "telephonylookup.xml";
-
-    // Root element. e.g. <telephony_lookup>
-    private static final String TELEPHONY_LOOKUP_ELEMENT = "telephony_lookup";
-
-    // Networks section. e.g. <networks>
-    private static final String NETWORKS_ELEMENT = "networks";
-
-    // Network data. e.g.
-    // <network mcc="310" mnc="370" country="gu">
-    private static final String NETWORK_ELEMENT = "network";
-    private static final String MOBILE_COUNTRY_CODE_ATTRIBUTE = "mcc";
-    private static final String MOBILE_NETWORK_CODE_ATTRIBUTE = "mnc";
-    // This is the ISO 3166 alpha-2 code (in lower case).
-    private static final String COUNTRY_ISO_CODE_ATTRIBUTE = "country";
-
-    private static TelephonyLookup instance;
-
-    private final ReaderSupplier xmlSource;
-
-    private TelephonyLookup(ReaderSupplier xmlSource) {
-        this.xmlSource = xmlSource;
-    }
-
-    /**
-     * Obtains an instance for use when resolving networks. This method handles using the correct
-     * file when there are several to choose from. This method never returns {@code null}. No
-     * in-depth validation is performed on the file content, see {@link #validate()}.
-     */
-    @libcore.api.CorePlatformApi
-    public static TelephonyLookup getInstance() {
-        synchronized(TelephonyLookup.class) {
-            if (instance == null) {
-                String[] telephonyLookupFilePaths =
-                        TimeZoneDataFiles.getTimeZoneFilePaths(TELEPHONYLOOKUP_FILE_NAME);
-                instance = createInstanceWithFallback(telephonyLookupFilePaths);
-            }
-        }
-        return instance;
-    }
-
-    // VisibleForTesting
-    public static TelephonyLookup createInstanceWithFallback(String... telephonyLookupFilePaths) {
-        IOException lastException = null;
-        for (String tzLookupFilePath : telephonyLookupFilePaths) {
-            try {
-                // We assume that any file in /data was validated before install, and the system
-                // file was validated before the device shipped. Therefore, we do not pay the
-                // validation cost here.
-                return createInstance(tzLookupFilePath);
-            } catch (IOException e) {
-                // There's expected to be two files, and it's normal for the first file not to
-                // exist so we don't log, but keep the lastException so we can log it if there
-                // are no valid files available.
-                if (lastException != null) {
-                    e.addSuppressed(lastException);
-                }
-                lastException = e;
-            }
-        }
-
-        System.logE("No valid file found in set: " + Arrays.toString(telephonyLookupFilePaths)
-                + " Printing exceptions and falling back to empty map.", lastException);
-        return createInstanceForTests("<telephony_lookup><networks /></telephony_lookup>");
-    }
-
-    /**
-     * Obtains an instance using a specific data file, throwing an IOException if the file does not
-     * exist or is not readable. This method never returns {@code null}. No in-depth validation is
-     * performed on the file content, see {@link #validate()}.
-     */
-    @libcore.api.CorePlatformApi
-    public static TelephonyLookup createInstance(String path) throws IOException {
-        ReaderSupplier xmlSupplier = ReaderSupplier.forFile(path, StandardCharsets.UTF_8);
-        return new TelephonyLookup(xmlSupplier);
-    }
-
-    /** Used to create an instance using an in-memory XML String instead of a file. */
-    // VisibleForTesting
-    public static TelephonyLookup createInstanceForTests(String xml) {
-        return new TelephonyLookup(ReaderSupplier.forString(xml));
-    }
-
-    /**
-     * Parses the data file, throws an exception if it is invalid or cannot be read.
-     */
-    @libcore.api.CorePlatformApi
-    public void validate() throws IOException {
-        try {
-            processXml(new TelephonyNetworkValidator());
-        } catch (XmlPullParserException e) {
-            throw new IOException("Parsing error", e);
-        }
-    }
-
-    /**
-     * Loads all the network &lt;-&gt; country mapping data into memory. This method can return
-     * {@code null} in the event of an error while reading the underlying data files.
-     */
-    @libcore.api.CorePlatformApi
-    public TelephonyNetworkFinder getTelephonyNetworkFinder() {
-        TelephonyNetworksExtractor extractor = new TelephonyNetworksExtractor();
-        try {
-            processXml(extractor);
-
-            return extractor.getTelephonyNetworkFinder();
-        } catch (XmlPullParserException | IOException e) {
-            System.logW("Error reading telephony networks", e);
-            return null;
-        }
-    }
-
-    /**
-     * Processes the XML, applying the {@link TelephonyNetworkProcessor} to the &lt;countryzones&gt;
-     * element. Processing can terminate early if {@link TelephonyNetworkProcessor
-     * #processNetwork(int, int, String, String)} it throws an exception.
-     */
-    private void processXml(TelephonyNetworkProcessor processor)
-            throws XmlPullParserException, IOException {
-        try (Reader reader = xmlSource.get()) {
-            XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
-            xmlPullParserFactory.setNamespaceAware(false);
-
-            XmlPullParser parser = xmlPullParserFactory.newPullParser();
-            parser.setInput(reader);
-
-            /*
-             * The expected XML structure is:
-             * <telephony_lookup>
-             *   <networks>
-             *     <network mcc="123" mnc="456" country="ab"/>
-             *     <network mcc="123" mnc="567" country="cd"/>
-             *   </networks>
-             * </telephony_lookup>
-             */
-
-            findNextStartTagOrThrowNoRecurse(parser, TELEPHONY_LOOKUP_ELEMENT);
-
-            // There is only one expected sub-element <telephony_lookup> in the format currently,
-            // skip over anything before it.
-            findNextStartTagOrThrowNoRecurse(parser, NETWORKS_ELEMENT);
-
-            processNetworks(parser, processor);
-
-            // Make sure we are on the </networks> tag.
-            checkOnEndTag(parser, NETWORKS_ELEMENT);
-
-            // Advance to the next event.
-            parser.next();
-
-            // Skip anything until </telephony_lookup>, and make sure the file is not truncated and
-            // we can find the end.
-            consumeUntilEndTag(parser, TELEPHONY_LOOKUP_ELEMENT);
-
-            // Make sure we are on the </telephony_lookup> tag.
-            checkOnEndTag(parser, TELEPHONY_LOOKUP_ELEMENT);
-        }
-    }
-
-    private static void processNetworks(XmlPullParser parser,
-            TelephonyNetworkProcessor processor) throws IOException, XmlPullParserException {
-
-        // Skip over any unexpected elements and process <network> elements.
-        while (findNextStartTagOrEndTagNoRecurse(parser, NETWORK_ELEMENT)) {
-            String mcc = parser.getAttributeValue(
-                    null /* namespace */, MOBILE_COUNTRY_CODE_ATTRIBUTE);
-            if (mcc == null) {
-                throw new XmlPullParserException(
-                        "Unable to find mcc: " + parser.getPositionDescription());
-            }
-
-            String mnc = parser.getAttributeValue(
-                    null /* namespace */, MOBILE_NETWORK_CODE_ATTRIBUTE);
-            if (mnc == null) {
-                throw new XmlPullParserException(
-                        "Unable to find mnc: " + parser.getPositionDescription());
-            }
-
-            String countryCode =
-                    parser.getAttributeValue(null /* namespace */, COUNTRY_ISO_CODE_ATTRIBUTE);
-            if (countryCode == null) {
-                throw new XmlPullParserException(
-                        "Unable to find country: " + parser.getPositionDescription());
-            }
-
-            String debugInfo = parser.getPositionDescription();
-            processor.processNetwork(mcc, mnc, countryCode, debugInfo);
-            // Advance to the next event.
-            parser.next();
-
-            // Skip anything until </network>.
-            consumeUntilEndTag(parser, NETWORK_ELEMENT);
-        }
-    }
-
-    /**
-     * Processes &lt;network&gt; data.
-     */
-    private interface TelephonyNetworkProcessor {
-
-        boolean CONTINUE = true;
-        boolean HALT = false;
-
-        /**
-         * Process network data. Problems with the data are reported as an exception.
-         */
-        void processNetwork(String mcc, String mnc, String countryIso, String debugInfo)
-                throws XmlPullParserException;
-    }
-
-    /**
-     * Validates &lt;network&gt; elements. Intended to be used before a proposed installation of new
-     * data. To be valid the MCC + MNC combination must generate a unique ID, country ISO code must
-     * be normalized.
-     */
-    private static class TelephonyNetworkValidator implements TelephonyNetworkProcessor {
-
-        private final Set<MccMnc> knownMccMncs = new HashSet<>();
-
-        @Override
-        public void processNetwork(String mcc, String mnc, String countryIso, String debugInfo)
-                throws XmlPullParserException {
-            if (mcc == null || mcc.length() != 3 || !isAsciiNumeric(mcc)) {
-                throw new XmlPullParserException(
-                        "MCC is not valid: mcc=" + mcc + " at " + debugInfo);
-            }
-
-            if (mnc == null || !(mnc.length() == 2 || mnc.length() == 3) || !isAsciiNumeric(mnc)) {
-                throw new XmlPullParserException(
-                        "MNC is not valid: mnc=" + mnc + " at " + debugInfo);
-            }
-
-            if (!normalizeCountryIso(countryIso).equals(countryIso)) {
-                throw new XmlPullParserException("Country code: " + countryIso
-                        + " is not normalized at " + debugInfo);
-            }
-
-            MccMnc mccMnc = new MccMnc(mcc, mnc);
-            if (knownMccMncs.contains(mccMnc)) {
-                throw new XmlPullParserException("Second entry for MCC + MNC: " + mccMnc
-                        + " at " + debugInfo);
-            }
-            knownMccMncs.add(mccMnc);
-        }
-
-        private static boolean isAsciiNumeric(String string) {
-            for (int i = 0; i < string.length(); i++) {
-                char character = string.charAt(i);
-                if (character < '0' || character > '9') {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    /**
-     * Reads all telephony network time zone information into memory and makes it available as a
-     * {@link TelephonyNetworkFinder}.
-     */
-    private static class TelephonyNetworksExtractor implements TelephonyNetworkProcessor {
-        private List<TelephonyNetwork> networksList = new ArrayList<>(10 /* default */);
-
-        @Override
-        public void processNetwork(String mcc, String mnc, String countryIso, String debugInfo)
-                throws XmlPullParserException {
-            TelephonyNetwork network = TelephonyNetwork.create(mcc, mnc, countryIso);
-            networksList.add(network);
-        }
-
-        TelephonyNetworkFinder getTelephonyNetworkFinder() {
-            return TelephonyNetworkFinder.create(networksList);
-        }
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/TelephonyNetwork.java b/luni/src/main/java/libcore/timezone/TelephonyNetwork.java
deleted file mode 100644
index e0f26ad..0000000
--- a/luni/src/main/java/libcore/timezone/TelephonyNetwork.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import static libcore.timezone.XmlUtils.normalizeCountryIso;
-
-import java.util.Objects;
-
-/**
- * Information about a telephony network.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class TelephonyNetwork {
-
-    /**
-     * A numeric network identifier consisting of the Mobile Country Code (MCC) and the Mobile
-     * Network Code (MNC).
-     *
-     * @hide
-     */
-    public static final class MccMnc {
-        final String mcc;
-        final String mnc;
-
-        public MccMnc(String mcc, String mnc) {
-            this.mcc = mcc;
-            this.mnc = mnc;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            MccMnc mccMnc = (MccMnc) o;
-            return Objects.equals(mcc, mccMnc.mcc)
-                    && Objects.equals(mnc, mccMnc.mnc);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mcc, mnc);
-        }
-
-        @Override
-        public String toString() {
-            return "MccMnc{"
-                    + "mcc=" + mcc
-                    + ", mnc=" + mnc
-                    + '}';
-        }
-    }
-
-    private final MccMnc mccMnc;
-    private final String countryIsoCode;
-
-    public static TelephonyNetwork create(String mcc, String mnc, String countryIsoCode) {
-        String normalizedCountryIso = normalizeCountryIso(countryIsoCode);
-        return new TelephonyNetwork(new MccMnc(mcc, mnc), normalizedCountryIso);
-    }
-
-    private TelephonyNetwork(MccMnc mccMnc, String countryIsoCode) {
-        this.mccMnc = mccMnc;
-        this.countryIsoCode = Objects.requireNonNull(countryIsoCode);
-    }
-
-    public MccMnc getMccMnc() {
-        return mccMnc;
-    }
-
-    @libcore.api.CorePlatformApi
-    public String getMcc() {
-        return mccMnc.mcc;
-    }
-
-    @libcore.api.CorePlatformApi
-    public String getMnc() {
-        return mccMnc.mnc;
-    }
-
-    @libcore.api.CorePlatformApi
-    public String getCountryIsoCode() {
-        return countryIsoCode;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        TelephonyNetwork that = (TelephonyNetwork) o;
-        return mccMnc.equals(that.mccMnc) &&
-                countryIsoCode.equals(that.countryIsoCode);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mccMnc, countryIsoCode);
-    }
-
-    @Override
-    public String toString() {
-        return "TelephonyNetwork{"
-                + "mccMnc=" + mccMnc
-                + ", countryIsoCode='" + countryIsoCode + '\''
-                + '}';
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/TelephonyNetworkFinder.java b/luni/src/main/java/libcore/timezone/TelephonyNetworkFinder.java
deleted file mode 100644
index f244159..0000000
--- a/luni/src/main/java/libcore/timezone/TelephonyNetworkFinder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import static libcore.timezone.XmlUtils.normalizeCountryIso;
-
-import libcore.timezone.TelephonyNetwork.MccMnc;
-
-import java.util.ArrayList;
-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;
-
-/**
- * A class that can find telephony networks loaded via {@link TelephonyLookup}.
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class TelephonyNetworkFinder {
-
-    private final Map<MccMnc, TelephonyNetwork> networksMap;
-    private final List<TelephonyNetwork> networksList;
-
-    public static TelephonyNetworkFinder create(List<TelephonyNetwork> networksList) {
-        Set<String> validCountryIsoCodes = new HashSet<>();
-        for (String validCountryIsoCode : Locale.getISOCountries()) {
-            validCountryIsoCodes.add(normalizeCountryIso(validCountryIsoCode));
-        }
-
-        Map<MccMnc, TelephonyNetwork> networksMap = new HashMap<>();
-        for (TelephonyNetwork network : networksList) {
-            if (!validCountryIsoCodes.contains(network.getCountryIsoCode())) {
-                System.logW("Unrecognized country code: " + network.getCountryIsoCode()
-                        + " for telephony network=" + network);
-            }
-
-            MccMnc mccMnc = network.getMccMnc();
-            TelephonyNetwork existingEntry = networksMap.put(mccMnc, network);
-            if (existingEntry != null) {
-                System.logW("Duplicate MccMnc detected for " + mccMnc
-                        + ". New entry=" + network + " replacing previous entry.");
-            }
-        }
-        return new TelephonyNetworkFinder(
-                Collections.unmodifiableList(new ArrayList<>(networksList)),
-                networksMap);
-    }
-
-    private TelephonyNetworkFinder(List<TelephonyNetwork> networksList,
-            Map<MccMnc, TelephonyNetwork> networksMap) {
-        this.networksList = networksList;
-        this.networksMap = networksMap;
-    }
-
-    @libcore.api.CorePlatformApi
-    public TelephonyNetwork findNetworkByMccMnc(String mcc, String mnc) {
-        return networksMap.get(new MccMnc(mcc, mnc));
-    }
-
-    // @VisibleForTesting
-    public List<TelephonyNetwork> getAll() {
-        return networksList;
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java b/luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java
deleted file mode 100644
index b4c8976..0000000
--- a/luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Utility methods associated with finding updateable time zone data files.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class TimeZoneDataFiles {
-    private static final String ANDROID_ROOT_ENV = "ANDROID_ROOT";
-    private static final String ANDROID_I18N_ROOT_ENV = "ANDROID_I18N_ROOT";
-    private static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT";
-    private static final String ANDROID_DATA_ENV = "ANDROID_DATA";
-
-    private TimeZoneDataFiles() {}
-
-    /**
-     * Returns time zone file paths for the specified file name in an array in the order they
-     * should be tried. See {@link #generateIcuDataPath()} for ICU files instead.
-     * <ul>
-     * <li>[0] - the location of the file in the /data partition (may not exist).</li>
-     * <li>[1] - the location of the file from the time zone module under /apex (must exist).</li>
-     * </ul>
-     */
-    // VisibleForTesting
-    public static String[] getTimeZoneFilePaths(String fileName) {
-        return new String[] {
-                getDataTimeZoneFile(fileName),
-                getTimeZoneModuleTzFile(fileName),
-        };
-    }
-
-    // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
-    @libcore.api.CorePlatformApi
-    public static String getDataTimeZoneRootDir() {
-        return System.getenv(ANDROID_DATA_ENV) + "/misc/zoneinfo/";
-    }
-
-    // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
-    @libcore.api.CorePlatformApi
-    public static String getDataTimeZoneFile(String fileName) {
-        return getDataTimeZoneRootDir() + "current/" + fileName;
-    }
-
-    public static String getTimeZoneModuleTzFile(String fileName) {
-        return getTimeZoneModuleFile("tz/" + fileName);
-    }
-
-    public static String getTimeZoneModuleIcuFile(String fileName) {
-        return getTimeZoneModuleFile("icu/" + fileName);
-    }
-
-    // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
-    @libcore.api.CorePlatformApi
-    public static String getTimeZoneModuleTzVersionFile() {
-        return getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
-    }
-
-    // VisibleForTesting
-    public static String getTimeZoneModuleFile(String fileName) {
-        return System.getenv(ANDROID_TZDATA_ROOT_ENV) + "/etc/" + fileName;
-    }
-
-    public static String getI18nModuleIcuFile(String fileName) {
-        return getI18nModuleFile("icu/" + fileName);
-    }
-
-    private static String getI18nModuleFile(String fileName) {
-        return System.getenv(ANDROID_I18N_ROOT_ENV) + "/etc/" + fileName;
-    }
-
-    public static String getSystemTzFile(String fileName) {
-        return getEnvironmentPath(ANDROID_ROOT_ENV, "/usr/share/zoneinfo/" + fileName);
-    }
-
-    public static String getSystemIcuFile(String fileName) {
-        return getEnvironmentPath(ANDROID_ROOT_ENV, "/usr/icu/" + fileName);
-    }
-
-    public static String generateIcuDataPath() {
-        List<String> paths = new ArrayList<>(3);
-
-        // Note: This logic below should match the logic in IcuRegistration.cpp in external/icu/
-        // to ensure consistent behavior between ICU4C and ICU4J.
-
-        // ICU should first look in ANDROID_DATA. This is used for (optional) time zone data
-        // delivered by APK (https://source.android.com/devices/tech/config/timezone-rules)
-        String dataIcuDataPath =
-                getEnvironmentPath(ANDROID_DATA_ENV, "/misc/zoneinfo/current/icu/");
-        if (dataIcuDataPath != null) {
-            paths.add(dataIcuDataPath);
-        }
-
-        // ICU should then look for a mounted time zone module file in /apex. This is used for
-        // (optional) time zone data that can be updated with an APEX file.
-        String timeZoneModuleIcuDataPath = getTimeZoneModuleIcuFile("");
-        paths.add(timeZoneModuleIcuDataPath);
-
-        // ICU should always look in the i18n module path as this is where most of the data
-        // can be found.
-        String i18nModuleIcuDataPath = getI18nModuleIcuFile("");
-        paths.add(i18nModuleIcuDataPath);
-
-        return String.join(":", paths);
-    }
-
-    /**
-     * Creates a path by combining the value of an environment variable with a relative path.
-     * Returns {@code null} if the environment variable is not set.
-     */
-    private static String getEnvironmentPath(String environmentVariable, String path) {
-        String variable = System.getenv(environmentVariable);
-        if (variable == null) {
-            return null;
-        }
-        return variable + path;
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/TimeZoneFinder.java b/luni/src/main/java/libcore/timezone/TimeZoneFinder.java
deleted file mode 100644
index 3f18e36..0000000
--- a/luni/src/main/java/libcore/timezone/TimeZoneFinder.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import static libcore.timezone.XmlUtils.checkOnEndTag;
-import static libcore.timezone.XmlUtils.consumeText;
-import static libcore.timezone.XmlUtils.consumeUntilEndTag;
-import static libcore.timezone.XmlUtils.findNextStartTagOrEndTagNoRecurse;
-import static libcore.timezone.XmlUtils.findNextStartTagOrThrowNoRecurse;
-import static libcore.timezone.XmlUtils.normalizeCountryIso;
-import static libcore.timezone.XmlUtils.parseBooleanAttribute;
-import static libcore.timezone.XmlUtils.parseLongAttribute;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlPullParserFactory;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-import libcore.timezone.XmlUtils.ReaderSupplier;
-
-/**
- * A class that can find matching time zones by loading data from the tzlookup.xml file.
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class TimeZoneFinder {
-
-    // VisibleForTesting
-    public static final String TZLOOKUP_FILE_NAME = "tzlookup.xml";
-
-    // Root element. e.g. <timezones ianaversion="2017b">
-    private static final String TIMEZONES_ELEMENT = "timezones";
-    private static final String IANA_VERSION_ATTRIBUTE = "ianaversion";
-
-    // Country zones section. e.g. <countryzones>
-    private static final String COUNTRY_ZONES_ELEMENT = "countryzones";
-
-    // Country data. e.g.
-    // <country code="gb" default="Europe/London" defaultBoost="y" everutc="y">
-    private static final String COUNTRY_ELEMENT = "country";
-    private static final String COUNTRY_CODE_ATTRIBUTE = "code";
-    private static final String DEFAULT_TIME_ZONE_ID_ATTRIBUTE = "default";
-    private static final String DEFAULT_TIME_ZONE_BOOST_ATTRIBUTE = "defaultBoost";
-    private static final String EVER_USES_UTC_ATTRIBUTE = "everutc";
-
-    // Country -> Time zone mapping. e.g. <id>ZoneId</id>, <id picker="n">ZoneId</id>,
-    // <id notafter={timestamp}>ZoneId</id>
-    // The default for the picker attribute when unspecified is "y".
-    // The notafter attribute is optional. It specifies a timestamp (time in milliseconds from Unix
-    // epoch start) after which the zone is not (effectively) in use. If unspecified the zone is in
-    // use forever.
-    private static final String ZONE_ID_ELEMENT = "id";
-    private static final String ZONE_SHOW_IN_PICKER_ATTRIBUTE = "picker";
-    private static final String ZONE_NOT_USED_AFTER_ATTRIBUTE = "notafter";
-
-    private static TimeZoneFinder instance;
-
-    private final ReaderSupplier xmlSource;
-
-    // Cached field for the last country looked up.
-    private CountryTimeZones lastCountryTimeZones;
-
-    private TimeZoneFinder(ReaderSupplier xmlSource) {
-        this.xmlSource = xmlSource;
-    }
-
-    /**
-     * Obtains an instance for use when resolving time zones. This method handles using the correct
-     * file when there are several to choose from. This method never returns {@code null}. No
-     * in-depth validation is performed on the file content, see {@link #validate()}.
-     */
-    @libcore.api.CorePlatformApi
-    public static TimeZoneFinder getInstance() {
-        synchronized(TimeZoneFinder.class) {
-            if (instance == null) {
-                String[] tzLookupFilePaths =
-                        TimeZoneDataFiles.getTimeZoneFilePaths(TZLOOKUP_FILE_NAME);
-                instance = createInstanceWithFallback(tzLookupFilePaths);
-            }
-        }
-        return instance;
-    }
-
-    // VisibleForTesting
-    public static TimeZoneFinder createInstanceWithFallback(String... tzLookupFilePaths) {
-        IOException lastException = null;
-        for (String tzLookupFilePath : tzLookupFilePaths) {
-            try {
-                // We assume that any file in /data was validated before install, and the system
-                // file was validated before the device shipped. Therefore, we do not pay the
-                // validation cost here.
-                return createInstance(tzLookupFilePath);
-            } catch (IOException e) {
-                // There's expected to be two files, and it's normal for the first file not to
-                // exist so we don't log, but keep the lastException so we can log it if there
-                // are no valid files available.
-                if (lastException != null) {
-                    e.addSuppressed(lastException);
-                }
-                lastException = e;
-            }
-        }
-
-        System.logE("No valid file found in set: " + Arrays.toString(tzLookupFilePaths)
-                + " Printing exceptions and falling back to empty map.", lastException);
-        return createInstanceForTests("<timezones><countryzones /></timezones>");
-    }
-
-    /**
-     * Obtains an instance using a specific data file, throwing an IOException if the file does not
-     * exist or is not readable. This method never returns {@code null}. No in-depth validation is
-     * performed on the file content, see {@link #validate()}.
-     */
-    @libcore.api.CorePlatformApi
-    public static TimeZoneFinder createInstance(String path) throws IOException {
-        ReaderSupplier xmlSupplier = ReaderSupplier.forFile(path, StandardCharsets.UTF_8);
-        return new TimeZoneFinder(xmlSupplier);
-    }
-
-    /** Used to create an instance using an in-memory XML String instead of a file. */
-    // VisibleForTesting
-    public static TimeZoneFinder createInstanceForTests(String xml) {
-        return new TimeZoneFinder(ReaderSupplier.forString(xml));
-    }
-
-    /**
-     * Parses the data file, throws an exception if it is invalid or cannot be read.
-     */
-    @libcore.api.CorePlatformApi
-    public void validate() throws IOException {
-        try {
-            processXml(new TimeZonesValidator());
-        } catch (XmlPullParserException e) {
-            throw new IOException("Parsing error", e);
-        }
-    }
-
-    /**
-     * Returns the IANA rules version associated with the data. If there is no version information
-     * or there is a problem reading the file then {@code null} is returned.
-     */
-    @libcore.api.CorePlatformApi
-    public String getIanaVersion() {
-        IanaVersionExtractor ianaVersionExtractor = new IanaVersionExtractor();
-        try {
-            processXml(ianaVersionExtractor);
-            return ianaVersionExtractor.getIanaVersion();
-        } catch (XmlPullParserException | IOException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Loads all the country &lt;-&gt; time zone mapping data into memory. This method can return
-     * {@code null} in the event of an error while reading the underlying data files.
-     */
-    @libcore.api.CorePlatformApi
-    public CountryZonesFinder getCountryZonesFinder() {
-        CountryZonesLookupExtractor extractor = new CountryZonesLookupExtractor();
-        try {
-            processXml(extractor);
-
-            return extractor.getCountryZonesLookup();
-        } catch (XmlPullParserException | IOException e) {
-            System.logW("Error reading country zones ", e);
-            return null;
-        }
-    }
-
-    /**
-     * Returns a {@link CountryTimeZones} object associated with the specified country code.
-     * Caching is handled as needed. If the country code is not recognized or there is an error
-     * during lookup this method can return null.
-     */
-    @libcore.api.CorePlatformApi
-    public CountryTimeZones lookupCountryTimeZones(String countryIso) {
-        synchronized (this) {
-            if (lastCountryTimeZones != null && lastCountryTimeZones.isForCountryCode(countryIso)) {
-                return lastCountryTimeZones;
-            }
-        }
-
-        SelectiveCountryTimeZonesExtractor extractor =
-                new SelectiveCountryTimeZonesExtractor(countryIso);
-        try {
-            processXml(extractor);
-
-            CountryTimeZones countryTimeZones = extractor.getValidatedCountryTimeZones();
-            if (countryTimeZones == null) {
-                // None matched. Return the null but don't change the cached value.
-                return null;
-            }
-
-            // Update the cached value.
-            synchronized (this) {
-                lastCountryTimeZones = countryTimeZones;
-            }
-            return countryTimeZones;
-        } catch (XmlPullParserException | IOException e) {
-            System.logW("Error reading country zones ", e);
-
-            // Error - don't change the cached value.
-            return null;
-        }
-    }
-
-    /**
-     * Processes the XML, applying the {@link TimeZonesProcessor} to the &lt;countryzones&gt;
-     * element. Processing can terminate early if the {@link TimeZonesProcessor#processCountryZones(
-     * String, String, boolean, boolean, List, String)} returns {@link TimeZonesProcessor#HALT} or
-     * it throws an exception.
-     */
-    private void processXml(TimeZonesProcessor processor)
-            throws XmlPullParserException, IOException {
-        try (Reader reader = xmlSource.get()) {
-            XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
-            xmlPullParserFactory.setNamespaceAware(false);
-
-            XmlPullParser parser = xmlPullParserFactory.newPullParser();
-            parser.setInput(reader);
-
-            /*
-             * The expected XML structure is:
-             * <timezones ianaversion="2017b">
-             *   <countryzones>
-             *     <country code="us" default="America/New_York">
-             *       <id>America/New_York"</id>
-             *       ...
-             *       <id picker="n">America/Indiana/Vincennes</id>
-             *       ...
-             *       <id>America/Los_Angeles</id>
-             *     </country>
-             *     <country code="gb" default="Europe/London" defaultBoost="y">
-             *       <id>Europe/London</id>
-             *     </country>
-             *   </countryzones>
-             * </timezones>
-             */
-
-            findNextStartTagOrThrowNoRecurse(parser, TIMEZONES_ELEMENT);
-
-            // We do not require the ianaversion attribute be present. It is metadata that helps
-            // with versioning but is not required.
-            String ianaVersion = parser.getAttributeValue(
-                    null /* namespace */, IANA_VERSION_ATTRIBUTE);
-            if (processor.processHeader(ianaVersion) == TimeZonesProcessor.HALT) {
-                return;
-            }
-
-            // There is only one expected sub-element <countryzones> in the format currently, skip
-            // over anything before it.
-            findNextStartTagOrThrowNoRecurse(parser, COUNTRY_ZONES_ELEMENT);
-
-            if (processCountryZones(parser, processor) == TimeZonesProcessor.HALT) {
-                return;
-            }
-
-            // Make sure we are on the </countryzones> tag.
-            checkOnEndTag(parser, COUNTRY_ZONES_ELEMENT);
-
-            // Advance to the next tag.
-            parser.next();
-
-            // Skip anything until </timezones>, and make sure the file is not truncated and we can
-            // find the end.
-            consumeUntilEndTag(parser, TIMEZONES_ELEMENT);
-
-            // Make sure we are on the </timezones> tag.
-            checkOnEndTag(parser, TIMEZONES_ELEMENT);
-        }
-    }
-
-    private static boolean processCountryZones(XmlPullParser parser,
-            TimeZonesProcessor processor) throws IOException, XmlPullParserException {
-
-        // Skip over any unexpected elements and process <country> elements.
-        while (findNextStartTagOrEndTagNoRecurse(parser, COUNTRY_ELEMENT)) {
-            String code = parser.getAttributeValue(
-                    null /* namespace */, COUNTRY_CODE_ATTRIBUTE);
-            if (code == null || code.isEmpty()) {
-                throw new XmlPullParserException(
-                        "Unable to find country code: " + parser.getPositionDescription());
-            }
-
-            String defaultTimeZoneId = parser.getAttributeValue(
-                    null /* namespace */, DEFAULT_TIME_ZONE_ID_ATTRIBUTE);
-            if (defaultTimeZoneId == null || defaultTimeZoneId.isEmpty()) {
-                throw new XmlPullParserException("Unable to find default time zone ID: "
-                        + parser.getPositionDescription());
-            }
-
-            boolean defaultTimeZoneBoost = parseBooleanAttribute(parser,
-                    DEFAULT_TIME_ZONE_BOOST_ATTRIBUTE, false);
-
-            Boolean everUsesUtc = parseBooleanAttribute(
-                    parser, EVER_USES_UTC_ATTRIBUTE, null /* defaultValue */);
-            if (everUsesUtc == null) {
-                // There is no valid default: we require this to be specified.
-                throw new XmlPullParserException(
-                        "Unable to find UTC hint attribute (" + EVER_USES_UTC_ATTRIBUTE + "): "
-                        + parser.getPositionDescription());
-            }
-
-            String debugInfo = parser.getPositionDescription();
-            List<TimeZoneMapping> timeZoneMappings = parseTimeZoneMappings(parser);
-            boolean result = processor.processCountryZones(code, defaultTimeZoneId,
-                    defaultTimeZoneBoost, everUsesUtc, timeZoneMappings, debugInfo);
-            if (result == TimeZonesProcessor.HALT) {
-                return TimeZonesProcessor.HALT;
-            }
-
-            // Make sure we are on the </country> element.
-            checkOnEndTag(parser, COUNTRY_ELEMENT);
-        }
-
-        return TimeZonesProcessor.CONTINUE;
-    }
-
-    private static List<TimeZoneMapping> parseTimeZoneMappings(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        List<TimeZoneMapping> timeZoneMappings = new ArrayList<>();
-
-        // Skip over any unexpected elements and process <id> elements.
-        while (findNextStartTagOrEndTagNoRecurse(parser, ZONE_ID_ELEMENT)) {
-            // The picker attribute is optional and defaulted to true.
-            boolean showInPicker = parseBooleanAttribute(
-                    parser, ZONE_SHOW_IN_PICKER_ATTRIBUTE, true /* defaultValue */);
-            Long notUsedAfter = parseLongAttribute(
-                    parser, ZONE_NOT_USED_AFTER_ATTRIBUTE, null /* defaultValue */);
-            String zoneIdString = consumeText(parser);
-
-            // Make sure we are on the </id> element.
-            checkOnEndTag(parser, ZONE_ID_ELEMENT);
-
-            // Process the TimeZoneMapping.
-            if (zoneIdString == null || zoneIdString.length() == 0) {
-                throw new XmlPullParserException("Missing text for " + ZONE_ID_ELEMENT + "): "
-                        + parser.getPositionDescription());
-            }
-
-            TimeZoneMapping timeZoneMapping =
-                    new TimeZoneMapping(zoneIdString, showInPicker, notUsedAfter);
-            timeZoneMappings.add(timeZoneMapping);
-        }
-
-        // The list is made unmodifiable to avoid callers changing it.
-        return Collections.unmodifiableList(timeZoneMappings);
-    }
-
-    /**
-     * Processes &lt;timezones&gt; data.
-     */
-    private interface TimeZonesProcessor {
-
-        boolean CONTINUE = true;
-        boolean HALT = false;
-
-        /**
-         * Return {@link #CONTINUE} if processing of the XML should continue, {@link #HALT} if it
-         * should stop (but without considering this an error). Problems with the data are
-         * reported as an exception.
-         *
-         * <p>The default implementation returns {@link #CONTINUE}.
-         */
-        default boolean processHeader(String ianaVersion) throws XmlPullParserException {
-            return CONTINUE;
-        }
-
-        /**
-         * Returns {@link #CONTINUE} if processing of the XML should continue, {@link #HALT} if it
-         * should stop (but without considering this an error). Problems with the data are
-         * reported as an exception.
-         *
-         * <p>The default implementation returns {@link #CONTINUE}.
-         */
-        default boolean processCountryZones(String countryIso, String defaultTimeZoneId,
-                boolean defaultTimeZoneBoost, boolean everUsesUtc,
-                List<TimeZoneMapping> timeZoneMappings, String debugInfo)
-                throws XmlPullParserException {
-            return CONTINUE;
-        }
-    }
-
-    /**
-     * Validates &lt;countryzones&gt; elements. Intended to be used before a proposed installation
-     * of new data. To be valid the country ISO code must be normalized, unique, the default time
-     * zone ID must be one of the time zones IDs and the time zone IDs list must not be empty. The
-     * IDs themselves are not checked against other data to see if they are recognized because other
-     * classes will not have been updated with the associated new time zone data yet and so will not
-     * be aware of newly added IDs.
-     */
-    private static class TimeZonesValidator implements TimeZonesProcessor {
-
-        private final Set<String> knownCountryCodes = new HashSet<>();
-
-        @Override
-        public boolean processCountryZones(String countryIso, String defaultTimeZoneId,
-                boolean defaultTimeZoneBoost, boolean everUsesUtc,
-                List<TimeZoneMapping> timeZoneMappings, String debugInfo)
-                throws XmlPullParserException {
-            if (!normalizeCountryIso(countryIso).equals(countryIso)) {
-                throw new XmlPullParserException("Country code: " + countryIso
-                        + " is not normalized at " + debugInfo);
-            }
-            if (knownCountryCodes.contains(countryIso)) {
-                throw new XmlPullParserException("Second entry for country code: " + countryIso
-                        + " at " + debugInfo);
-            }
-            if (timeZoneMappings.isEmpty()) {
-                throw new XmlPullParserException("No time zone IDs for country code: " + countryIso
-                        + " at " + debugInfo);
-            }
-            if (!TimeZoneMapping.containsTimeZoneId(timeZoneMappings, defaultTimeZoneId)) {
-                throw new XmlPullParserException("defaultTimeZoneId for country code: "
-                        + countryIso + " is not one of the zones " + timeZoneMappings + " at "
-                        + debugInfo);
-            }
-            knownCountryCodes.add(countryIso);
-
-            return CONTINUE;
-        }
-    }
-
-    /**
-     * Reads just the IANA version from the file header. The version is then available via
-     * {@link #getIanaVersion()}.
-     */
-    private static class IanaVersionExtractor implements TimeZonesProcessor {
-
-        private String ianaVersion;
-
-        @Override
-        public boolean processHeader(String ianaVersion) throws XmlPullParserException {
-            this.ianaVersion = ianaVersion;
-            return HALT;
-        }
-
-        public String getIanaVersion() {
-            return ianaVersion;
-        }
-    }
-
-    /**
-     * Reads all country time zone information into memory and makes it available as a
-     * {@link CountryZonesFinder}.
-     */
-    private static class CountryZonesLookupExtractor implements TimeZonesProcessor {
-        private List<CountryTimeZones> countryTimeZonesList = new ArrayList<>(250 /* default */);
-
-        @Override
-        public boolean processCountryZones(String countryIso, String defaultTimeZoneId,
-                boolean defaultTimeZoneBoost, boolean everUsesUtc,
-                List<TimeZoneMapping> timeZoneMappings, String debugInfo)
-                throws XmlPullParserException {
-
-            CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                    countryIso, defaultTimeZoneId, defaultTimeZoneBoost, everUsesUtc,
-                    timeZoneMappings, debugInfo);
-            countryTimeZonesList.add(countryTimeZones);
-            return CONTINUE;
-        }
-
-        CountryZonesFinder getCountryZonesLookup() {
-            return new CountryZonesFinder(countryTimeZonesList);
-        }
-    }
-
-    /**
-     * Extracts <em>validated</em> time zones information associated with a specific country code.
-     * Processing is halted when the country code is matched and the validated result is also made
-     * available via {@link #getValidatedCountryTimeZones()}.
-     */
-    private static class SelectiveCountryTimeZonesExtractor implements TimeZonesProcessor {
-
-        private final String countryCodeToMatch;
-        private CountryTimeZones validatedCountryTimeZones;
-
-        private SelectiveCountryTimeZonesExtractor(String countryCodeToMatch) {
-            this.countryCodeToMatch = normalizeCountryIso(countryCodeToMatch);
-        }
-
-        @Override
-        public boolean processCountryZones(String countryIso, String defaultTimeZoneId,
-                boolean defaultTimeZoneBoost, boolean everUsesUtc,
-                List<TimeZoneMapping> timeZoneMappings, String debugInfo) {
-            countryIso = normalizeCountryIso(countryIso);
-            if (!countryCodeToMatch.equals(countryIso)) {
-                return CONTINUE;
-            }
-            validatedCountryTimeZones = CountryTimeZones.createValidated(countryIso,
-                    defaultTimeZoneId, defaultTimeZoneBoost, everUsesUtc, timeZoneMappings,
-                    debugInfo);
-
-            return HALT;
-        }
-
-        /**
-         * Returns the CountryTimeZones that matched, or {@code null} if there were no matches.
-         */
-        CountryTimeZones getValidatedCountryTimeZones() {
-            return validatedCountryTimeZones;
-        }
-    }
-
-}
diff --git a/luni/src/main/java/libcore/timezone/TzDataSetVersion.java b/luni/src/main/java/libcore/timezone/TzDataSetVersion.java
deleted file mode 100644
index 8b97069b..0000000
--- a/luni/src/main/java/libcore/timezone/TzDataSetVersion.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Constants and logic associated with the time zone data version file.
- * @hide
- */
-@libcore.api.CorePlatformApi
-public final class TzDataSetVersion {
-
-    // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
-    /**
-     * The name typically given to the {@link TzDataSetVersion} file. See
-     * {@link TzDataSetVersion#readFromFile(File)}.
-     */
-    @libcore.api.CorePlatformApi
-    public static final String DEFAULT_FILE_NAME = "tz_version";
-
-    /**
-     * The major tz data format version supported by this device.
-     * Increment this for non-backwards compatible changes to the tz data format. Reset the minor
-     * version to 1 when doing so.
-     */
-    // @VisibleForTesting : Keep this inline-able: it is used from CTS tests.
-    public static final int CURRENT_FORMAT_MAJOR_VERSION = 4; // Android R
-
-    /**
-     * Returns the major tz data format version supported by this device.
-     */
-    @libcore.api.CorePlatformApi
-    public static int currentFormatMajorVersion() {
-        return CURRENT_FORMAT_MAJOR_VERSION;
-    }
-
-    /**
-     * The minor tz data format version supported by this device. Increment this for
-     * backwards-compatible changes to the tz data format.
-     */
-    // @VisibleForTesting : Keep this inline-able: it is used from CTS tests.
-    public static final int CURRENT_FORMAT_MINOR_VERSION = 1;
-
-    /**
-     * Returns the minor tz data format version supported by this device.
-     */
-    @libcore.api.CorePlatformApi
-    public static int currentFormatMinorVersion() {
-        return CURRENT_FORMAT_MINOR_VERSION;
-    }
-
-    /** The full major + minor tz data format version for this device. */
-    private static final String FULL_CURRENT_FORMAT_VERSION_STRING =
-            toFormatVersionString(CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION);
-
-    private static final int FORMAT_VERSION_STRING_LENGTH =
-            FULL_CURRENT_FORMAT_VERSION_STRING.length();
-    private static final Pattern FORMAT_VERSION_PATTERN = Pattern.compile("(\\d{3})\\.(\\d{3})");
-
-    /** A pattern that matches the IANA rules value of a rules update. e.g. "2016g" */
-    private static final Pattern RULES_VERSION_PATTERN = Pattern.compile("(\\d{4}\\w)");
-
-    private static final int RULES_VERSION_LENGTH = 5;
-
-    /** A pattern that matches the revision of a rules update. e.g. "001" */
-    private static final Pattern REVISION_PATTERN = Pattern.compile("(\\d{3})");
-
-    private static final int REVISION_LENGTH = 3;
-
-    /**
-     * The length of a well-formed tz data set version file:
-     * {Format version}|{Rule version}|{Revision}
-     */
-    private static final int TZ_DATA_VERSION_FILE_LENGTH = FORMAT_VERSION_STRING_LENGTH + 1
-            + RULES_VERSION_LENGTH
-            + 1 + REVISION_LENGTH;
-
-    private static final Pattern TZ_DATA_VERSION_FILE_PATTERN = Pattern.compile(
-            FORMAT_VERSION_PATTERN.pattern() + "\\|"
-                    + RULES_VERSION_PATTERN.pattern() + "\\|"
-                    + REVISION_PATTERN.pattern()
-                    + ".*" /* ignore trailing */);
-
-    private final int formatMajorVersion;
-    private final int formatMinorVersion;
-    private final String rulesVersion;
-    private final int revision;
-
-    @libcore.api.CorePlatformApi
-    public TzDataSetVersion(int formatMajorVersion, int formatMinorVersion, String rulesVersion,
-            int revision) throws TzDataSetException {
-        this.formatMajorVersion = validate3DigitVersion(formatMajorVersion);
-        this.formatMinorVersion = validate3DigitVersion(formatMinorVersion);
-        if (!RULES_VERSION_PATTERN.matcher(rulesVersion).matches()) {
-            throw new TzDataSetException("Invalid rulesVersion: " + rulesVersion);
-        }
-        this.rulesVersion = rulesVersion;
-        this.revision = validate3DigitVersion(revision);
-    }
-
-    // VisibleForTesting
-    public static TzDataSetVersion fromBytes(byte[] bytes) throws TzDataSetException {
-        String tzDataVersion = new String(bytes, StandardCharsets.US_ASCII);
-        try {
-            Matcher matcher = TZ_DATA_VERSION_FILE_PATTERN.matcher(tzDataVersion);
-            if (!matcher.matches()) {
-                throw new TzDataSetException(
-                        "Invalid tz data version string: \"" + tzDataVersion + "\"");
-            }
-            String formatMajorVersion = matcher.group(1);
-            String formatMinorVersion = matcher.group(2);
-            String rulesVersion = matcher.group(3);
-            String revision = matcher.group(4);
-            return new TzDataSetVersion(
-                    from3DigitVersionString(formatMajorVersion),
-                    from3DigitVersionString(formatMinorVersion),
-                    rulesVersion,
-                    from3DigitVersionString(revision));
-        } catch (IndexOutOfBoundsException e) {
-            // The use of the regexp above should make this impossible.
-            throw new TzDataSetException(
-                    "tz data version string too short: \"" + tzDataVersion + "\"");
-        }
-    }
-
-    // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
-    @libcore.api.CorePlatformApi
-    public static TzDataSetVersion readFromFile(File file) throws IOException, TzDataSetException {
-        byte[] versionBytes = readBytes(file, TzDataSetVersion.TZ_DATA_VERSION_FILE_LENGTH);
-        return fromBytes(versionBytes);
-    }
-
-    /**
-     * Reads the version of time zone data supplied by the time zone data module.
-     */
-    @libcore.api.CorePlatformApi
-    public static TzDataSetVersion readTimeZoneModuleVersion()
-            throws IOException, TzDataSetException {
-        String tzVersionFileName =
-                TimeZoneDataFiles.getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
-        return readFromFile(new File(tzVersionFileName));
-    }
-
-    @libcore.api.CorePlatformApi
-    public int getFormatMajorVersion() {
-        return formatMajorVersion;
-    }
-
-    @libcore.api.CorePlatformApi
-    public int getFormatMinorVersion() {
-        return formatMinorVersion;
-    }
-
-    @libcore.api.CorePlatformApi
-    public String getRulesVersion() {
-        return rulesVersion;
-    }
-
-    // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
-    @libcore.api.CorePlatformApi
-    public int getRevision() {
-        return revision;
-    }
-
-    // Remove from CorePlatformApi when all users in platform code are removed. http://b/123398797
-    @libcore.api.CorePlatformApi
-    public byte[] toBytes() {
-        return toBytes(formatMajorVersion, formatMinorVersion, rulesVersion, revision);
-    }
-
-    private static byte[] toBytes(
-            int majorFormatVersion, int minorFormatVerison, String rulesVersion, int revision) {
-        return (toFormatVersionString(majorFormatVersion, minorFormatVerison)
-                + "|" + rulesVersion + "|" + to3DigitVersionString(revision))
-                .getBytes(StandardCharsets.US_ASCII);
-    }
-
-    @libcore.api.CorePlatformApi
-    public static boolean isCompatibleWithThisDevice(TzDataSetVersion tzDataVersion) {
-        return (CURRENT_FORMAT_MAJOR_VERSION == tzDataVersion.formatMajorVersion)
-                && (CURRENT_FORMAT_MINOR_VERSION <= tzDataVersion.formatMinorVersion);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        TzDataSetVersion that = (TzDataSetVersion) o;
-
-        if (formatMajorVersion != that.formatMajorVersion) {
-            return false;
-        }
-        if (formatMinorVersion != that.formatMinorVersion) {
-            return false;
-        }
-        if (revision != that.revision) {
-            return false;
-        }
-        return rulesVersion.equals(that.rulesVersion);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = formatMajorVersion;
-        result = 31 * result + formatMinorVersion;
-        result = 31 * result + rulesVersion.hashCode();
-        result = 31 * result + revision;
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "TzDataSetVersion{" +
-                "formatMajorVersion=" + formatMajorVersion +
-                ", formatMinorVersion=" + formatMinorVersion +
-                ", rulesVersion='" + rulesVersion + '\'' +
-                ", revision=" + revision +
-                '}';
-    }
-
-    /**
-     * Returns a version as a zero-padded three-digit String value.
-     */
-    private static String to3DigitVersionString(int version) {
-        try {
-            return String.format(Locale.ROOT, "%03d", validate3DigitVersion(version));
-        } catch (TzDataSetException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    /**
-     * Validates and parses a zero-padded three-digit String value.
-     */
-    private static int from3DigitVersionString(String versionString) throws TzDataSetException {
-        final String parseErrorMessage = "versionString must be a zero padded, 3 digit, positive"
-                + " decimal integer";
-        if (versionString.length() != 3) {
-            throw new TzDataSetException(parseErrorMessage);
-        }
-        try {
-            int version = Integer.parseInt(versionString);
-            return validate3DigitVersion(version);
-        } catch (NumberFormatException e) {
-            throw new TzDataSetException(parseErrorMessage, e);
-        }
-    }
-
-    private static int validate3DigitVersion(int value) throws TzDataSetException {
-        // 0 is allowed but is reserved for testing.
-        if (value < 0 || value > 999) {
-            throw new TzDataSetException("Expected 0 <= value <= 999, was " + value);
-        }
-        return value;
-    }
-
-    private static String toFormatVersionString(int majorFormatVersion, int minorFormatVersion) {
-        return to3DigitVersionString(majorFormatVersion)
-                + "." + to3DigitVersionString(minorFormatVersion);
-    }
-
-    /**
-     * Reads up to {@code maxBytes} bytes from the specified file. The returned array can be
-     * shorter than {@code maxBytes} if the file is shorter.
-     */
-    private static byte[] readBytes(File file, int maxBytes) throws IOException {
-        if (maxBytes <= 0) {
-            throw new IllegalArgumentException("maxBytes ==" + maxBytes);
-        }
-
-        try (FileInputStream in = new FileInputStream(file)) {
-            byte[] max = new byte[maxBytes];
-            int bytesRead = in.read(max, 0, maxBytes);
-            byte[] toReturn = new byte[bytesRead];
-            System.arraycopy(max, 0, toReturn, 0, bytesRead);
-            return toReturn;
-        }
-    }
-
-    /**
-     * A checked exception used in connection with time zone data sets.
-     */
-    @libcore.api.CorePlatformApi
-    public static class TzDataSetException extends Exception {
-
-        @libcore.api.CorePlatformApi
-        public TzDataSetException(String message) {
-            super(message);
-        }
-
-        @libcore.api.CorePlatformApi
-        public TzDataSetException(String message, Throwable cause) {
-            super(message, cause);
-        }
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/XmlUtils.java b/luni/src/main/java/libcore/timezone/XmlUtils.java
deleted file mode 100644
index a9616f9..0000000
--- a/luni/src/main/java/libcore/timezone/XmlUtils.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.timezone;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Locale;
-
-class XmlUtils {
-
-    private static final String TRUE_ATTRIBUTE_VALUE = "y";
-
-    private static final String FALSE_ATTRIBUTE_VALUE = "n";
-
-    private XmlUtils() {}
-
-    /**
-     * Parses an attribute value, which must be either {@code null} or a valid signed long value.
-     * If the attribute value is {@code null} then {@code defaultValue} is returned. If the
-     * attribute is present but not a valid long value then an XmlPullParserException is thrown.
-     */
-    static Long parseLongAttribute(XmlPullParser parser, String attributeName,
-            Long defaultValue) throws XmlPullParserException {
-        String attributeValueString = parser.getAttributeValue(null /* namespace */, attributeName);
-        if (attributeValueString == null) {
-            return defaultValue;
-        }
-        try {
-            return Long.parseLong(attributeValueString);
-        } catch (NumberFormatException e) {
-            throw new XmlPullParserException("Attribute \"" + attributeName
-                    + "\" is not a long value: " + parser.getPositionDescription());
-        }
-    }
-
-    /**
-     * Parses an attribute value, which must be either {@code null}, {@code "y"} or {@code "n"}.
-     * If the attribute value is {@code null} then {@code defaultValue} is returned. If the
-     * attribute is present but not "y" or "n" then an XmlPullParserException is thrown.
-     */
-    static Boolean parseBooleanAttribute(XmlPullParser parser,
-            String attributeName, Boolean defaultValue) throws XmlPullParserException {
-        String attributeValueString = parser.getAttributeValue(null /* namespace */, attributeName);
-        if (attributeValueString == null) {
-            return defaultValue;
-        }
-        boolean isTrue = TRUE_ATTRIBUTE_VALUE.equals(attributeValueString);
-        if (!(isTrue || FALSE_ATTRIBUTE_VALUE.equals(attributeValueString))) {
-            throw new XmlPullParserException("Attribute \"" + attributeName
-                    + "\" is not \"y\" or \"n\": " + parser.getPositionDescription());
-        }
-        return isTrue;
-    }
-
-    /**
-     * Advances the the parser to the START_TAG for the specified element without decreasing the
-     * depth, or increasing the depth by more than one (i.e. no recursion into child nodes).
-     * If the next (non-nested) END_TAG an exception is thrown. Throws an exception if the end of
-     * the document is encountered unexpectedly.
-     */
-    static void findNextStartTagOrThrowNoRecurse(XmlPullParser parser, String elementName)
-            throws IOException, XmlPullParserException {
-        if (!findNextStartTagOrEndTagNoRecurse(parser, elementName)) {
-            throw new XmlPullParserException("No next element found with name " + elementName);
-        }
-    }
-
-    /**
-     * Advances the the parser to the START_TAG for the specified element without decreasing the
-     * depth, or increasing the depth by more than one (i.e. no recursion into child nodes).
-     * Returns {@code true} if the requested START_TAG is found, or {@code false} when the next
-     * (non-nested) END_TAG is encountered instead. Throws an exception if the end of the document
-     * is encountered unexpectedly.
-     */
-    static boolean findNextStartTagOrEndTagNoRecurse(XmlPullParser parser, String elementName)
-            throws IOException, XmlPullParserException {
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-            switch (type) {
-                case XmlPullParser.START_TAG:
-                    String currentElementName = parser.getName();
-                    if (elementName.equals(currentElementName)) {
-                        return true;
-                    }
-
-                    // It was not the START_TAG we were looking for. Consume until the end.
-                    parser.next();
-                    consumeUntilEndTag(parser, currentElementName);
-                    break;
-                case XmlPullParser.END_TAG:
-                    return false;
-                default:
-                    // Ignore.
-                    break;
-            }
-        }
-        throw new XmlPullParserException("Unexpected end of document while looking for "
-                + elementName);
-    }
-
-    /**
-     * Consume any remaining contents of an element and move to the END_TAG. Used when processing
-     * within an element can stop.
-     *
-     * <p>When called, the parser must be pointing at one of:
-     * <ul>
-     *     <li>the END_TAG we are looking for</li>
-     *     <li>a TEXT</li>
-     *     <li>a START_TAG nested within the element that can be consumed</li>
-     * </ul>
-     * Note: The parser synthesizes an END_TAG for self-closing tags so this works for them too.
-     */
-    static void consumeUntilEndTag(XmlPullParser parser, String elementName)
-            throws IOException, XmlPullParserException {
-
-        if (isEndTag(parser, elementName)) {
-            // Early return - we are already there.
-            return;
-        }
-
-        // Keep track of the required depth in case there are nested elements to be consumed.
-        // Both the name and the depth must match our expectation to complete.
-
-        int requiredDepth = parser.getDepth();
-        // A TEXT tag would be at the same depth as the END_TAG we are looking for.
-        if (parser.getEventType() == XmlPullParser.START_TAG) {
-            // A START_TAG would have incremented the depth, so we're looking for an END_TAG one
-            // higher than the current tag.
-            requiredDepth--;
-        }
-
-        while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-            int type = parser.next();
-
-            int currentDepth = parser.getDepth();
-            if (currentDepth < requiredDepth) {
-                throw new XmlPullParserException(
-                        "Unexpected depth while looking for end tag: "
-                                + parser.getPositionDescription());
-            } else if (currentDepth == requiredDepth) {
-                if (type == XmlPullParser.END_TAG) {
-                    if (elementName.equals(parser.getName())) {
-                        return;
-                    }
-                    throw new XmlPullParserException(
-                            "Unexpected eng tag: " + parser.getPositionDescription());
-                }
-            }
-            // Everything else is either a type we are not interested in or is too deep and so is
-            // ignored.
-        }
-        throw new XmlPullParserException("Unexpected end of document");
-    }
-
-    /**
-     * Throws an exception if the current element is not an end tag.
-     * Note: The parser synthesizes an END_TAG for self-closing tags so this works for them too.
-     */
-    static void checkOnEndTag(XmlPullParser parser, String elementName)
-            throws XmlPullParserException {
-        if (!isEndTag(parser, elementName)) {
-            throw new XmlPullParserException(
-                    "Unexpected tag encountered: " + parser.getPositionDescription());
-        }
-    }
-
-    /**
-     * Returns true if the current tag is an end tag.
-     * Note: The parser synthesizes an END_TAG for self-closing tags so this works for them too.
-     */
-    private static boolean isEndTag(XmlPullParser parser, String elementName)
-            throws XmlPullParserException {
-        return parser.getEventType() == XmlPullParser.END_TAG
-                && parser.getName().equals(elementName);
-    }
-
-    static String normalizeCountryIso(String countryIso) {
-        // Lowercase ASCII is normalized for the purposes of the input files and the code in this
-        // class and related classes.
-        return countryIso.toLowerCase(Locale.US);
-    }
-
-    /**
-     * Reads the text inside the current element. Should be called when the parser is currently
-     * on the START_TAG before the TEXT. The parser will be positioned on the END_TAG after this
-     * call when it completes successfully.
-     */
-    static String consumeText(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-
-        int type = parser.next();
-        String text;
-        if (type == XmlPullParser.TEXT) {
-            text = parser.getText();
-        } else {
-            throw new XmlPullParserException("Text not found. Found type=" + type
-                    + " at " + parser.getPositionDescription());
-        }
-
-        type = parser.next();
-        if (type != XmlPullParser.END_TAG) {
-            throw new XmlPullParserException(
-                    "Unexpected nested tag or end of document when expecting text: type=" + type
-                            + " at " + parser.getPositionDescription());
-        }
-        return text;
-    }
-
-    /**
-     * A source of Readers that can be used repeatedly.
-     */
-    interface ReaderSupplier {
-        /** Returns a Reader. Throws an IOException if the Reader cannot be created. */
-        Reader get() throws IOException;
-
-        static ReaderSupplier forFile(String fileName, Charset charSet) throws IOException {
-            Path file = Paths.get(fileName);
-            if (!Files.exists(file)) {
-                throw new FileNotFoundException(fileName + " does not exist");
-            }
-            if (!Files.isRegularFile(file) && Files.isReadable(file)) {
-                throw new IOException(fileName + " must be a regular readable file.");
-            }
-            return () -> Files.newBufferedReader(file, charSet);
-        }
-
-        static ReaderSupplier forString(String xml) {
-            return () -> new StringReader(xml);
-        }
-    }
-}
diff --git a/luni/src/main/java/libcore/timezone/ZoneInfoDb.java b/luni/src/main/java/libcore/timezone/ZoneInfoDb.java
deleted file mode 100644
index a94d293..0000000
--- a/luni/src/main/java/libcore/timezone/ZoneInfoDb.java
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright (C) 2007 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.timezone;
-
-import android.system.ErrnoException;
-import dalvik.annotation.optimization.ReachabilitySensitive;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import libcore.io.BufferIterator;
-import libcore.io.MemoryMappedFile;
-import libcore.util.BasicLruCache;
-import libcore.util.ZoneInfo;
-
-/**
- * A class used to initialize the time zone database. This implementation uses the
- * Olson tzdata as the source of time zone information. However, to conserve
- * disk space (inodes) and reduce I/O, all the data is concatenated into a single file,
- * with an index to indicate the starting position of each time zone record.
- *
- * @hide - used to implement TimeZone
- */
-@libcore.api.CorePlatformApi
-public final class ZoneInfoDb implements AutoCloseable {
-
-  // VisibleForTesting
-  public static final String TZDATA_FILE_NAME = "tzdata";
-
-  private static final ZoneInfoDb DATA = ZoneInfoDb.loadTzDataWithFallback(
-          TimeZoneDataFiles.getTimeZoneFilePaths(TZDATA_FILE_NAME));
-
-  // The database reserves 40 bytes for each id.
-  private static final int SIZEOF_TZNAME = 40;
-
-  // The database uses 32-bit (4 byte) integers.
-  private static final int SIZEOF_TZINT = 4;
-
-  // Each index entry takes up this number of bytes.
-  public static final int SIZEOF_INDEX_ENTRY = SIZEOF_TZNAME + 3 * SIZEOF_TZINT;
-
-  /**
-   * {@code true} if {@link #close()} has been called meaning the instance cannot provide any
-   * data.
-   */
-  private boolean closed;
-
-  /**
-   * Rather than open, read, and close the big data file each time we look up a time zone,
-   * we map the big data file during startup, and then just use the MemoryMappedFile.
-   *
-   * At the moment, this "big" data file is about 500 KiB. At some point, that will be small
-   * enough that we could just keep the byte[] in memory, but using mmap(2) like this has the
-   * nice property that even if someone replaces the file under us (because multiple gservices
-   * updates have gone out, say), we still get a consistent (if outdated) view of the world.
-   */
-  // Android-added: @ReachabilitySensitive
-  @ReachabilitySensitive
-  private MemoryMappedFile mappedFile;
-
-  private String version;
-  private String zoneTab;
-
-  /**
-   * The 'ids' array contains time zone ids sorted alphabetically, for binary searching.
-   * The other two arrays are in the same order. 'byteOffsets' gives the byte offset
-   * of each time zone, and 'rawUtcOffsetsCache' gives the time zone's raw UTC offset.
-   */
-  private String[] ids;
-  private int[] byteOffsets;
-  private int[] rawUtcOffsetsCache; // Access this via getRawUtcOffsets instead.
-
-  /**
-   * ZoneInfo objects are worth caching because they are expensive to create.
-   * See http://b/8270865 for context.
-   */
-  private final static int CACHE_SIZE = 1;
-  private final BasicLruCache<String, ZoneInfo> cache =
-      new BasicLruCache<String, ZoneInfo>(CACHE_SIZE) {
-    @Override
-    protected ZoneInfo create(String id) {
-      try {
-        return makeTimeZoneUncached(id);
-      } catch (IOException e) {
-        throw new IllegalStateException("Unable to load timezone for ID=" + id, e);
-      }
-    }
-  };
-
-  @libcore.api.CorePlatformApi
-  public static ZoneInfoDb getInstance() {
-    return DATA;
-  }
-
-  /**
-   * Loads the data at the specified paths in order, returning the first valid one as a
-   * {@link ZoneInfoDb} object. If there is no valid one found a basic fallback instance is created
-   * containing just GMT.
-   */
-  public static ZoneInfoDb loadTzDataWithFallback(String... paths) {
-    for (String path : paths) {
-      ZoneInfoDb tzData = new ZoneInfoDb();
-      if (tzData.loadData(path)) {
-        return tzData;
-      }
-    }
-
-    // We didn't find any usable tzdata on disk, so let's just hard-code knowledge of "GMT".
-    // This is actually implemented in TimeZone itself, so if this is the only time zone
-    // we report, we won't be asked any more questions.
-    System.logE("Couldn't find any " + TZDATA_FILE_NAME + " file!");
-    return ZoneInfoDb.createFallback();
-  }
-
-  /**
-   * Loads the data at the specified path and returns the {@link ZoneInfoDb} object if it is valid,
-   * otherwise {@code null}.
-   */
-  @libcore.api.CorePlatformApi
-  public static ZoneInfoDb loadTzData(String path) {
-    ZoneInfoDb tzData = new ZoneInfoDb();
-    if (tzData.loadData(path)) {
-      return tzData;
-    }
-    return null;
-  }
-
-  private static ZoneInfoDb createFallback() {
-    ZoneInfoDb tzData = new ZoneInfoDb();
-    tzData.populateFallback();
-    return tzData;
-  }
-
-  private ZoneInfoDb() {
-  }
-
-  /**
-   * Visible for testing.
-   */
-  public BufferIterator getBufferIterator(String id) {
-    checkNotClosed();
-
-    // Work out where in the big data file this time zone is.
-    int index = Arrays.binarySearch(ids, id);
-    if (index < 0) {
-      return null;
-    }
-
-    int byteOffset = byteOffsets[index];
-    BufferIterator it = mappedFile.bigEndianIterator();
-    it.skip(byteOffset);
-    return it;
-  }
-
-  private void populateFallback() {
-    version = "missing";
-    zoneTab = "# Emergency fallback data.\n";
-    ids = new String[] { "GMT" };
-    byteOffsets = rawUtcOffsetsCache = new int[1];
-  }
-
-  /**
-   * Loads the data file at the specified path. If the data is valid {@code true} will be
-   * returned and the {@link ZoneInfoDb} instance can be used. If {@code false} is returned then the
-   * ZoneInfoDB instance is left in a closed state and must be discarded.
-   */
-  private boolean loadData(String path) {
-    try {
-      mappedFile = MemoryMappedFile.mmapRO(path);
-    } catch (ErrnoException errnoException) {
-      return false;
-    }
-    try {
-      readHeader();
-      return true;
-    } catch (Exception ex) {
-      close();
-
-      // Something's wrong with the file.
-      // Log the problem and return false so we try the next choice.
-      System.logE(TZDATA_FILE_NAME + " file \"" + path + "\" was present but invalid!", ex);
-      return false;
-    }
-  }
-
-  private void readHeader() throws IOException {
-    // byte[12] tzdata_version  -- "tzdata2012f\0"
-    // int index_offset
-    // int data_offset
-    // int zonetab_offset
-    BufferIterator it = mappedFile.bigEndianIterator();
-
-    try {
-      byte[] tzdata_version = new byte[12];
-      it.readByteArray(tzdata_version, 0, tzdata_version.length);
-      String magic = new String(tzdata_version, 0, 6, StandardCharsets.US_ASCII);
-      if (!magic.equals("tzdata") || tzdata_version[11] != 0) {
-        throw new IOException("bad tzdata magic: " + Arrays.toString(tzdata_version));
-      }
-      version = new String(tzdata_version, 6, 5, StandardCharsets.US_ASCII);
-
-      final int fileSize = mappedFile.size();
-      int index_offset = it.readInt();
-      validateOffset(index_offset, fileSize);
-      int data_offset = it.readInt();
-      validateOffset(data_offset, fileSize);
-      int zonetab_offset = it.readInt();
-      validateOffset(zonetab_offset, fileSize);
-
-      if (index_offset >= data_offset || data_offset >= zonetab_offset) {
-        throw new IOException("Invalid offset: index_offset=" + index_offset
-                + ", data_offset=" + data_offset + ", zonetab_offset=" + zonetab_offset
-                + ", fileSize=" + fileSize);
-      }
-
-      readIndex(it, index_offset, data_offset);
-      readZoneTab(it, zonetab_offset, fileSize - zonetab_offset);
-    } catch (IndexOutOfBoundsException e) {
-      throw new IOException("Invalid read from data file", e);
-    }
-  }
-
-  private static void validateOffset(int offset, int size) throws IOException {
-    if (offset < 0 || offset >= size) {
-      throw new IOException("Invalid offset=" + offset + ", size=" + size);
-    }
-  }
-
-  private void readZoneTab(BufferIterator it, int zoneTabOffset, int zoneTabSize) {
-    byte[] bytes = new byte[zoneTabSize];
-    it.seek(zoneTabOffset);
-    it.readByteArray(bytes, 0, bytes.length);
-    zoneTab = new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII);
-  }
-
-  private void readIndex(BufferIterator it, int indexOffset, int dataOffset) throws IOException {
-    it.seek(indexOffset);
-
-    byte[] idBytes = new byte[SIZEOF_TZNAME];
-    int indexSize = (dataOffset - indexOffset);
-    if (indexSize % SIZEOF_INDEX_ENTRY != 0) {
-      throw new IOException("Index size is not divisible by " + SIZEOF_INDEX_ENTRY
-              + ", indexSize=" + indexSize);
-    }
-    int entryCount = indexSize / SIZEOF_INDEX_ENTRY;
-
-    byteOffsets = new int[entryCount];
-    ids = new String[entryCount];
-
-    for (int i = 0; i < entryCount; i++) {
-      // Read the fixed length timezone ID.
-      it.readByteArray(idBytes, 0, idBytes.length);
-
-      // Read the offset into the file where the data for ID can be found.
-      byteOffsets[i] = it.readInt();
-      byteOffsets[i] += dataOffset;
-
-      int length = it.readInt();
-      if (length < 44) {
-        throw new IOException("length in index file < sizeof(tzhead)");
-      }
-      it.skip(4); // Skip the unused 4 bytes that used to be the raw offset.
-
-      // Calculate the true length of the ID.
-      int len = 0;
-      while (idBytes[len] != 0 && len < idBytes.length) {
-        len++;
-      }
-      if (len == 0) {
-        throw new IOException("Invalid ID at index=" + i);
-      }
-      ids[i] = new String(idBytes, 0, len, StandardCharsets.US_ASCII);
-      if (i > 0) {
-        if (ids[i].compareTo(ids[i - 1]) <= 0) {
-          throw new IOException("Index not sorted or contains multiple entries with the same ID"
-                  + ", index=" + i + ", ids[i]=" + ids[i] + ", ids[i - 1]=" + ids[i - 1]);
-        }
-      }
-    }
-  }
-
-  @libcore.api.CorePlatformApi
-  public void validate() throws IOException {
-    checkNotClosed();
-    // Validate the data in the tzdata file by loading each and every zone.
-    for (String id : getAvailableIDs()) {
-      ZoneInfo zoneInfo = makeTimeZoneUncached(id);
-      if (zoneInfo == null) {
-        throw new IOException("Unable to find data for ID=" + id);
-      }
-    }
-  }
-
-  ZoneInfo makeTimeZoneUncached(String id) throws IOException {
-    BufferIterator it = getBufferIterator(id);
-    if (it == null) {
-      return null;
-    }
-
-    return ZoneInfo.readTimeZone(id, it, System.currentTimeMillis());
-  }
-
-  public String[] getAvailableIDs() {
-    checkNotClosed();
-    return ids.clone();
-  }
-
-  public String[] getAvailableIDs(int rawUtcOffset) {
-    checkNotClosed();
-    List<String> matches = new ArrayList<String>();
-    int[] rawUtcOffsets = getRawUtcOffsets();
-    for (int i = 0; i < rawUtcOffsets.length; ++i) {
-      if (rawUtcOffsets[i] == rawUtcOffset) {
-        matches.add(ids[i]);
-      }
-    }
-    return matches.toArray(new String[matches.size()]);
-  }
-
-  private synchronized int[] getRawUtcOffsets() {
-    if (rawUtcOffsetsCache != null) {
-      return rawUtcOffsetsCache;
-    }
-    rawUtcOffsetsCache = new int[ids.length];
-    for (int i = 0; i < ids.length; ++i) {
-      // This creates a TimeZone, which is quite expensive. Hence the cache.
-      // Note that icu4c does the same (without the cache), so if you're
-      // switching this code over to icu4j you should check its performance.
-      // Telephony shouldn't care, but someone converting a bunch of calendar
-      // events might.
-      rawUtcOffsetsCache[i] = cache.get(ids[i]).getRawOffset();
-    }
-    return rawUtcOffsetsCache;
-  }
-
-  @libcore.api.CorePlatformApi
-  public String getVersion() {
-    checkNotClosed();
-    return version;
-  }
-
-  public String getZoneTab() {
-    checkNotClosed();
-    return zoneTab;
-  }
-
-  @libcore.api.CorePlatformApi
-  public ZoneInfo makeTimeZone(String id) throws IOException {
-    checkNotClosed();
-    ZoneInfo zoneInfo = cache.get(id);
-    // The object from the cache is cloned because TimeZone / ZoneInfo are mutable.
-    return zoneInfo == null ? null : (ZoneInfo) zoneInfo.clone();
-  }
-
-  @libcore.api.CorePlatformApi
-  public boolean hasTimeZone(String id) throws IOException {
-    checkNotClosed();
-    return cache.get(id) != null;
-  }
-
-  public void close() {
-    if (!closed) {
-      closed = true;
-
-      // Clear state that takes up appreciable heap.
-      ids = null;
-      byteOffsets = null;
-      rawUtcOffsetsCache = null;
-      cache.evictAll();
-
-      // Remove the mapped file (if needed).
-      if (mappedFile != null) {
-        try {
-          mappedFile.close();
-        } catch (ErrnoException ignored) {
-        }
-        mappedFile = null;
-      }
-    }
-  }
-
-  private void checkNotClosed() throws IllegalStateException {
-    if (closed) {
-      throw new IllegalStateException("ZoneInfoDB instance is closed");
-    }
-  }
-
-  @Override protected void finalize() throws Throwable {
-    try {
-      close();
-    } finally {
-      super.finalize();
-    }
-  }
-}
diff --git a/luni/src/main/java/libcore/util/ArrayUtils.java b/luni/src/main/java/libcore/util/ArrayUtils.java
index 3a8d854..5330bc0 100644
--- a/luni/src/main/java/libcore/util/ArrayUtils.java
+++ b/luni/src/main/java/libcore/util/ArrayUtils.java
@@ -18,7 +18,6 @@
 /**
  * @hide
  */
-@libcore.api.CorePlatformApi
 public final class ArrayUtils {
     private ArrayUtils() {}
 
@@ -30,7 +29,6 @@
      * @throws ArrayIndexOutOfBoundsException if the range from {@code offset} with length
      * {@code count} is out of bounds of the array
      */
-    @libcore.api.CorePlatformApi
     public static void throwsIfOutOfBounds(int len, int offset, int count) {
         if (len < 0) {
             throw new ArrayIndexOutOfBoundsException("Negative length: " + len);
diff --git a/luni/src/main/java/libcore/util/CoreLibraryDebug.java b/luni/src/main/java/libcore/util/CoreLibraryDebug.java
deleted file mode 100644
index 0d6ee46..0000000
--- a/luni/src/main/java/libcore/util/CoreLibraryDebug.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-import com.android.icu.util.Icu4cMetadata;
-
-import libcore.timezone.TimeZoneDataFiles;
-import libcore.timezone.TzDataSetVersion;
-import libcore.timezone.TzDataSetVersion.TzDataSetException;
-import libcore.timezone.ZoneInfoDb;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Provides APIs for obtaining metadata for the managed core library and lower-level
- * components like bionic and the runtime.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public class CoreLibraryDebug {
-
-    private static final String CORE_LIBRARY_TIMEZONE_DEBUG_PREFIX = "core_library.timezone.";
-
-    private CoreLibraryDebug() {}
-
-    /**
-     * Returns information about the Core Library for debugging.
-     */
-    @libcore.api.CorePlatformApi
-    public static DebugInfo getDebugInfo() {
-        DebugInfo debugInfo = new DebugInfo();
-        populateTimeZoneFilesInfo(debugInfo);
-        populateTimeZoneLibraryReportedVersion(debugInfo);
-        return debugInfo;
-    }
-
-    /**
-     * Adds information about the available time zone file sets on the device to the supplied
-     * {@link DebugInfo}. See also {@link #populateTimeZoneLibraryReportedVersion(DebugInfo)} for a method
-     * that provides information about the time zone files actually in use by libraries.
-     */
-    private static void populateTimeZoneFilesInfo(DebugInfo debugInfo) {
-        String debugKeyPrefix = CORE_LIBRARY_TIMEZONE_DEBUG_PREFIX + "source.";
-
-        // Time zone module tz data set.
-        {
-            String tzDataModulePrefix = debugKeyPrefix + "tzdata_module_";
-            String versionFile =
-                    TimeZoneDataFiles.getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
-            addTzDataSetVersionDebugInfo(versionFile, tzDataModulePrefix, debugInfo);
-        }
-
-        // /system tz data set.
-        {
-            String systemDirPrefix = debugKeyPrefix + "system_";
-            String versionFile =
-                    TimeZoneDataFiles.getSystemTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
-            addTzDataSetVersionDebugInfo(versionFile, systemDirPrefix, debugInfo);
-        }
-    }
-
-    private static void addTzDataSetVersionDebugInfo(String tzDataSetVersionFile,
-            String debugKeyPrefix, DebugInfo debugInfo) {
-        File file = new File(tzDataSetVersionFile);
-        String statusKey = debugKeyPrefix + "status";
-        if (file.exists()) {
-            try {
-                TzDataSetVersion tzDataSetVersion =
-                        TzDataSetVersion.readFromFile(file);
-                String formatVersionString = tzDataSetVersion.getFormatMajorVersion() + "."
-                        + tzDataSetVersion.getFormatMinorVersion();
-                debugInfo.addStringEntry(statusKey, "OK")
-                        .addStringEntry(debugKeyPrefix + "formatVersion", formatVersionString)
-                        .addStringEntry(debugKeyPrefix + "rulesVersion",
-                                tzDataSetVersion.getRulesVersion())
-                        .addStringEntry(debugKeyPrefix + "revision",
-                                tzDataSetVersion.getRevision());
-            } catch (IOException | TzDataSetException e) {
-                debugInfo.addStringEntry(statusKey, "ERROR");
-                debugInfo.addStringEntry(debugKeyPrefix + "exception_class", e.getClass().getName());
-                debugInfo.addStringEntry(debugKeyPrefix + "exception_msg", e.getMessage());
-                System.logE("Error reading " + file, e);
-            }
-        } else {
-            debugInfo.addStringEntry(statusKey, "NOT_FOUND");
-        }
-    }
-
-    private static void populateTimeZoneLibraryReportedVersion(DebugInfo debugInfo) {
-        String debugKeyPrefix = CORE_LIBRARY_TIMEZONE_DEBUG_PREFIX + "lib.";
-        debugInfo.addStringEntry(
-                debugKeyPrefix + "icu4j.tzdb_version",
-                android.icu.util.TimeZone.getTZDataVersion());
-        debugInfo.addStringEntry(
-                debugKeyPrefix + "libcore.tzdb_version",
-                ZoneInfoDb.getInstance().getVersion());
-        debugInfo.addStringEntry(
-                debugKeyPrefix + "icu4c.tzdb_version",
-                Icu4cMetadata.getTzdbVersion());
-    }
-}
diff --git a/luni/src/main/java/libcore/util/DebugInfo.java b/luni/src/main/java/libcore/util/DebugInfo.java
deleted file mode 100644
index 78baa71..0000000
--- a/luni/src/main/java/libcore/util/DebugInfo.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A container class for debug information.
- *
- * @hide
- */
-@libcore.api.CorePlatformApi
-public class DebugInfo {
-    private final List<DebugEntry> entries;
-
-    @libcore.api.CorePlatformApi
-    public DebugInfo() {
-        this.entries = new ArrayList<>();
-    }
-
-    /**
-     * Adds a key / string value.
-     *
-     * @return {@code this} for chaining calls
-     */
-    @libcore.api.CorePlatformApi
-    public DebugInfo addStringEntry(String key, String value) {
-        entries.add(new DebugEntry(key, value));
-        return this;
-    }
-
-    /**
-     * Adds a key / string value. Converts the supplied int value to a String.
-     *
-     * @return {@code this} for chaining calls
-     */
-    @libcore.api.CorePlatformApi
-    public DebugInfo addStringEntry(String key, int value) {
-        addStringEntry(key, Integer.toString(value));
-        return this;
-    }
-
-    /** Returns all the debug entries. */
-    @libcore.api.CorePlatformApi
-    public List<DebugEntry> getDebugEntries() {
-        return entries;
-    }
-
-    /** Returns the first debug entry with the given key, or {@code null} if it does not exist. */
-    public DebugEntry getDebugEntry(String key) {
-        for (DebugEntry entry : getDebugEntries()) {
-            if (key.equals(entry.getKey())) {
-                return entry;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * A generic key/value for a single piece of debug information.
-     *
-     * @hide
-     */
-    @libcore.api.CorePlatformApi
-    public static class DebugEntry {
-        private final String key;
-        private final String stringValue;
-
-        @libcore.api.CorePlatformApi
-        public DebugEntry(String key, String stringValue) {
-            this.key = key;
-            this.stringValue = stringValue;
-        }
-
-        @libcore.api.CorePlatformApi
-        public String getKey() {
-            return key;
-        }
-
-        @libcore.api.CorePlatformApi
-        public String getStringValue() {
-            return stringValue;
-        }
-    }
-}
diff --git a/luni/src/main/java/libcore/util/EmptyArray.java b/luni/src/main/java/libcore/util/EmptyArray.java
index 0d1e321..f34fe02 100644
--- a/luni/src/main/java/libcore/util/EmptyArray.java
+++ b/luni/src/main/java/libcore/util/EmptyArray.java
@@ -16,43 +16,84 @@
 
 package libcore.util;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import dalvik.annotation.compat.VersionCodes;
 
-/** @hide */
-@libcore.api.CorePlatformApi
+/**
+ * Empty array is immutable. Use a shared empty array to avoid allocation.
+ *
+ * @hide
+ */
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = MODULE_LIBRARIES)
 public final class EmptyArray {
     private EmptyArray() {}
 
-    @libcore.api.CorePlatformApi
-    public static final boolean[] BOOLEAN = new boolean[0];
+    /** @hide */
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final @NonNull boolean[] BOOLEAN = new boolean[0];
+
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk=VersionCodes.Q,
             publicAlternatives="Use {@code new byte[0]} instead.")
-    @libcore.api.CorePlatformApi
-    public static final byte[] BYTE = new byte[0];
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final @NonNull byte[] BYTE = new byte[0];
+
+    /** @hide */
     public static final char[] CHAR = new char[0];
+
+    /** @hide */
     public static final double[] DOUBLE = new double[0];
-    @libcore.api.CorePlatformApi
-    public static final float[] FLOAT = new float[0];
+
+    /** @hide */
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final @NonNull float[] FLOAT = new float[0];
+
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk=VersionCodes.Q,
             publicAlternatives="Use {@code new int[0]} instead.")
-    @libcore.api.CorePlatformApi
-    public static final int[] INT = new int[0];
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final @NonNull int[] INT = new int[0];
+
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk=VersionCodes.Q,
             publicAlternatives="Use {@code new long[0]} instead.")
-    @libcore.api.CorePlatformApi
-    public static final long[] LONG = new long[0];
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final @NonNull long[] LONG = new long[0];
 
+    /** @hide */
     public static final Class<?>[] CLASS = new Class[0];
+
+    /** @hide */
     @UnsupportedAppUsage(maxTargetSdk=VersionCodes.Q,
             publicAlternatives="Use {@code new Object[0]} instead.")
-    @libcore.api.CorePlatformApi
-    public static final Object[] OBJECT = new Object[0];
-    @libcore.api.CorePlatformApi
-    public static final String[] STRING = new String[0];
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final @NonNull Object[] OBJECT = new Object[0];
+
+    /** @hide */
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final @NonNull String[] STRING = new String[0];
+
+    /** @hide */
     public static final Throwable[] THROWABLE = new Throwable[0];
+
+    /** @hide */
     public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
+
+    /** @hide */
     public static final java.lang.reflect.Type[] TYPE = new java.lang.reflect.Type[0];
+
+    /** @hide */
     public static final java.lang.reflect.TypeVariable[] TYPE_VARIABLE =
         new java.lang.reflect.TypeVariable[0];
 }
diff --git a/luni/src/main/java/libcore/util/FP16.java b/luni/src/main/java/libcore/util/FP16.java
index 602e1d4..d5a78ed 100644
--- a/luni/src/main/java/libcore/util/FP16.java
+++ b/luni/src/main/java/libcore/util/FP16.java
@@ -16,6 +16,10 @@
 
 package libcore.util;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 /**
  * <p>The {@code FP16} class is a wrapper and a utility class to manipulate half-precision 16-bit
  * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -86,91 +90,179 @@
  * @hide
  */
 
-@libcore.api.CorePlatformApi
-public class FP16 {
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+public final class FP16 {
     /**
      * The number of bits used to represent a half-precision float value.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int SIZE = 16;
 
     /**
      * Epsilon is the difference between 1.0 and the next value representable
      * by a half-precision floating-point.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short EPSILON = (short) 0x1400;
 
     /**
      * Maximum exponent a finite half-precision float may have.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int MAX_EXPONENT = 15;
     /**
      * Minimum exponent a normalized half-precision float may have.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int MIN_EXPONENT = -14;
 
     /**
      * Smallest negative value a half-precision float may have.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short LOWEST_VALUE = (short) 0xfbff;
     /**
      * Maximum positive finite value a half-precision float may have.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short MAX_VALUE = (short) 0x7bff;
     /**
      * Smallest positive normal value a half-precision float may have.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short MIN_NORMAL = (short) 0x0400;
     /**
      * Smallest positive non-zero value a half-precision float may have.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short MIN_VALUE = (short) 0x0001;
     /**
      * A Not-a-Number representation of a half-precision float.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short NaN = (short) 0x7e00;
     /**
      * Negative infinity of type half-precision float.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short NEGATIVE_INFINITY = (short) 0xfc00;
     /**
      * Negative 0 of type half-precision float.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short NEGATIVE_ZERO = (short) 0x8000;
     /**
      * Positive infinity of type half-precision float.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short POSITIVE_INFINITY = (short) 0x7c00;
     /**
      * Positive 0 of type half-precision float.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final short POSITIVE_ZERO = (short) 0x0000;
 
-    @libcore.api.CorePlatformApi
+    /**
+     * The offset to shift by to obtain the sign bit.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int SIGN_SHIFT                = 15;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * The offset to shift by to obtain the exponent bits.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int EXPONENT_SHIFT            = 10;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * The bitmask to AND a number with to obtain the sign bit.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int SIGN_MASK                 = 0x8000;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * The bitmask to AND a number shifted by {@link #EXPONENT_SHIFT} right, to obtain exponent bits.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int SHIFTED_EXPONENT_MASK     = 0x1f;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * The bitmask to AND a number with to obtain significand bits.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int SIGNIFICAND_MASK          = 0x3ff;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * The bitmask to AND with to obtain exponent and significand bits.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int EXPONENT_SIGNIFICAND_MASK = 0x7fff;
-    @libcore.api.CorePlatformApi
+
+    /**
+     * The offset of the exponent from the actual value.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static final int EXPONENT_BIAS             = 15;
 
     private static final int FP32_SIGN_SHIFT            = 31;
@@ -203,8 +295,11 @@
      *          value less than {@code 0} if {@code x} is numerically less than {@code y},
      *          and a value greater than {@code 0} if {@code x} is numerically greater
      *          than {@code y}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static int compare(short x, short y) {
         if (less(x, y)) return -1;
         if (greater(x, y)) return 1;
@@ -233,8 +328,11 @@
      * @param h A half-precision float value
      * @return The value of the specified half-precision float rounded to the nearest
      *         half-precision float value
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static short rint(short h) {
         int bits = h & 0xffff;
         int abs = bits & EXPONENT_SIGNIFICAND_MASK;
@@ -276,8 +374,11 @@
      * @param h A half-precision float value
      * @return The smallest half-precision float value toward negative infinity
      *         greater than or equal to the specified half-precision float value
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static short ceil(short h) {
         int bits = h & 0xffff;
         int abs = bits & EXPONENT_SIGNIFICAND_MASK;
@@ -317,8 +418,11 @@
      * @param h A half-precision float value
      * @return The largest half-precision float value toward positive infinity
      *         less than or equal to the specified half-precision float value
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static short floor(short h) {
         int bits = h & 0xffff;
         int abs = bits & EXPONENT_SIGNIFICAND_MASK;
@@ -356,8 +460,11 @@
      * @param h A half-precision float value
      * @return The truncated half-precision float value of the specified
      *         half-precision float value
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static short trunc(short h) {
         int bits = h & 0xffff;
         int abs = bits & EXPONENT_SIGNIFICAND_MASK;
@@ -385,8 +492,11 @@
      * @param x The first half-precision value
      * @param y The second half-precision value
      * @return The smaller of the two specified half-precision values
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static short min(short x, short y) {
         if (isNaN(x)) return NaN;
         if (isNaN(y)) return NaN;
@@ -411,8 +521,11 @@
      * @param y The second half-precision value
      *
      * @return The larger of the two specified half-precision values
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static short max(short x, short y) {
         if (isNaN(x)) return NaN;
         if (isNaN(y)) return NaN;
@@ -434,8 +547,11 @@
      * @param y The second half-precision value
      *
      * @return True if x is less than y, false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean less(short x, short y) {
         if (isNaN(x)) return false;
         if (isNaN(y)) return false;
@@ -453,8 +569,11 @@
      * @param y The second half-precision value
      *
      * @return True if x is less than or equal to y, false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean lessEquals(short x, short y) {
         if (isNaN(x)) return false;
         if (isNaN(y)) return false;
@@ -472,8 +591,11 @@
      * @param y The second half-precision value
      *
      * @return True if x is greater than y, false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean greater(short x, short y) {
         if (isNaN(x)) return false;
         if (isNaN(y)) return false;
@@ -491,8 +613,11 @@
      * @param y The second half-precision value
      *
      * @return True if x is greater than y, false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean greaterEquals(short x, short y) {
         if (isNaN(x)) return false;
         if (isNaN(y)) return false;
@@ -510,8 +635,11 @@
      * @param y The second half-precision value
      *
      * @return True if x is equal to y, false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean equals(short x, short y) {
         if (isNaN(x)) return false;
         if (isNaN(y)) return false;
@@ -526,8 +654,11 @@
      * @param h A half-precision float value
      * @return True if the value is positive infinity or negative infinity,
      *         false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean isInfinite(short h) {
         return (h & EXPONENT_SIGNIFICAND_MASK) == POSITIVE_INFINITY;
     }
@@ -538,8 +669,11 @@
      *
      * @param h A half-precision float value
      * @return True if the value is a NaN, false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean isNaN(short h) {
         return (h & EXPONENT_SIGNIFICAND_MASK) > POSITIVE_INFINITY;
     }
@@ -553,8 +687,11 @@
      *
      * @param h A half-precision float value
      * @return True if the value is normalized, false otherwise
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static boolean isNormalized(short h) {
         return (h & POSITIVE_INFINITY) != 0 && (h & POSITIVE_INFINITY) != POSITIVE_INFINITY;
     }
@@ -573,8 +710,11 @@
      *
      * @param h The half-precision float value to convert to single-precision
      * @return A normalized single-precision float value
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static float toFloat(short h) {
         int bits = h & 0xffff;
         int s = bits & SIGN_MASK;
@@ -628,8 +768,11 @@
      *
      * @param f The single-precision float value to convert to half-precision
      * @return A half-precision float value
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static short toHalf(float f) {
         int bits = Float.floatToRawIntBits(f);
         int s = (bits >>> FP32_SIGN_SHIFT    );
@@ -708,8 +851,11 @@
      *
      * @param h A half-precision float value
      * @return A hexadecimal string representation of the specified value
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static String toHexString(short h) {
         StringBuilder o = new StringBuilder();
 
diff --git a/luni/src/main/java/libcore/util/HexEncoding.java b/luni/src/main/java/libcore/util/HexEncoding.java
index 6d00074..5edcb20 100644
--- a/luni/src/main/java/libcore/util/HexEncoding.java
+++ b/luni/src/main/java/libcore/util/HexEncoding.java
@@ -16,11 +16,16 @@
 
 package libcore.util;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 /**
  * Hexadecimal encoding where each byte is represented by two hexadecimal digits.
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public class HexEncoding {
 
     private static final char[] LOWER_CASE_DIGITS = {
@@ -36,8 +41,16 @@
 
     /**
      * Encodes the provided byte as a two-digit hexadecimal String value.
+     *
+     * @param  b byte to encode
+     * @param  upperCase {@code true} to use uppercase letters, {@code false}
+     *         for lowercase
+     * @return the encoded string
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static String encodeToString(byte b, boolean upperCase) {
         char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS;
         char[] buf = new char[2]; // We always want two digits.
@@ -48,24 +61,46 @@
 
     /**
      * Encodes the provided data as a sequence of hexadecimal characters.
+     *
+     * @param  data byte array to encode
+     * @return the encoded data, using uppercase letters
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static char[] encode(byte[] data) {
         return encode(data, 0, data.length, true /* upperCase */);
     }
 
     /**
      * Encodes the provided data as a sequence of hexadecimal characters.
+     *
+     * @param  data byte array to encode
+     * @param  upperCase {@code true} to use uppercase letters, {@code false}
+     *         for lowercase
+     * @return the encoded data
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static char[] encode(byte[] data, boolean upperCase) {
         return encode(data, 0, data.length, upperCase);
     }
 
     /**
      * Encodes the provided data as a sequence of hexadecimal characters.
+     *
+     * @param  data byte array containing the data to encode
+     * @param  offset offset of the data to encode in the {@code data} array
+     * @param  len length of the data to encode in the {@code data} array
+     * @return the encoded data, using uppercase letters
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static char[] encode(byte[] data, int offset, int len) {
         return encode(data, offset, len, true /* upperCase */);
     }
@@ -88,63 +123,103 @@
 
     /**
      * Encodes the provided data as a sequence of hexadecimal characters.
+     *
+     * @param  data byte array to encode
+     * @return the encoded data, using uppercase letters
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static String encodeToString(byte[] data) {
         return encodeToString(data, true /* upperCase */);
     }
 
     /**
      * Encodes the provided data as a sequence of hexadecimal characters.
+     *
+     * @param  data byte array to encode.
+     * @param  upperCase {@code true} to use uppercase letters, {@code false}
+     *         for lowercase
+     * @return the encoded data
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static String encodeToString(byte[] data, boolean upperCase) {
         return new String(encode(data, upperCase));
     }
 
     /**
-     * Decodes the provided hexadecimal string into a byte array.  Odd-length inputs
-     * are not allowed.
+     * Decodes the provided hexadecimal sequence. Odd-length inputs are not
+     * allowed.
      *
-     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     * @param  encoded string of hexadecimal characters to decode. Letters
+     *         can be either uppercase or lowercase.
+     * @return the decoded data
+     * @throws IllegalArgumentException if the input is malformed
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static byte[] decode(String encoded) throws IllegalArgumentException {
         return decode(encoded.toCharArray());
     }
 
     /**
-     * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
-     * is {@code true} odd-length inputs are allowed and the first character is interpreted
-     * as the lower bits of the first result byte.
+     * Decodes the provided hexadecimal sequence.
      *
-     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     * @param  encoded string of hexadecimal characters to decode. Letters
+     *         can be either uppercase or lowercase.
+     * @param  allowSingleChar If {@code true} odd-length inputs are allowed and
+     *         the first character is interpreted as the lower bits of the first
+     *         result byte. If {@code false} odd-length inputs are not allowed.
+     * @return the decoded data
+     * @throws IllegalArgumentException if the input is malformed
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static byte[] decode(String encoded, boolean allowSingleChar)
             throws IllegalArgumentException {
         return decode(encoded.toCharArray(), allowSingleChar);
     }
 
     /**
-     * Decodes the provided hexadecimal string into a byte array.  Odd-length inputs
-     * are not allowed.
+     * Decodes the provided hexadecimal sequence. Odd-length inputs are not
+     * allowed.
      *
-     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     * @param  encoded char array of hexadecimal characters to decode. Letters
+     *         can be either uppercase or lowercase.
+     * @return the decoded data
+     * @throws IllegalArgumentException if the input is malformed
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static byte[] decode(char[] encoded) throws IllegalArgumentException {
         return decode(encoded, false);
     }
 
     /**
-     * Decodes the provided hexadecimal string into a byte array. If {@code allowSingleChar}
-     * is {@code true} odd-length inputs are allowed and the first character is interpreted
-     * as the lower bits of the first result byte.
+     * Decodes the provided hexadecimal sequence.
      *
-     * Throws an {@code IllegalArgumentException} if the input is malformed.
+     * @param  encoded char array of hexadecimal characters to decode. Letters
+     *         can be either uppercase or lowercase.
+     * @param  allowSingleChar If {@code true} odd-length inputs are allowed and
+     *         the first character is interpreted as the lower bits of the first
+     *         result byte. If {@code false} odd-length inputs are not allowed.
+     * @return the decoded data
+     * @throws IllegalArgumentException if the input is malformed
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static byte[] decode(char[] encoded, boolean allowSingleChar)
             throws IllegalArgumentException {
         int encodedLength = encoded.length;
diff --git a/luni/src/main/java/libcore/util/NativeAllocationRegistry.java b/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
index e114ebe..e336525 100644
--- a/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
+++ b/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
@@ -16,11 +16,17 @@
 
 package libcore.util;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import dalvik.system.VMRuntime;
 import sun.misc.Cleaner;
 
 import java.lang.ref.Reference;
 
+import libcore.util.NonNull;
+
 /**
  * A NativeAllocationRegistry is used to associate native allocations with
  * Java objects and register them with the runtime.
@@ -40,7 +46,8 @@
  * used to register any number of native allocations of that kind.
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.IntraCoreApi
 public class NativeAllocationRegistry {
 
@@ -60,29 +67,33 @@
     private static final long IS_MALLOCED = 0x1;
 
     /**
-     * Return a NativeAllocationRegistry for native memory that is mostly
+     * Return a {@link NativeAllocationRegistry} for native memory that is mostly
      * allocated by means other than the system memory allocator. For example,
      * the memory may be allocated directly with mmap.
      * @param classLoader  ClassLoader that was used to load the native
      *                     library defining freeFunction.
      *                     This ensures that the the native library isn't unloaded
-     *                     before freeFunction is called.
+     *                     before {@code freeFunction} is called.
      * @param freeFunction address of a native function of type
-     *                     <code>void f(void* nativePtr)</code> used to free this
+     *                     {@code void f(void* nativePtr)} used to free this
      *                     kind of native allocation
      * @param size         estimated size in bytes of the part of the described
      *                     native memory that is not allocated with system malloc.
      *                     Approximate values are acceptable.
-     * @throws IllegalArgumentException If <code>size</code> is negative
+     * @return allocated {@link NativeAllocationRegistry}
+     * @throws IllegalArgumentException If {@code size} is negative
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static NativeAllocationRegistry createNonmalloced(
-            ClassLoader classLoader, long freeFunction, long size) {
+            @NonNull ClassLoader classLoader, long freeFunction, long size) {
         return new NativeAllocationRegistry(classLoader, freeFunction, size, false);
     }
 
     /**
-     * Return a NativeAllocationRegistry for native memory that is mostly
+     * Return a {@link NativeAllocationRegistry} for native memory that is mostly
      * allocated by the system memory allocator.
      * For example, the memory may be allocated directly with new or malloc.
      * <p>
@@ -92,36 +103,44 @@
      * </pre>
      * <p>
      * @param classLoader  ClassLoader that was used to load the native
-     *                     library freeFunction belongs to.
+     *                     library {@code freeFunction} belongs to.
      * @param freeFunction address of a native function of type
-     *                     <code>void f(void* nativePtr)</code> used to free this
+     *                     {@code void f(void* nativePtr)} used to free this
      *                     kind of native allocation
      * @param size         estimated size in bytes of the part of the described
      *                     native memory allocated with system malloc.
      *                     Approximate values are acceptable. For sizes less than
      *                     a few hundered KB, use the simplified overload below.
-     * @throws IllegalArgumentException If <code>size</code> is negative
+     * @return allocated {@link NativeAllocationRegistry}
+     * @throws IllegalArgumentException If {@code size} is negative
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static NativeAllocationRegistry createMalloced(
-            ClassLoader classLoader, long freeFunction, long size) {
+            @NonNull ClassLoader classLoader, long freeFunction, long size) {
         return new NativeAllocationRegistry(classLoader, freeFunction, size, true);
     }
 
     /**
-     * Return a NativeAllocationRegistry for native memory that is mostly
+     * Return a {@link NativeAllocationRegistry} for native memory that is mostly
      * allocated by the system memory allocator. This version is preferred
      * for smaller objects (typically less than a few hundred KB).
      * @param classLoader  ClassLoader that was used to load the native
-     *                     library freeFunction belongs to.
+     *                     library {@code freeFunction} belongs to.
      * @param freeFunction address of a native function of type
-     *                     <code>void f(void* nativePtr)</code> used to free this
+     *                     {@code void f(void* nativePtr)} used to free this
      *                     kind of native allocation
+     * @return allocated {@link NativeAllocationRegistry}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
     public static NativeAllocationRegistry createMalloced(
-            ClassLoader classLoader, long freeFunction) {
+            @NonNull ClassLoader classLoader, long freeFunction) {
         return new NativeAllocationRegistry(classLoader, freeFunction, 0, true);
     }
 
@@ -164,24 +183,24 @@
     }
 
     /**
-     * Constructs a NativeAllocationRegistry for a particular kind of native
+     * Constructs a {@link NativeAllocationRegistry} for a particular kind of native
      * allocation.
      * <p>
      * New code should use the preceding factory methods rather than calling this
      * constructor directly.
      * <p>
-     * The <code>size</code> should be an estimate of the total number of
+     * The {@code size} should be an estimate of the total number of
      * native bytes this kind of native allocation takes up excluding bytes allocated
      * with system malloc. Different
-     * NativeAllocationRegistrys must be used to register native allocations
+     * {@link NativeAllocationRegistry}s must be used to register native allocations
      * with different estimated sizes, even if they use the same
-     * <code>freeFunction</code>. This is used to help inform the garbage
+     * {@code freeFunction}. This is used to help inform the garbage
      * collector about the possible need for collection. Memory allocated with
      * native malloc is implicitly included, and ideally should not be included in this
      * argument.
      * <p>
      * @param classLoader  ClassLoader that was used to load the native
-     *                     library freeFunction belongs to.
+     *                     library {@code freeFunction} belongs to.
      * @param freeFunction address of a native function used to free this
      *                     kind of native allocation
      * @param size         estimated size in bytes of this kind of native
@@ -189,26 +208,27 @@
      *                     A value of 0 indicates that the memory was allocated mainly
      *                     with malloc.
      *
-     * @param mallocAllocation the native object is primarily allocated via malloc.
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public NativeAllocationRegistry(@NonNull ClassLoader classLoader, long freeFunction, long size) {
         this(classLoader, freeFunction, size, size == 0);
     }
 
     /**
      * Registers a new native allocation and associated Java object with the
      * runtime.
-     * This NativeAllocationRegistry's <code>freeFunction</code> will
-     * automatically be called with <code>nativePtr</code> as its sole
-     * argument when <code>referent</code> becomes unreachable. If you
-     * maintain copies of <code>nativePtr</code> outside
-     * <code>referent</code>, you must not access these after
-     * <code>referent</code> becomes unreachable, because they may be dangling
+     * This {@link NativeAllocationRegistry}'s {@code freeFunction} will
+     * automatically be called with {@code nativePtr} as its sole
+     * argument when {@code referent} becomes unreachable. If you
+     * maintain copies of {@code nativePtr} outside
+     * {@code referent}, you must not access these after
+     * {@code referent} becomes unreachable, because they may be dangling
      * pointers.
      * <p>
      * The returned Runnable can be used to free the native allocation before
-     * <code>referent</code> becomes unreachable. The runnable will have no
+     * {@code referent} becomes unreachable. The runnable will have no
      * effect if the native allocation has already been freed by the runtime
      * or by using the runnable.
      * <p>
@@ -217,20 +237,23 @@
      * if the registration attempt throws an exception (other than one reporting
      * a programming error).
      *
-     * @param referent      Non-null java object to associate the native allocation with
+     * @param referent      Non-{@code null} java object to associate the native allocation with
      * @param nativePtr     Non-zero address of the native allocation
      * @return runnable to explicitly free native allocation
-     * @throws IllegalArgumentException if either referent or nativePtr is null.
+     * @throws IllegalArgumentException if either referent or nativePtr is {@code null}.
      * @throws OutOfMemoryError  if there is not enough space on the Java heap
      *                           in which to register the allocation. In this
-     *                           case, <code>freeFunction</code> will be
-     *                           called with <code>nativePtr</code> as its
-     *                           argument before the OutOfMemoryError is
+     *                           case, {@code freeFunction} will be
+     *                           called with {@code nativePtr} as its
+     *                           argument before the {@link OutOfMemoryError} is
      *                           thrown.
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     @libcore.api.IntraCoreApi
-    public Runnable registerNativeAllocation(Object referent, long nativePtr) {
+    public @NonNull Runnable registerNativeAllocation(@NonNull Object referent, long nativePtr) {
         if (referent == null) {
             throw new IllegalArgumentException("referent is null");
         }
@@ -311,12 +334,19 @@
     }
 
     /**
-     * Calls <code>freeFunction</code>(<code>nativePtr</code>).
+     * Calls {@code freeFunction}({@code nativePtr}).
      * Provided as a convenience in the case where you wish to manually free a
-     * native allocation using a <code>freeFunction</code> without using a
-     * NativeAllocationRegistry.
+     * native allocation using a {@code freeFunction} without using a
+     * {@link NativeAllocationRegistry}.
+     *
+     * @param freeFunction address of a native function used to free this
+     *                     kind of native allocation
+     * @param nativePtr    pointer to pass to freeing function
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public static native void applyFreeFunction(long freeFunction, long nativePtr);
 }
 
diff --git a/luni/src/main/java/libcore/util/NullFromTypeParam.java b/luni/src/main/java/libcore/util/NullFromTypeParam.java
index 3b6908e..e895f87 100644
--- a/luni/src/main/java/libcore/util/NullFromTypeParam.java
+++ b/luni/src/main/java/libcore/util/NullFromTypeParam.java
@@ -26,6 +26,8 @@
  * Annotation for generic types in methods, denotes that a type nullability is
  * deliberately left floating; the nullability is the same as the
  * actual type parameter of the class.
+ *
+ * @hide - prevent references to this from being output to the signature file.
  */
 @Documented
 @Retention(SOURCE)
diff --git a/luni/src/main/java/libcore/util/SneakyThrow.java b/luni/src/main/java/libcore/util/SneakyThrow.java
index 679996f..5a347d9 100644
--- a/luni/src/main/java/libcore/util/SneakyThrow.java
+++ b/luni/src/main/java/libcore/util/SneakyThrow.java
@@ -16,6 +16,10 @@
 
 package libcore.util;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 /**
  * Provides a hacky method that always throws {@code t} even if {@code t} is a checked exception.
  * and is not declared to be thrown.
@@ -25,7 +29,8 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public class SneakyThrow {
 
     private SneakyThrow() {
@@ -34,9 +39,14 @@
     /**
      * A hacky method that always throws {@code t} even if {@code t} is a checked exception,
      * and is not declared to be thrown.
+     *
+     * @param t throwable to throw
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static void sneakyThrow(Throwable t) {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static void sneakyThrow(@NonNull Throwable t) {
         SneakyThrow.<RuntimeException>sneakyThrow_(t);
     }
 
diff --git a/luni/src/main/java/libcore/util/XmlObjectFactory.java b/luni/src/main/java/libcore/util/XmlObjectFactory.java
index 3fafb39..7a1d7ec 100644
--- a/luni/src/main/java/libcore/util/XmlObjectFactory.java
+++ b/luni/src/main/java/libcore/util/XmlObjectFactory.java
@@ -16,6 +16,10 @@
 
 package libcore.util;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import com.android.org.kxml2.io.KXmlParser;
 import com.android.org.kxml2.io.KXmlSerializer;
 import org.apache.harmony.xml.ExpatReader;
@@ -28,7 +32,8 @@
  *
  * @hide
  */
-@libcore.api.CorePlatformApi
+@SystemApi(client = MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public class XmlObjectFactory {
 
     private XmlObjectFactory() {}
@@ -36,26 +41,41 @@
     /**
      * Returns a new instance of the platform default {@link XmlSerializer} more efficiently than
      * using {@code XmlPullParserFactory.newInstance().newSerializer()}.
+     *
+     * @return platform default {@link XmlSerializer}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static XmlSerializer newXmlSerializer() {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull XmlSerializer newXmlSerializer() {
         return new KXmlSerializer();
     }
 
     /**
      * Returns a new instance of the platform default {@link XmlPullParser} more efficiently than
      * using {@code XmlPullParserFactory.newInstance().newPullParser()}.
+     *
+     * @return platform default {@link XmlPullParser}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static XmlPullParser newXmlPullParser() {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull XmlPullParser newXmlPullParser() {
         return new KXmlParser();
     }
 
     /**
      * Returns the plaform default {@link XMLReader}.
+     *
+     * @return plaform default {@link XMLReader}
+     *
+     * @hide
      */
-    @libcore.api.CorePlatformApi
-    public static XMLReader newXMLReader() {
+    @SystemApi(client = MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static @NonNull XMLReader newXMLReader() {
         return new ExpatReader();
     }
 }
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 85bf1bc..32ec445 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -24,41 +24,30 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 
+import com.android.i18n.timezone.ZoneInfoData;
 import java.io.IOException;
 import java.io.ObjectInputStream;
+import java.io.ObjectInputStream.GetField;
+import java.io.ObjectOutputStream;
+import java.io.ObjectOutputStream.PutField;
+import java.io.ObjectStreamField;
+import java.lang.reflect.Field;
 import java.util.Arrays;
-import java.util.Calendar;
 import java.util.Date;
-import java.util.GregorianCalendar;
+import java.util.Objects;
 import java.util.TimeZone;
-import libcore.io.BufferIterator;
-import libcore.timezone.ZoneInfoDb;
 
 /**
- * Our concrete TimeZone implementation, backed by zoneinfo data.
+ *  Our concrete TimeZone implementation, backed by a {@link ZoneInfoData}. This class is not
+ *  thread-safe.
  *
- * <p>This reads time zone information from a binary file stored on the platform. The binary file
- * is essentially a single file containing compacted versions of all the tzfiles produced by the
- * zone info compiler (zic) tool (see {@code man 5 tzfile} for details of the format and
- * {@code man 8 zic}) and an index by long name, e.g. Europe/London.
- *
- * <p>The compacted form is created by {@code external/icu/tools/ZoneCompactor.java} and is used
- * by both this and Bionic. {@link ZoneInfoDb} is responsible for mapping the binary file, and
- * reading the index and creating a {@link BufferIterator} that provides access to an entry for a
- * specific file. This class is responsible for reading the data from that {@link BufferIterator}
- * and storing it a representation to support the {@link TimeZone} and {@link GregorianCalendar}
- * implementations. See {@link ZoneInfo#readTimeZone(String, BufferIterator, long)}.
- *
- * <p>This class does not use all the information from the {@code tzfile}; it uses:
- * {@code tzh_timecnt} and the associated transition times and type information. For each type
- * (described by {@code struct ttinfo}) it uses {@code tt_gmtoff} and {@code tt_isdst}.
- *
- * <p>This class should be in libcore.timezone but this class is Serializable so cannot
- * be moved there without breaking apps that have (for some reason) serialized TimeZone objects.
+ * This class exists in this package and has certain fields / a defined serialization footprint for
+ * app compatibility reasons. The knowledge of the underlying file format has been split out into
+ * {@link ZoneInfoData} which is intended to be updated independently of the classes in
+ * libcore.util.
  *
  * @hide - used to implement TimeZone
  */
-@libcore.api.CorePlatformApi
 public final class ZoneInfo extends TimeZone {
     private static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
     private static final long MILLISECONDS_PER_400_YEARS =
@@ -67,27 +56,34 @@
     private static final long UNIX_OFFSET = 62167219200000L;
 
     private static final int[] NORMAL = new int[] {
-        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+            0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
     };
 
     private static final int[] LEAP = new int[] {
-        0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
+            0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
     };
 
     // Proclaim serialization compatibility with pre-OpenJDK AOSP
     static final long serialVersionUID = -4598738130123921552L;
 
     /**
-     * The (best guess) non-DST offset used "today". It is stored in milliseconds.
-     * See also {@link #mOffsets} which holds values relative to this value, albeit in seconds.
+     * Keep the serialization compatibility even though most of the fields have been moved to
+     * {@link ZoneInfoData}. Originally, Android just had the Serializable subclass of TimeZone,
+     * ZoneInfo. This has been split into two: ZoneInfoData is now responsible for the immutable
+     * data fields, and this class holds the rest.
      */
-    private int mRawOffset;
+    private static final ObjectStreamField[] serialPersistentFields;
 
-    /**
-     * The earliest non-DST offset for the zone. It is stored in milliseconds and is absolute, i.e.
-     * it is not relative to mRawOffset.
-     */
-    private final int mEarliestRawOffset;
+    static {
+        int srcLen = ZoneInfoData.ZONEINFO_SERIALIZED_FIELDS.length;
+        serialPersistentFields = new ObjectStreamField[2 + srcLen];
+        // Serialize mDstSavings and mUseDst fields, but not mTransitions because the
+        // ZoneInfoData delegate should handle it.
+        serialPersistentFields[0] = new ObjectStreamField("mDstSavings", int.class);
+        serialPersistentFields[1] = new ObjectStreamField("mUseDst", boolean.class);
+        System.arraycopy(ZoneInfoData.ZONEINFO_SERIALIZED_FIELDS, 0 /* srcPos */,
+                serialPersistentFields, 2 /* destPos */, srcLen /* length */);
+    }
 
     /**
      * Implements {@link #useDaylightTime()}
@@ -95,9 +91,9 @@
      * <p>True if the transition active at the time this instance was created, or future
      * transitions support DST. It is possible that caching this value at construction time and
      * using it for the lifetime of the instance does not match the contract of the
-     * {@link TimeZone#useDaylightTime()} method but it appears to be what the RI does and that
-     * method is not particularly useful when it comes to historical or future times as it does not
-     * allow the time to be specified.
+     * {@link java.util.TimeZone#useDaylightTime()} method but it appears to be what the RI does
+     * and that method is not particularly useful when it comes to historical or future times as it
+     * does not allow the time to be specified.
      *
      * <p>When this is false then {@link #mDstSavings} will be 0.
      *
@@ -108,362 +104,95 @@
     /**
      * Implements {@link #getDSTSavings()}
      *
-     * <p>This should be final but is not because it may need to be fixed up by
-     * {@link #readObject(ObjectInputStream)} to correct an inconsistency in the previous version
-     * of the code whereby this was set to a non-zero value even though DST was not actually used.
-     *
      * @see #mUseDst
      */
-    private int mDstSavings;
+    private final int mDstSavings;
 
     /**
-     * The times (in seconds) at which the offsets changes for any reason, whether that is a change
-     * in the offset from UTC or a change in the DST.
-     *
-     * <p>These times are pre-calculated externally from a set of rules (both historical and
-     * future) and stored in a file from which {@link ZoneInfo#readTimeZone(String, BufferIterator,
-     * long)} reads the data. That is quite different to {@link java.util.SimpleTimeZone}, which has
-     * essentially human readable rules (e.g. DST starts at 01:00 on the first Sunday in March and
-     * ends at 01:00 on the last Sunday in October) that can be used to determine the DST transition
-     * times across a number of years
-     *
-     * <p>In terms of {@link ZoneInfo tzfile} structure this array is of length {@code tzh_timecnt}
-     * and contains the times in seconds converted to long to make them safer to use.
-     *
-     * <p>They are stored in order from earliest (lowest) time to latest (highest). A transition is
-     * identified by its index within this array. A transition {@code T} is active at a specific
-     * time {@code X} if {@code T} is the highest transition whose time is less than or equal to
-     * {@code X}.
-     *
-     * @see #mTypes
+     * This field is kept only for app compatibility indicated by @UnsupportedAppUsage. Do not
+     * modify the content of this array as it is a reference to an internal data structure used by
+     * mDelegate.
      */
     @UnsupportedAppUsage
     private final long[] mTransitions;
 
     /**
-     * The type of the transition, where type is a pair consisting of the offset and whether the
-     * offset includes DST or not.
-     *
-     * <p>Each transition in {@link #mTransitions} has an associated type in this array at the same
-     * index. The type is an index into the arrays {@link #mOffsets} and {@link #mIsDsts} that each
-     * contain one part of the pair.
-     *
-     * <p>In the {@link ZoneInfo tzfile} structure the type array only contains unique instances of
-     * the {@code struct ttinfo} to save space and each type may be referenced by multiple
-     * transitions. However, the type pairs stored in this class are not guaranteed unique because
-     * they do not include the {@code tt_abbrind}, which is the abbreviated identifier to use for
-     * the time zone after the transition.
-     *
-     * @see #mTransitions
-     * @see #mOffsets
-     * @see #mIsDsts
+     * Despite being transient, mDelegate is still serialized as part of this object. Please
+     * see {@link #readObject(ObjectInputStream)} and {@link #writeObject(ObjectOutputStream)}
      */
-    private final byte[] mTypes;
+    private transient ZoneInfoData mDelegate;
 
     /**
-     * The offset parts of the transition types, in seconds.
-     *
-     * <p>These are actually a delta to the {@link #mRawOffset}. So, if the offset is say +7200
-     * seconds and {@link #mRawOffset} is say +3600 then this will have a value of +3600.
-     *
-     * <p>The offset in milliseconds can be computed using:
-     * {@code mRawOffset + mOffsets[type] * 1000}
-     *
-     * @see #mTypes
-     * @see #mIsDsts
+     * Creates an instance using the current system clock time to calculate the {@link #mDstSavings}
+     * and {@link #mUseDst} fields. See also {@link #createZoneInfo(ZoneInfoData, long)}.
      */
-    private final int[] mOffsets;
-
-    /**
-     * Specifies whether an associated offset includes DST or not.
-     *
-     * <p>Each entry in here is 1 if the offset at the same index in {@link #mOffsets} includes DST
-     * and 0 otherwise.
-     *
-     * @see #mTypes
-     * @see #mOffsets
-     */
-    private final byte[] mIsDsts;
-
-    public static ZoneInfo readTimeZone(String id, BufferIterator it, long currentTimeMillis)
-            throws IOException {
-
-        // Skip over the superseded 32-bit header and data.
-        skipOver32BitData(id, it);
-
-        // Read the v2+ 64-bit header and data.
-        return read64BitData(id, it, currentTimeMillis);
+    public static ZoneInfo createZoneInfo(ZoneInfoData delegate) {
+        return createZoneInfo(delegate, System.currentTimeMillis());
     }
 
     /**
-     * Skip over the 32-bit data with some minimal validation to make sure sure we reading a valid
-     * and supported file.
+     * Creates an instance and recalculate the fields {@link #mDstSavings} and {@link #mUseDst} from
+     * the {@code timeInMillis}.
      */
-    private static void skipOver32BitData(String id, BufferIterator it) throws IOException {
-        // Variable names beginning tzh_ correspond to those in "tzfile.h".
-
-        // Check tzh_magic.
-        int tzh_magic = it.readInt();
-        if (tzh_magic != 0x545a6966) { // "TZif"
-            throw new IOException("Timezone id=" + id + " has an invalid header=" + tzh_magic);
-        }
-
-        byte tzh_version = it.readByte();
-        checkTzifVersionAcceptable(id, tzh_version);
-
-        // Skip the unused bytes.
-        it.skip(15);
-
-        // Read the header values necessary to read through all the 32-bit data.
-        int tzh_ttisgmtcnt = it.readInt();
-        int tzh_ttisstdcnt = it.readInt();
-        int tzh_leapcnt = it.readInt();
-        int tzh_timecnt = it.readInt();
-        int tzh_typecnt = it.readInt();
-        int tzh_charcnt = it.readInt();
-
-        // Skip transitions data, 4 bytes for each 32-bit time + 1 byte for isDst.
-        final int transitionInfoSize = 4 + 1;
-        it.skip(tzh_timecnt * transitionInfoSize);
-
-        // Skip ttinfos.
-        // struct ttinfo {
-        //     int32_t       tt_gmtoff;
-        //     unsigned char tt_isdst;
-        //     unsigned char tt_abbrind;
-        // };
-        final int ttinfoSize = 4 + 1 + 1;
-        it.skip(tzh_typecnt * ttinfoSize);
-
-        // Skip tzh_charcnt time zone abbreviations.
-        it.skip(tzh_charcnt);
-
-        // Skip tzh_leapcnt repetitions of a 32-bit time + a 32-bit correction.
-        int leapInfoSize = 4 + 4;
-        it.skip(tzh_leapcnt * leapInfoSize);
-
-        // Skip ttisstds and ttisgmts information. These can be ignored for our usecases as per
-        // https://mm.icann.org/pipermail/tz/2006-February/013359.html
-        it.skip(tzh_ttisstdcnt + tzh_ttisgmtcnt);
+    // VisibleForTesting
+    public static ZoneInfo createZoneInfo(ZoneInfoData delegate, long timeInMillis) {
+        Integer latestDstSavings = delegate.getLatestDstSavingsMillis(timeInMillis);
+        boolean useDst = latestDstSavings != null;
+        int dstSavings = latestDstSavings == null ? 0 : latestDstSavings;
+        return new ZoneInfo(delegate, dstSavings, useDst);
     }
 
-    /**
-     * Read the 64-bit header and data for {@code id} from the current position of {@code it} and
-     * return a ZoneInfo.
-     */
-    private static ZoneInfo read64BitData(String id, BufferIterator it, long currentTimeMillis)
-            throws IOException {
-        // Variable names beginning tzh_ correspond to those in "tzfile.h".
-
-        // Check tzh_magic.
-        int tzh_magic = it.readInt();
-        if (tzh_magic != 0x545a6966) { // "TZif"
-            throw new IOException("Timezone id=" + id + " has an invalid header=" + tzh_magic);
-        }
-
-        byte tzh_version = it.readByte();
-        checkTzifVersionAcceptable(id, tzh_version);
-
-        // Skip the uninteresting parts of the header.
-        it.skip(27);
-
-        // Read the sizes of the arrays we're about to read.
-        int tzh_timecnt = it.readInt();
-
-        // Arbitrary ceiling to prevent allocating memory for corrupt data.
-        final int MAX_TRANSITIONS = 2000;
-        if (tzh_timecnt < 0 || tzh_timecnt > MAX_TRANSITIONS) {
-            throw new IOException(
-                    "Timezone id=" + id + " has an invalid number of transitions="
-                            + tzh_timecnt);
-        }
-
-        int tzh_typecnt = it.readInt();
-        final int MAX_TYPES = 256;
-        if (tzh_typecnt < 1) {
-            throw new IOException("ZoneInfo requires at least one type "
-                    + "to be provided for each timezone but could not find one for '" + id
-                    + "'");
-        } else if (tzh_typecnt > MAX_TYPES) {
-            throw new IOException(
-                    "Timezone with id " + id + " has too many types=" + tzh_typecnt);
-        }
-
-        it.skip(4); // Skip tzh_charcnt.
-
-        long[] transitions64 = new long[tzh_timecnt];
-        it.readLongArray(transitions64, 0, transitions64.length);
-        for (int i = 0; i < tzh_timecnt; ++i) {
-            if (i > 0 && transitions64[i] <= transitions64[i - 1]) {
-                throw new IOException(
-                        id + " transition at " + i + " is not sorted correctly, is "
-                                + transitions64[i] + ", previous is " + transitions64[i - 1]);
-            }
-        }
-
-        byte[] types = new byte[tzh_timecnt];
-        it.readByteArray(types, 0, types.length);
-        for (int i = 0; i < types.length; i++) {
-            int typeIndex = types[i] & 0xff;
-            if (typeIndex >= tzh_typecnt) {
-                throw new IOException(
-                        id + " type at " + i + " is not < " + tzh_typecnt + ", is "
-                                + typeIndex);
-            }
-        }
-
-        int[] gmtOffsets = new int[tzh_typecnt];
-        byte[] isDsts = new byte[tzh_typecnt];
-        for (int i = 0; i < tzh_typecnt; ++i) {
-            gmtOffsets[i] = it.readInt();
-            byte isDst = it.readByte();
-            if (isDst != 0 && isDst != 1) {
-                throw new IOException(id + " dst at " + i + " is not 0 or 1, is " + isDst);
-            }
-            isDsts[i] = isDst;
-            // We skip the abbreviation index. This would let us provide historically-accurate
-            // time zone abbreviations (such as "AHST", "YST", and "AKST" for standard time in
-            // America/Anchorage in 1982, 1983, and 1984 respectively). ICU only knows the current
-            // names, though, so even if we did use this data to provide the correct abbreviations
-            // for en_US, we wouldn't be able to provide correct abbreviations for other locales,
-            // nor would we be able to provide correct long forms (such as "Yukon Standard Time")
-            // for any locale. (The RI doesn't do any better than us here either.)
-            it.skip(1);
-        }
-        return new ZoneInfo(id, transitions64, types, gmtOffsets, isDsts, currentTimeMillis);
+    private ZoneInfo(ZoneInfoData delegate, int dstSavings, boolean useDst) {
+        mDelegate = delegate;
+        mDstSavings = dstSavings;
+        mUseDst = useDst;
+        mTransitions = delegate.getTransitions();
+        setID(delegate.getID());
     }
 
-    private static void checkTzifVersionAcceptable(String id, byte tzh_version) throws IOException {
-        char tzh_version_char = (char) tzh_version;
-        // Version >= 2 is required because the 64-bit time section is required. v3 is the latest
-        // version known at the time of writing and is identical to v2 in the parts used by this
-        // class.
-        if (tzh_version_char != '2' && tzh_version_char != '3') {
-            throw new IOException(
-                    "Timezone id=" + id + " has an invalid format version=\'" + tzh_version_char
-                            + "\' (" + tzh_version + ")");
-        }
-    }
-
-    private ZoneInfo(String name, long[] transitions, byte[] types, int[] gmtOffsets, byte[] isDsts,
-            long currentTimeMillis) {
-        if (gmtOffsets.length == 0) {
-            throw new IllegalArgumentException("ZoneInfo requires at least one offset "
-                    + "to be provided for each timezone but could not find one for '" + name + "'");
-        }
-        mTransitions = transitions;
-        mTypes = types;
-        mIsDsts = isDsts;
-        setID(name);
-
-        // Find the latest daylight and standard offsets (if any).
-        int lastStdTransitionIndex = -1;
-        int lastDstTransitionIndex = -1;
-        for (int i = mTransitions.length - 1;
-                (lastStdTransitionIndex == -1 || lastDstTransitionIndex == -1) && i >= 0; --i) {
-            int typeIndex = mTypes[i] & 0xff;
-            if (lastStdTransitionIndex == -1 && mIsDsts[typeIndex] == 0) {
-                lastStdTransitionIndex = i;
-            }
-            if (lastDstTransitionIndex == -1 && mIsDsts[typeIndex] != 0) {
-                lastDstTransitionIndex = i;
-            }
-        }
-
-        // Use the latest non-daylight offset (if any) as the raw offset.
-        if (mTransitions.length == 0) {
-            // This case is no longer expected to occur in the data used on Android after changes
-            // made in zic version 2014c. It is kept as a fallback.
-            // If there are no transitions then use the first GMT offset.
-            mRawOffset = gmtOffsets[0];
-        } else {
-            if (lastStdTransitionIndex == -1) {
-                throw new IllegalStateException( "ZoneInfo requires at least one non-DST "
-                        + "transition to be provided for each timezone that has at least one "
-                        + "transition but could not find one for '" + name + "'");
-            }
-            mRawOffset = gmtOffsets[mTypes[lastStdTransitionIndex] & 0xff];
-        }
-
-        if (lastDstTransitionIndex != -1) {
-            // Check to see if the last DST transition is in the future or the past. If it is in
-            // the past then we treat it as if it doesn't exist, at least for the purposes of
-            // setting mDstSavings and mUseDst.
-            long lastDSTTransitionTime = mTransitions[lastDstTransitionIndex];
-
-            // Convert the current time in millis into seconds. Unlike other places that convert
-            // time in milliseconds into seconds in order to compare with transition time this
-            // rounds up rather than down. It does that because this is interested in what
-            // transitions apply in future
-            long currentUnixTimeSeconds = roundUpMillisToSeconds(currentTimeMillis);
-
-            // Is this zone observing DST currently or in the future?
-            // We don't care if they've historically used it: most places have at least once.
-            // See http://code.google.com/p/android/issues/detail?id=877.
-            // This test means that for somewhere like Morocco, which tried DST in 2009 but has
-            // no future plans (and thus no future schedule info) will report "true" from
-            // useDaylightTime at the start of 2009 but "false" at the end. This seems appropriate.
-            if (lastDSTTransitionTime < currentUnixTimeSeconds) {
-                // The last DST transition is before now so treat it as if it doesn't exist.
-                lastDstTransitionIndex = -1;
-            }
-        }
-
-        if (lastDstTransitionIndex == -1) {
-            // There were no DST transitions or at least no future DST transitions so DST is not
-            // used.
-            mDstSavings = 0;
-            mUseDst = false;
-        } else {
-            // 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.
-            int lastGmtOffset = gmtOffsets[mTypes[lastStdTransitionIndex] & 0xff];
-            int lastDstOffset = gmtOffsets[mTypes[lastDstTransitionIndex] & 0xff];
-            mDstSavings = (lastDstOffset - lastGmtOffset) * 1000;
-            mUseDst = true;
-        }
-
-        // From the tzfile docs (Jan 2019):
-        // The localtime(3) function uses the first standard-time ttinfo structure
-        // in the file (or simply the first ttinfo structure in the absence of a
-        // standard-time structure) if either tzh_timecnt is zero or the time
-        // argument is less than the first transition time recorded in the file.
-        //
-        // Cache the raw offset associated with the first nonDst type, in case we're asked about
-        // times that predate our transition data. Android falls back to mRawOffset if there are
-        // only DST ttinfo structures (assumed rare).
-        int firstStdTypeIndex = -1;
-        for (int i = 0; i < mIsDsts.length; ++i) {
-            if (mIsDsts[i] == 0) {
-                firstStdTypeIndex = i;
-                break;
-            }
-        }
-
-        int earliestRawOffset = (firstStdTypeIndex != -1)
-                ? gmtOffsets[firstStdTypeIndex] : mRawOffset;
-
-        // Rather than keep offsets from UTC, we use offsets from local time, so the raw offset
-        // can be changed and automatically affect all the offsets.
-        mOffsets = gmtOffsets;
-        for (int i = 0; i < mOffsets.length; i++) {
-            mOffsets[i] -= mRawOffset;
-        }
-
-        // tzdata uses seconds, but Java uses milliseconds.
-        mRawOffset *= 1000;
-        mEarliestRawOffset = earliestRawOffset * 1000;
-    }
-
-    /**
-     * Ensure that when deserializing an instance that {@link #mDstSavings} is always 0 when
-     * {@link #mUseDst} is false.
-     */
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
-        in.defaultReadObject();
-        if (!mUseDst && mDstSavings != 0) {
-            mDstSavings = 0;
+        GetField getField = in.readFields();
+        // TimeZone#getID() should return the proper ID because the fields in the superclass should
+        // have been deserialized.
+        mDelegate = ZoneInfoData.createFromSerializationFields(getID(), getField);
+
+        // Set the final fields by reflection.
+        boolean useDst = getField.get("mUseDst", false);
+        int dstSavings = getField.get("mDstSavings", 0);
+        long[] transitions = mDelegate.getTransitions();
+        /** For pre-OpenJDK compatibility, ensure that when deserializing an instance that
+         * {@link #mDstSavings} is always 0 when {@link #mUseDst} is false
+         */
+        if (!useDst && dstSavings != 0) {
+            dstSavings = 0;
         }
+        int finalDstSavings = dstSavings;
+        setFinalField("mDstSavings", (f -> f.setInt(this, finalDstSavings)));
+        setFinalField("mUseDst", (f -> f.setBoolean(this, useDst)));
+        setFinalField("mTransitions", (f -> f.set(this, transitions)));
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        PutField putField = out.putFields();
+        putField.put("mUseDst", mUseDst);
+        putField.put("mDstSavings", mDstSavings);
+        // mDelegate writes the field mTransitions.
+        mDelegate.writeToSerializationFields(putField);
+        out.writeFields();
+    }
+
+    private static void setFinalField(String field, FieldSetter setter) {
+        try {
+            Field mTransitionsField = ZoneInfo.class.getDeclaredField(field);
+            mTransitionsField.setAccessible(true);
+            setter.set(mTransitionsField);
+        } catch (ReflectiveOperationException e) {
+            // The field should always exist because it's a member field in this class.
+        }
+    }
+
+    private interface FieldSetter {
+        void set(Field field) throws IllegalArgumentException, IllegalAccessException;
     }
 
     @Override
@@ -489,244 +218,60 @@
         calc += (day - 1) * MILLISECONDS_PER_DAY;
         calc += millis;
 
-        calc -= mRawOffset;
+        calc -= mDelegate.getRawOffset();
         calc -= UNIX_OFFSET;
 
-        return getOffset(calc);
-    }
-
-    /**
-     * Find the transition in the {@code timezone} in effect at {@code seconds}.
-     *
-     * <p>Returns an index in the range -1..timeZone.mTransitions.length - 1. -1 is used to
-     * indicate the time is before the first transition. Other values are an index into
-     * timeZone.mTransitions.
-     */
-    public int findTransitionIndex(long seconds) {
-        int transition = Arrays.binarySearch(mTransitions, seconds);
-        if (transition < 0) {
-            transition = ~transition - 1;
-            if (transition < 0) {
-                return -1;
-            }
-        }
-
-        return transition;
-    }
-
-    /**
-     * Finds the index within the {@link #mOffsets}/{@link #mIsDsts} arrays for the specified time
-     * in seconds, since 1st Jan 1970 00:00:00.
-     * @param seconds the time in seconds.
-     * @return -1 if the time is before the first transition, or [0..{@code mOffsets}-1] for the
-     * active offset.
-     */
-    int findOffsetIndexForTimeInSeconds(long seconds) {
-        int transition = findTransitionIndex(seconds);
-        if (transition < 0) {
-            return -1;
-        }
-
-        return mTypes[transition] & 0xff;
-    }
-
-    /**
-     * Finds the index within the {@link #mOffsets}/{@link #mIsDsts} arrays for the specified time
-     * in milliseconds, since 1st Jan 1970 00:00:00.000.
-     * @param millis the time in milliseconds.
-     * @return -1 if the time is before the first transition, or [0..{@code mOffsets}-1] for the
-     * active offset.
-     */
-    int findOffsetIndexForTimeInMilliseconds(long millis) {
-        // This rounds the time in milliseconds down to the time in seconds.
-        //
-        // It can't just divide a timestamp in millis by 1000 to obtain a transition time in
-        // seconds because / (div) in Java rounds towards zero. Times before 1970 are negative and
-        // if they have a millisecond component then div would result in obtaining a time that is
-        // one second after what we need.
-        //
-        // e.g. dividing -12,001 milliseconds by 1000 would result in -12 seconds. If there was a
-        //      transition at -12 seconds then that would be incorrectly treated as being active
-        //      for a time of -12,001 milliseconds even though that time is before the transition
-        //      should occur.
-
-        return findOffsetIndexForTimeInSeconds(roundDownMillisToSeconds(millis));
-    }
-
-    /**
-     * Converts time in milliseconds into a time in seconds, rounding down to the closest time
-     * in seconds before the time in milliseconds.
-     *
-     * <p>It's not sufficient to simply divide by 1000 because that rounds towards 0 and so while
-     * for positive numbers it produces a time in seconds that precedes the time in milliseconds
-     * for negative numbers it can produce a time in seconds that follows the time in milliseconds.
-     *
-     * <p>This basically does the same as {@code (long) Math.floor(millis / 1000.0)} but should be
-     * faster.
-     *
-     * @param millis the time in milliseconds, may be negative.
-     * @return the time in seconds.
-     */
-    static long roundDownMillisToSeconds(long millis) {
-        if (millis < 0) {
-            // If the time is less than zero then subtract 999 and then divide by 1000 rounding
-            // towards 0 as usual, e.g.
-            // -12345 -> -13344 / 1000 = -13
-            // -12000 -> -12999 / 1000 = -12
-            // -12001 -> -13000 / 1000 = -13
-            return (millis - 999) / 1000;
-        } else {
-            return millis / 1000;
-        }
-    }
-
-    /**
-     * Converts time in milliseconds into a time in seconds, rounding up to the closest time
-     * in seconds before the time in milliseconds.
-     *
-     * <p>It's not sufficient to simply divide by 1000 because that rounds towards 0 and so while
-     * for negative numbers it produces a time in seconds that follows the time in milliseconds
-     * for positive numbers it can produce a time in seconds that precedes the time in milliseconds.
-     *
-     * <p>This basically does the same as {@code (long) Math.ceil(millis / 1000.0)} but should be
-     * faster.
-     *
-     * @param millis the time in milliseconds, may be negative.
-     * @return the time in seconds.
-     */
-    static long roundUpMillisToSeconds(long millis) {
-        if (millis > 0) {
-            // If the time is greater than zero then add 999 and then divide by 1000 rounding
-            // towards 0 as usual, e.g.
-            // 12345 -> 13344 / 1000 = 13
-            // 12000 -> 12999 / 1000 = 12
-            // 12001 -> 13000 / 1000 = 13
-            return (millis + 999) / 1000;
-        } else {
-            return millis / 1000;
-        }
-    }
-
-    /**
-     * Get the raw and DST offsets for the specified time in milliseconds since
-     * 1st Jan 1970 00:00:00.000 UTC.
-     *
-     * <p>The raw offset, i.e. that part of the total offset which is not due to DST, is stored at
-     * index 0 of the {@code offsets} array and the DST offset, i.e. that part of the offset which
-     * is due to DST is stored at index 1.
-     *
-     * @param utcTimeInMillis the UTC time in milliseconds.
-     * @param offsets the array whose length must be greater than or equal to 2.
-     * @return the total offset which is the sum of the raw and DST offsets.
-     */
-    public int getOffsetsByUtcTime(long utcTimeInMillis, int[] offsets) {
-        int transitionIndex = findTransitionIndex(roundDownMillisToSeconds(utcTimeInMillis));
-        int totalOffset;
-        int rawOffset;
-        int dstOffset;
-        if (transitionIndex == -1) {
-            // See getOffset(long) and inDaylightTime(Date) for an explanation as to why these
-            // values are used for times before the first transition.
-            rawOffset = mEarliestRawOffset;
-            dstOffset = 0;
-            totalOffset = rawOffset;
-        } else {
-            int type = mTypes[transitionIndex] & 0xff;
-
-            // Get the total offset used for the transition.
-            totalOffset = mRawOffset + mOffsets[type] * 1000;
-            if (mIsDsts[type] == 0) {
-                // Offset does not include DST so DST is 0 and the raw offset is the total offset.
-                rawOffset = totalOffset;
-                dstOffset = 0;
-            } else {
-                // Offset does include DST, we need to find the preceding transition that did not
-                // include the DST offset so that we can calculate the DST offset.
-                rawOffset = -1;
-                for (transitionIndex -= 1; transitionIndex >= 0; --transitionIndex) {
-                    type = mTypes[transitionIndex] & 0xff;
-                    if (mIsDsts[type] == 0) {
-                        rawOffset = mRawOffset + mOffsets[type] * 1000;
-                        break;
-                    }
-                }
-                // If no previous transition was found then use the earliest raw offset.
-                if (rawOffset == -1) {
-                    rawOffset = mEarliestRawOffset;
-                }
-
-                // The DST offset is the difference between the total and the raw offset.
-                dstOffset = totalOffset - rawOffset;
-            }
-        }
-
-        offsets[0] = rawOffset;
-        offsets[1] = dstOffset;
-
-        return totalOffset;
+        return mDelegate.getOffset(calc);
     }
 
     @Override
     public int getOffset(long when) {
-        int offsetIndex = findOffsetIndexForTimeInMilliseconds(when);
-        if (offsetIndex == -1) {
-            // Assume that all times before our first transition correspond to the
-            // oldest-known non-daylight offset. The obvious alternative would be to
-            // use the current raw offset, but that seems like a greater leap of faith.
-            return mEarliestRawOffset;
-        }
-        return mRawOffset + mOffsets[offsetIndex] * 1000;
+        return mDelegate.getOffset(when);
     }
 
-    @Override public boolean inDaylightTime(Date time) {
-        long when = time.getTime();
-        int offsetIndex = findOffsetIndexForTimeInMilliseconds(when);
-        if (offsetIndex == -1) {
-            // Assume that all times before our first transition are non-daylight.
-            // Transition data tends to start with a transition to daylight, so just
-            // copying the first transition would assume the opposite.
-            // http://code.google.com/p/android/issues/detail?id=14395
-            return false;
-        }
-        return mIsDsts[offsetIndex] == 1;
+    @Override
+    public boolean inDaylightTime(Date time) {
+        return mDelegate.isInDaylightTime(time.getTime());
     }
 
-    @Override public int getRawOffset() {
-        return mRawOffset;
+    @Override
+    public int getRawOffset() {
+        return mDelegate.getRawOffset();
     }
 
-    @Override public void setRawOffset(int off) {
-        mRawOffset = off;
+    @Override
+    public void setRawOffset(int off) {
+        mDelegate = mDelegate.createCopyWithRawOffset(off);
     }
 
-    @Override public int getDSTSavings() {
+    @Override
+    public int getDSTSavings() {
         return mDstSavings;
     }
 
-    @Override public boolean useDaylightTime() {
+    @Override
+    public boolean useDaylightTime() {
         return mUseDst;
     }
 
-    @Override public boolean hasSameRules(TimeZone timeZone) {
+    @Override
+    public boolean hasSameRules(TimeZone timeZone) {
         if (!(timeZone instanceof ZoneInfo)) {
             return false;
         }
         ZoneInfo other = (ZoneInfo) timeZone;
+
         if (mUseDst != other.mUseDst) {
             return false;
         }
         if (!mUseDst) {
-            return mRawOffset == other.mRawOffset;
+            return mDelegate.getRawOffset() == other.getRawOffset();
         }
-        return mRawOffset == other.mRawOffset
-                // Arrays.equals returns true if both arrays are null
-                && Arrays.equals(mOffsets, other.mOffsets)
-                && Arrays.equals(mIsDsts, other.mIsDsts)
-                && Arrays.equals(mTypes, other.mTypes)
-                && Arrays.equals(mTransitions, other.mTransitions);
+        return mDelegate.hasSameRules(other.mDelegate);
     }
 
-    @Override public boolean equals(Object obj) {
+    @Override
+    public boolean equals(Object obj) {
         if (!(obj instanceof ZoneInfo)) {
             return false;
         }
@@ -736,736 +281,29 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + getID().hashCode();
-        result = prime * result + Arrays.hashCode(mOffsets);
-        result = prime * result + Arrays.hashCode(mIsDsts);
-        result = prime * result + mRawOffset;
-        result = prime * result + Arrays.hashCode(mTransitions);
-        result = prime * result + Arrays.hashCode(mTypes);
-        result = prime * result + (mUseDst ? 1231 : 1237);
-        return result;
+        /*
+         * TODO(http://b/173499812): Can 2 ZoneInfo objects have different hashCode but equals?
+         * mDelegate.hashCode compares more fields than rules and ID.
+         */
+        return Objects.hash(mUseDst, mDelegate);
     }
 
     @Override
     public String toString() {
-        return getClass().getName() + "[id=\"" + getID() + "\"" +
-            ",mRawOffset=" + mRawOffset +
-            ",mEarliestRawOffset=" + mEarliestRawOffset +
-            ",mUseDst=" + mUseDst +
-            ",mDstSavings=" + mDstSavings +
-            ",transitions=" + mTransitions.length +
-            "]";
+        return getClass().getName() +
+                "[mDstSavings=" + mDstSavings +
+                ",mUseDst=" + mUseDst +
+                ",mDelegate=" + mDelegate.toString() + "]";
     }
 
     @Override
     public Object clone() {
-        // Overridden for documentation. The default clone() behavior is exactly what we want.
-        // Though mutable, the arrays of offset data are treated as immutable. Only ID and
-        // mRawOffset are mutable in this class, and those are an immutable object and a primitive
-        // respectively.
-        return super.clone();
+        // Pass the mDstSavings and mUseDst explicitly because they must not be recalculated when
+        // cloning. See {@link #create(ZoneInfoData)}.
+        return new ZoneInfo(mDelegate, mDstSavings, mUseDst);
     }
 
-    /**
-     * A class that represents a "wall time". This class is modeled on the C tm struct and
-     * is used to support android.text.format.Time behavior. Unlike the tm struct the year is
-     * represented as the full year, not the years since 1900.
-     *
-     * <p>This class contains a rewrite of various native functions that android.text.format.Time
-     * once relied on such as mktime_tz and localtime_tz. This replacement does not support leap
-     * seconds but does try to preserve behavior around ambiguous date/times found in the BSD
-     * version of mktime that was previously used.
-     *
-     * <p>The original native code used a 32-bit value for time_t on 32-bit Android, which
-     * was the only variant of Android available at the time. To preserve old behavior this code
-     * deliberately uses {@code int} rather than {@code long} for most things and performs
-     * calculations in seconds. This creates deliberate truncation issues for date / times before
-     * 1901 and after 2038. This is intentional but might be fixed in future if all the knock-ons
-     * can be resolved: Application code may have come to rely on the range so previously values
-     * like zero for year could indicate an invalid date but if we move to long the year zero would
-     * be valid.
-     *
-     * <p>All offsets are considered to be safe for addition / subtraction / multiplication without
-     * worrying about overflow. All absolute time arithmetic is checked for overflow / underflow.
-     *
-     * @hide
-     */
-    @libcore.api.CorePlatformApi
-    public static class WallTime {
-
-        // We use a GregorianCalendar (set to UTC) to handle all the date/time normalization logic
-        // and to convert from a broken-down date/time to a millis value.
-        // Unfortunately, it cannot represent an initial state with a zero day and would
-        // automatically normalize it, so we must copy values into and out of it as needed.
-        private final GregorianCalendar calendar;
-
-        private int year;
-        private int month;
-        private int monthDay;
-        private int hour;
-        private int minute;
-        private int second;
-        private int weekDay;
-        private int yearDay;
-        private int isDst;
-        private int gmtOffsetSeconds;
-
-        @libcore.api.CorePlatformApi
-        public WallTime() {
-            this.calendar = new GregorianCalendar(0, 0, 0, 0, 0, 0);
-            calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
-        }
-
-        /**
-         * Sets the wall time to a point in time using the time zone information provided. This
-         * is a replacement for the old native localtime_tz() function.
-         *
-         * <p>When going from an instant to a wall time it is always unambiguous because there
-         * is only one offset rule acting at any given instant. We do not consider leap seconds.
-         */
-        @libcore.api.CorePlatformApi
-        public void localtime(int timeSeconds, ZoneInfo zoneInfo) {
-            try {
-                int offsetSeconds = zoneInfo.mRawOffset / 1000;
-
-                // Find out the timezone DST state and adjustment.
-                byte isDst;
-                if (zoneInfo.mTransitions.length == 0) {
-                    isDst = 0;
-                } else {
-                    // offsetIndex can be in the range -1..zoneInfo.mOffsets.length - 1
-                    int offsetIndex = zoneInfo.findOffsetIndexForTimeInSeconds(timeSeconds);
-                    if (offsetIndex == -1) {
-                        // -1 means timeSeconds is "before the first recorded transition". The first
-                        // recorded transition is treated as a transition from non-DST and the
-                        // earliest known raw offset.
-                        offsetSeconds = zoneInfo.mEarliestRawOffset / 1000;
-                        isDst = 0;
-                    } else {
-                        offsetSeconds += zoneInfo.mOffsets[offsetIndex];
-                        isDst = zoneInfo.mIsDsts[offsetIndex];
-                    }
-                }
-
-                // Perform arithmetic that might underflow before setting fields.
-                int wallTimeSeconds = checked32BitAdd(timeSeconds, offsetSeconds);
-
-                // Set fields.
-                calendar.setTimeInMillis(wallTimeSeconds * 1000L);
-                copyFieldsFromCalendar();
-                this.isDst = isDst;
-                this.gmtOffsetSeconds = offsetSeconds;
-            } catch (CheckedArithmeticException e) {
-                // Just stop, leaving fields untouched.
-            }
-        }
-
-        /**
-         * Returns the time in seconds since beginning of the Unix epoch for the wall time using the
-         * time zone information provided. This is a replacement for an old native mktime_tz() C
-         * function.
-         *
-         * <p>When going from a wall time to an instant the answer can be ambiguous. A wall
-         * time can map to zero, one or two instants given sane date/time transitions. Sane
-         * in this case means that transitions occur less frequently than the offset
-         * differences between them (which could cause all sorts of craziness like the
-         * skipping out of transitions).
-         *
-         * <p>For example, this is not fully supported:
-         * <ul>
-         *     <li>t1 { time = 1, offset = 0 }
-         *     <li>t2 { time = 2, offset = -1 }
-         *     <li>t3 { time = 3, offset = -2 }
-         * </ul>
-         * A wall time in this case might map to t1, t2 or t3.
-         *
-         * <p>We do not handle leap seconds.
-         * <p>We assume that no timezone offset transition has an absolute offset > 24 hours.
-         * <p>We do not assume that adjacent transitions modify the DST state; adjustments can
-         * occur for other reasons such as when a zone changes its raw offset.
-         */
-        @libcore.api.CorePlatformApi
-        public int mktime(ZoneInfo zoneInfo) {
-            // Normalize isDst to -1, 0 or 1 to simplify isDst equality checks below.
-            this.isDst = this.isDst > 0 ? this.isDst = 1 : this.isDst < 0 ? this.isDst = -1 : 0;
-
-            copyFieldsToCalendar();
-            final long longWallTimeSeconds = calendar.getTimeInMillis() / 1000;
-            if (Integer.MIN_VALUE > longWallTimeSeconds
-                    || longWallTimeSeconds > Integer.MAX_VALUE) {
-                // For compatibility with the old native 32-bit implementation we must treat
-                // this as an error. Note: -1 could be confused with a real time.
-                return -1;
-            }
-
-            try {
-                final int wallTimeSeconds =  (int) longWallTimeSeconds;
-                final int rawOffsetSeconds = zoneInfo.mRawOffset / 1000;
-                final int rawTimeSeconds = checked32BitSubtract(wallTimeSeconds, rawOffsetSeconds);
-
-                if (zoneInfo.mTransitions.length == 0) {
-                    // There is no transition information. There is just a raw offset for all time.
-                    if (this.isDst > 0) {
-                        // Caller has asserted DST, but there is no DST information available.
-                        return -1;
-                    }
-                    copyFieldsFromCalendar();
-                    this.isDst = 0;
-                    this.gmtOffsetSeconds = rawOffsetSeconds;
-                    return rawTimeSeconds;
-                }
-
-                // We cannot know for sure what instant the wall time will map to. Unfortunately, in
-                // order to know for sure we need the timezone information, but to get the timezone
-                // information we need an instant. To resolve this we use the raw offset to find an
-                // OffsetInterval; this will get us the OffsetInterval we need or very close.
-
-                // The initialTransition can be between -1 and (zoneInfo.mTransitions - 1). -1
-                // indicates the rawTime is before the first transition and is handled gracefully by
-                // createOffsetInterval().
-                final int initialTransitionIndex = zoneInfo.findTransitionIndex(rawTimeSeconds);
-
-                if (isDst < 0) {
-                    // This is treated as a special case to get it out of the way:
-                    // When a caller has set isDst == -1 it means we can return the first match for
-                    // the wall time we find. If the caller has specified a wall time that cannot
-                    // exist this always returns -1.
-
-                    Integer result = doWallTimeSearch(zoneInfo, initialTransitionIndex,
-                            wallTimeSeconds, true /* mustMatchDst */);
-                    return result == null ? -1 : result;
-                }
-
-                // If the wall time asserts a DST (isDst == 0 or 1) the search is performed twice:
-                // 1) The first attempts to find a DST offset that matches isDst exactly.
-                // 2) If it fails, isDst is assumed to be incorrect and adjustments are made to see
-                // if a valid wall time can be created. The result can be somewhat arbitrary.
-
-                Integer result = doWallTimeSearch(zoneInfo, initialTransitionIndex, wallTimeSeconds,
-                        true /* mustMatchDst */);
-                if (result == null) {
-                    result = doWallTimeSearch(zoneInfo, initialTransitionIndex, wallTimeSeconds,
-                            false /* mustMatchDst */);
-                }
-                if (result == null) {
-                    result = -1;
-                }
-                return result;
-            } catch (CheckedArithmeticException e) {
-                return -1;
-            }
-        }
-
-        /**
-         * Attempt to apply DST adjustments to {@code oldWallTimeSeconds} to create a wall time in
-         * {@code targetInterval}.
-         *
-         * <p>This is used when a caller has made an assertion about standard time / DST that cannot
-         * be matched to any offset interval that exists. We must therefore assume that the isDst
-         * assertion is incorrect and the invalid wall time is the result of some modification the
-         * caller made to a valid wall time that pushed them outside of the offset interval they
-         * were in. We must correct for any DST change that should have been applied when they did
-         * so.
-         *
-         * <p>Unfortunately, we have no information about what adjustment they made and so cannot
-         * know which offset interval they were previously in. For example, they may have added a
-         * second or a year to a valid time to arrive at what they have.
-         *
-         * <p>We try all offset types that are not the same as the isDst the caller asserted. For
-         * each possible offset we work out the offset difference between that and
-         * {@code targetInterval}, apply it, and see if we are still in {@code targetInterval}. If
-         * we are, then we have found an adjustment.
-         */
-        private Integer tryOffsetAdjustments(ZoneInfo zoneInfo, int oldWallTimeSeconds,
-                OffsetInterval targetInterval, int transitionIndex, int isDstToFind)
-                throws CheckedArithmeticException {
-
-            int[] offsetsToTry = getOffsetsOfType(zoneInfo, transitionIndex, isDstToFind);
-            for (int j = 0; j < offsetsToTry.length; j++) {
-                int rawOffsetSeconds = zoneInfo.mRawOffset / 1000;
-                int jOffsetSeconds = rawOffsetSeconds + offsetsToTry[j];
-                int targetIntervalOffsetSeconds = targetInterval.getTotalOffsetSeconds();
-                int adjustmentSeconds = targetIntervalOffsetSeconds - jOffsetSeconds;
-                int adjustedWallTimeSeconds =
-                        checked32BitAdd(oldWallTimeSeconds, adjustmentSeconds);
-                if (targetInterval.containsWallTime(adjustedWallTimeSeconds)) {
-                    // Perform any arithmetic that might overflow.
-                    int returnValue = checked32BitSubtract(adjustedWallTimeSeconds,
-                            targetIntervalOffsetSeconds);
-
-                    // Modify field state and return the result.
-                    calendar.setTimeInMillis(adjustedWallTimeSeconds * 1000L);
-                    copyFieldsFromCalendar();
-                    this.isDst = targetInterval.getIsDst();
-                    this.gmtOffsetSeconds = targetIntervalOffsetSeconds;
-                    return returnValue;
-                }
-            }
-            return null;
-        }
-
-        /**
-         * Return an array of offsets that have the requested {@code isDst} value.
-         * The {@code startIndex} is used as a starting point so transitions nearest
-         * to that index are returned first.
-         */
-        private static int[] getOffsetsOfType(ZoneInfo zoneInfo, int startIndex, int isDst) {
-            // +1 to account for the synthetic transition we invent before the first recorded one.
-            int[] offsets = new int[zoneInfo.mOffsets.length + 1];
-            boolean[] seen = new boolean[zoneInfo.mOffsets.length];
-            int numFound = 0;
-
-            int delta = 0;
-            boolean clampTop = false;
-            boolean clampBottom = false;
-            do {
-                // delta = { 1, -1, 2, -2, 3, -3...}
-                delta *= -1;
-                if (delta >= 0) {
-                    delta++;
-                }
-
-                int transitionIndex = startIndex + delta;
-                if (delta < 0 && transitionIndex < -1) {
-                    clampBottom = true;
-                    continue;
-                } else if (delta > 0 && transitionIndex >=  zoneInfo.mTypes.length) {
-                    clampTop = true;
-                    continue;
-                }
-
-                if (transitionIndex == -1) {
-                    if (isDst == 0) {
-                        // Synthesize a non-DST transition before the first transition we have
-                        // data for.
-                        offsets[numFound++] = 0; // offset of 0 from raw offset
-                    }
-                    continue;
-                }
-                int type = zoneInfo.mTypes[transitionIndex] & 0xff;
-                if (!seen[type]) {
-                    if (zoneInfo.mIsDsts[type] == isDst) {
-                        offsets[numFound++] = zoneInfo.mOffsets[type];
-                    }
-                    seen[type] = true;
-                }
-            } while (!(clampTop && clampBottom));
-
-            int[] toReturn = new int[numFound];
-            System.arraycopy(offsets, 0, toReturn, 0, numFound);
-            return toReturn;
-        }
-
-        /**
-         * Find a time <em>in seconds</em> the same or close to {@code wallTimeSeconds} that
-         * satisfies {@code mustMatchDst}. The search begins around the timezone offset transition
-         * with {@code initialTransitionIndex}.
-         *
-         * <p>If {@code mustMatchDst} is {@code true} the method can only return times that
-         * use timezone offsets that satisfy the {@code this.isDst} requirements.
-         * If {@code this.isDst == -1} it means that any offset can be used.
-         *
-         * <p>If {@code mustMatchDst} is {@code false} any offset that covers the
-         * currently set time is acceptable. That is: if {@code this.isDst} == -1, any offset
-         * transition can be used, if it is 0 or 1 the offset used must match {@code this.isDst}.
-         *
-         * <p>Note: This method both uses and can modify field state. It returns the matching time
-         * in seconds if a match has been found and modifies fields, or it returns {@code null} and
-         * leaves the field state unmodified.
-         */
-        private Integer doWallTimeSearch(ZoneInfo zoneInfo, int initialTransitionIndex,
-                int wallTimeSeconds, boolean mustMatchDst) throws CheckedArithmeticException {
-
-            // The loop below starts at the initialTransitionIndex and radiates out from that point
-            // up to 24 hours in either direction by applying transitionIndexDelta to inspect
-            // adjacent transitions (0, -1, +1, -2, +2). 24 hours is used because we assume that no
-            // total offset from UTC is ever > 24 hours. clampTop and clampBottom are used to
-            // indicate whether the search has either searched > 24 hours or exhausted the
-            // transition data in that direction. The search stops when a match is found or if
-            // clampTop and clampBottom are both true.
-            // The match logic employed is determined by the mustMatchDst parameter.
-            final int MAX_SEARCH_SECONDS = 24 * 60 * 60;
-            boolean clampTop = false, clampBottom = false;
-            int loop = 0;
-            do {
-                // transitionIndexDelta = { 0, -1, 1, -2, 2,..}
-                int transitionIndexDelta = (loop + 1) / 2;
-                if (loop % 2 == 1) {
-                    transitionIndexDelta *= -1;
-                }
-                loop++;
-
-                // Only do any work in this iteration if we need to.
-                if (transitionIndexDelta > 0 && clampTop
-                        || transitionIndexDelta < 0 && clampBottom) {
-                    continue;
-                }
-
-                // Obtain the OffsetInterval to use.
-                int currentTransitionIndex = initialTransitionIndex + transitionIndexDelta;
-                OffsetInterval offsetInterval =
-                        OffsetInterval.create(zoneInfo, currentTransitionIndex);
-                if (offsetInterval == null) {
-                    // No transition exists with the index we tried: Stop searching in the
-                    // current direction.
-                    clampTop |= (transitionIndexDelta > 0);
-                    clampBottom |= (transitionIndexDelta < 0);
-                    continue;
-                }
-
-                // Match the wallTimeSeconds against the OffsetInterval.
-                if (mustMatchDst) {
-                    // Work out if the interval contains the wall time the caller specified and
-                    // matches their isDst value.
-                    if (offsetInterval.containsWallTime(wallTimeSeconds)) {
-                        if (this.isDst == -1 || offsetInterval.getIsDst() == this.isDst) {
-                            // This always returns the first OffsetInterval it finds that matches
-                            // the wall time and isDst requirements. If this.isDst == -1 this means
-                            // the result might be a DST or a non-DST answer for wall times that can
-                            // exist in two OffsetIntervals.
-                            int totalOffsetSeconds = offsetInterval.getTotalOffsetSeconds();
-                            int returnValue =
-                                    checked32BitSubtract(wallTimeSeconds, totalOffsetSeconds);
-
-                            copyFieldsFromCalendar();
-                            this.isDst = offsetInterval.getIsDst();
-                            this.gmtOffsetSeconds = totalOffsetSeconds;
-                            return returnValue;
-                        }
-                    }
-                } else {
-                    // To retain similar behavior to the old native implementation: if the caller is
-                    // asserting the same isDst value as the OffsetInterval we are looking at we do
-                    // not try to find an adjustment from another OffsetInterval of the same isDst
-                    // type. If you remove this you get different results in situations like a
-                    // DST -> DST transition or STD -> STD transition that results in an interval of
-                    // "skipped" wall time. For example: if 01:30 (DST) is invalid and between two
-                    // DST intervals, and the caller has passed isDst == 1, this results in a -1
-                    // being returned.
-                    if (isDst != offsetInterval.getIsDst()) {
-                        final int isDstToFind = isDst;
-                        Integer returnValue = tryOffsetAdjustments(zoneInfo, wallTimeSeconds,
-                                offsetInterval, currentTransitionIndex, isDstToFind);
-                        if (returnValue != null) {
-                            return returnValue;
-                        }
-                    }
-                }
-
-                // See if we can avoid another loop in the current direction.
-                if (transitionIndexDelta > 0) {
-                    // If we are searching forward and the OffsetInterval we have ends
-                    // > MAX_SEARCH_SECONDS after the wall time, we don't need to look any further
-                    // forward.
-                    boolean endSearch = offsetInterval.getEndWallTimeSeconds() - wallTimeSeconds
-                            > MAX_SEARCH_SECONDS;
-                    if (endSearch) {
-                        clampTop = true;
-                    }
-                } else if (transitionIndexDelta < 0) {
-                    boolean endSearch = wallTimeSeconds - offsetInterval.getStartWallTimeSeconds()
-                            >= MAX_SEARCH_SECONDS;
-                    if (endSearch) {
-                        // If we are searching backward and the OffsetInterval starts
-                        // > MAX_SEARCH_SECONDS before the wall time, we don't need to look any
-                        // further backwards.
-                        clampBottom = true;
-                    }
-                }
-            } while (!(clampTop && clampBottom));
-            return null;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setYear(int year) {
-            this.year = year;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setMonth(int month) {
-            this.month = month;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setMonthDay(int monthDay) {
-            this.monthDay = monthDay;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setHour(int hour) {
-            this.hour = hour;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setMinute(int minute) {
-            this.minute = minute;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setSecond(int second) {
-            this.second = second;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setWeekDay(int weekDay) {
-            this.weekDay = weekDay;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setYearDay(int yearDay) {
-            this.yearDay = yearDay;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setIsDst(int isDst) {
-            this.isDst = isDst;
-        }
-
-        @libcore.api.CorePlatformApi
-        public void setGmtOffset(int gmtoff) {
-            this.gmtOffsetSeconds = gmtoff;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getYear() {
-            return year;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getMonth() {
-            return month;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getMonthDay() {
-            return monthDay;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getHour() {
-            return hour;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getMinute() {
-            return minute;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getSecond() {
-            return second;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getWeekDay() {
-            return weekDay;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getYearDay() {
-            return yearDay;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getGmtOffset() {
-            return gmtOffsetSeconds;
-        }
-
-        @libcore.api.CorePlatformApi
-        public int getIsDst() {
-            return isDst;
-        }
-
-        private void copyFieldsToCalendar() {
-            calendar.set(Calendar.YEAR, year);
-            calendar.set(Calendar.MONTH, month);
-            calendar.set(Calendar.DAY_OF_MONTH, monthDay);
-            calendar.set(Calendar.HOUR_OF_DAY, hour);
-            calendar.set(Calendar.MINUTE, minute);
-            calendar.set(Calendar.SECOND, second);
-            calendar.set(Calendar.MILLISECOND, 0);
-        }
-
-        private void copyFieldsFromCalendar() {
-            year = calendar.get(Calendar.YEAR);
-            month = calendar.get(Calendar.MONTH);
-            monthDay = calendar.get(Calendar.DAY_OF_MONTH);
-            hour = calendar.get(Calendar.HOUR_OF_DAY);
-            minute = calendar.get(Calendar.MINUTE);
-            second =  calendar.get(Calendar.SECOND);
-
-            // Calendar uses Sunday == 1. Android Time uses Sunday = 0.
-            weekDay = calendar.get(Calendar.DAY_OF_WEEK) - 1;
-            // Calendar enumerates from 1, Android Time enumerates from 0.
-            yearDay = calendar.get(Calendar.DAY_OF_YEAR) - 1;
-        }
-    }
-
-    /**
-     * A wall-time representation of a timezone offset interval.
-     *
-     * <p>Wall-time means "as it would appear locally in the timezone in which it applies".
-     * For example in 2007:
-     * PST was a -8:00 offset that ran until Mar 11, 2:00 AM.
-     * PDT was a -7:00 offset and ran from Mar 11, 3:00 AM to Nov 4, 2:00 AM.
-     * PST was a -8:00 offset and ran from Nov 4, 1:00 AM.
-     * Crucially this means that there was a "gap" after PST when PDT started, and an overlap when
-     * PDT ended and PST began.
-     *
-     * <p>Although wall-time means "local time", for convenience all wall-time values are stored in
-     * the number of seconds since the beginning of the Unix epoch to get that time <em>in UTC</em>.
-     * To convert from a wall-time to the actual UTC time it is necessary to <em>subtract</em> the
-     * {@code totalOffsetSeconds}.
-     * For example: If the offset in PST is -07:00 hours, then:
-     * timeInPstSeconds = wallTimeUtcSeconds - offsetSeconds
-     * i.e. 13:00 UTC - (-07:00) = 20:00 UTC = 13:00 PST
-     */
-    static class OffsetInterval {
-
-        /** The time the interval starts in seconds since start of epoch, inclusive. */
-        private final int startWallTimeSeconds;
-        /** The time the interval ends in seconds since start of epoch, exclusive. */
-        private final int endWallTimeSeconds;
-        private final int isDst;
-        private final int totalOffsetSeconds;
-
-        /**
-         * Creates an {@link OffsetInterval}.
-         *
-         * <p>If {@code transitionIndex} is -1, where possible the transition is synthesized to run
-         * from the beginning of 32-bit time until the first transition in {@code timeZone} with
-         * offset information based on the first type defined. If {@code transitionIndex} is the
-         * last transition, that transition is considered to run until the end of 32-bit time.
-         * Otherwise, the information is extracted from {@code timeZone.mTransitions},
-         * {@code timeZone.mOffsets} and {@code timeZone.mIsDsts}.
-         *
-         * <p>This method can return null when:
-         * <ol>
-         * <li>the {@code transitionIndex} is outside the allowed range, i.e.
-         *   {@code transitionIndex < -1 || transitionIndex >= [the number of transitions]}.</li>
-         * <li>when calculations result in a zero-length interval. This is only expected to occur
-         *   when dealing with transitions close to (or exactly at) {@code Integer.MIN_VALUE} and
-         *   {@code Integer.MAX_VALUE} and where it's difficult to convert from UTC to local times.
-         *   </li>
-         * </ol>
-         */
-        public static OffsetInterval create(ZoneInfo timeZone, int transitionIndex) {
-            if (transitionIndex < -1 || transitionIndex >= timeZone.mTransitions.length) {
-                return null;
-            }
-
-            if (transitionIndex == -1) {
-                int totalOffsetSeconds = timeZone.mEarliestRawOffset / 1000;
-                int isDst = 0;
-
-                int startWallTimeSeconds = Integer.MIN_VALUE;
-                int endWallTimeSeconds =
-                        saturated32BitAdd(timeZone.mTransitions[0], totalOffsetSeconds);
-                if (startWallTimeSeconds == endWallTimeSeconds) {
-                    // There's no point in returning an OffsetInterval that lasts 0 seconds.
-                    return null;
-                }
-                return new OffsetInterval(startWallTimeSeconds, endWallTimeSeconds, isDst,
-                        totalOffsetSeconds);
-            }
-
-            int rawOffsetSeconds = timeZone.mRawOffset / 1000;
-            int type = timeZone.mTypes[transitionIndex] & 0xff;
-            int totalOffsetSeconds = timeZone.mOffsets[type] + rawOffsetSeconds;
-            int endWallTimeSeconds;
-            if (transitionIndex == timeZone.mTransitions.length - 1) {
-                endWallTimeSeconds = Integer.MAX_VALUE;
-            } else {
-                endWallTimeSeconds = saturated32BitAdd(
-                        timeZone.mTransitions[transitionIndex + 1], totalOffsetSeconds);
-            }
-            int isDst = timeZone.mIsDsts[type];
-            int startWallTimeSeconds =
-                    saturated32BitAdd(timeZone.mTransitions[transitionIndex], totalOffsetSeconds);
-            if (startWallTimeSeconds == endWallTimeSeconds) {
-                // There's no point in returning an OffsetInterval that lasts 0 seconds.
-                return null;
-            }
-            return new OffsetInterval(
-                    startWallTimeSeconds, endWallTimeSeconds, isDst, totalOffsetSeconds);
-        }
-
-        private OffsetInterval(int startWallTimeSeconds, int endWallTimeSeconds, int isDst,
-                int totalOffsetSeconds) {
-            this.startWallTimeSeconds = startWallTimeSeconds;
-            this.endWallTimeSeconds = endWallTimeSeconds;
-            this.isDst = isDst;
-            this.totalOffsetSeconds = totalOffsetSeconds;
-        }
-
-        public boolean containsWallTime(long wallTimeSeconds) {
-            return wallTimeSeconds >= startWallTimeSeconds && wallTimeSeconds < endWallTimeSeconds;
-        }
-
-        public int getIsDst() {
-            return isDst;
-        }
-
-        public int getTotalOffsetSeconds() {
-            return totalOffsetSeconds;
-        }
-
-        public long getEndWallTimeSeconds() {
-            return endWallTimeSeconds;
-        }
-
-        public long getStartWallTimeSeconds() {
-            return startWallTimeSeconds;
-        }
-    }
-
-    /**
-     * An exception used to indicate an arithmetic overflow or underflow.
-     */
-    private static class CheckedArithmeticException extends Exception {
-    }
-
-    /**
-     * Calculate (a + b). The result must be in the Integer range otherwise an exception is thrown.
-     *
-     * @throws CheckedArithmeticException if overflow or underflow occurs
-     */
-    private static int checked32BitAdd(long a, int b) throws CheckedArithmeticException {
-        // Adapted from Guava IntMath.checkedAdd();
-        long result = a + b;
-        if (result != (int) result) {
-            throw new CheckedArithmeticException();
-        }
-        return (int) result;
-    }
-
-    /**
-     * Calculate (a - b). The result must be in the Integer range otherwise an exception is thrown.
-     *
-     * @throws CheckedArithmeticException if overflow or underflow occurs
-     */
-    private static int checked32BitSubtract(long a, int b) throws CheckedArithmeticException {
-        // Adapted from Guava IntMath.checkedSubtract();
-        long result = a - b;
-        if (result != (int) result) {
-            throw new CheckedArithmeticException();
-        }
-        return (int) result;
-    }
-
-    /**
-     * Calculate (a + b). If the result would overflow or underflow outside of the Integer range
-     * Integer.MAX_VALUE or Integer.MIN_VALUE will be returned, respectively.
-     */
-    private static int saturated32BitAdd(long a, int b) {
-        long result = a + b;
-        if (result > Integer.MAX_VALUE) {
-            return Integer.MAX_VALUE;
-        } else if (result < Integer.MIN_VALUE) {
-            return Integer.MIN_VALUE;
-        }
-        return (int) result;
+    public int getOffsetsByUtcTime(long utcTimeInMillis, int[] offsets) {
+        return mDelegate.getOffsetsByUtcTime(utcTimeInMillis, offsets);
     }
 }
diff --git a/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java b/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java
index 8872e1d..52ed681 100644
--- a/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java
+++ b/luni/src/main/java/org/apache/harmony/xml/ExpatParser.java
@@ -268,7 +268,7 @@
          * specify how to tell whether or not the systemId is a URL let alone
          * how to resolve it.
          *
-         * Other implementations do various insane things. We try to keep it
+         * Other implementations do various dangerous things. We try to keep it
          * simple: if the systemId parses as a URI and it's relative, we try to
          * resolve it against the parent document's systemId. If anything goes
          * wrong, we go with the original systemId. If crazybob had designed
diff --git a/luni/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java b/luni/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java
index d9b903f..662a0ff 100644
--- a/luni/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xml/dom/DocumentTypeImpl.java
@@ -81,12 +81,12 @@
     }
 
     public NamedNodeMap getEntities() {
-        // TODO Dummy. Implement this later, if at all (we're DOM level 2 only).
+        // TODO Fake. Implement this later, if at all (we're DOM level 2 only).
         return null;
     }
 
     public String getInternalSubset() {
-        // TODO Dummy. Implement this later, if at all (we're DOM level 2 only).
+        // TODO Fake. Implement this later, if at all (we're DOM level 2 only).
         return null;
     }
 
@@ -95,7 +95,7 @@
     }
 
     public NamedNodeMap getNotations() {
-        // TODO Dummy. Implement this later, if at all (we're DOM level 2 only).
+        // TODO Fake. Implement this later, if at all (we're DOM level 2 only).
         return null;
     }
 
diff --git a/luni/src/main/native/Android.bp b/luni/src/main/native/Android.bp
index 423cdd1..1f69c67 100644
--- a/luni/src/main/native/Android.bp
+++ b/luni/src/main/native/Android.bp
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_luni_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_luni_license"],
+}
+
 filegroup {
     name: "luni_native_srcs",
     visibility: [
@@ -30,9 +38,8 @@
         "java_lang_StringToReal.cpp",
         "java_lang_invoke_MethodHandle.cpp",
         "java_lang_invoke_VarHandle.cpp",
-        "java_math_NativeBN.cpp",
+        "libcore_math_NativeBN.cpp",
         "libcore_icu_ICU.cpp",
-        "libcore_icu_TimeZoneNames.cpp",
         "libcore_io_AsynchronousCloseMonitor.cpp",
         "libcore_io_Linux.cpp",
         "libcore_io_Memory.cpp",
diff --git a/luni/src/main/native/IcuUtilities.cpp b/luni/src/main/native/IcuUtilities.cpp
index 0db59f9..6811ce5 100644
--- a/luni/src/main/native/IcuUtilities.cpp
+++ b/luni/src/main/native/IcuUtilities.cpp
@@ -16,18 +16,11 @@
 
 #define LOG_TAG "IcuUtilities"
 
-#include <android/log.h>
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
 
 #include "IcuUtilities.h"
 
 #include "JniConstants.h"
-#include "JniException.h"
-#include "unicode/strenum.h"
-#include "unicode/ustring.h"
-#include "unicode/uloc.h"
 
 bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error) {
   if (U_SUCCESS(error)) {
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
old mode 100644
new mode 100755
index 478d015..5127fa1
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -45,6 +45,7 @@
 
 // Constants
 jclass booleanClass;
+jclass byteBufferClass;
 jclass doubleClass;
 jclass errnoExceptionClass;
 jclass fileDescriptorClass;
@@ -60,11 +61,15 @@
 jclass longClass;
 jclass netlinkSocketAddressClass;
 jclass packetSocketAddressClass;
+jclass vmSocketAddressClass;
+jclass primitiveByteArrayClass;
 jclass stringClass;
 jclass structAddrinfoClass;
+jclass structCmsghdrClass;
 jclass structGroupReqClass;
 jclass structIfaddrsClass;
 jclass structLingerClass;
+jclass structMsghdrClass;
 jclass structPasswdClass;
 jclass structPollfdClass;
 jclass structStatClass;
@@ -90,6 +95,7 @@
     }
 
     booleanClass = findClass(env, "java/lang/Boolean");
+    byteBufferClass = findClass(env, "java/nio/ByteBuffer");
     doubleClass = findClass(env, "java/lang/Double");
     errnoExceptionClass = findClass(env, "android/system/ErrnoException");
     fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
@@ -105,11 +111,15 @@
     longClass = findClass(env, "java/lang/Long");
     netlinkSocketAddressClass = findClass(env, "android/system/NetlinkSocketAddress");
     packetSocketAddressClass = findClass(env, "android/system/PacketSocketAddress");
+    vmSocketAddressClass = findClass(env, "android/system/VmSocketAddress");
+    primitiveByteArrayClass = findClass(env, "[B");
     stringClass = findClass(env, "java/lang/String");
     structAddrinfoClass = findClass(env, "android/system/StructAddrinfo");
+    structCmsghdrClass = findClass(env, "android/system/StructCmsghdr");
     structGroupReqClass = findClass(env, "android/system/StructGroupReq");
     structIfaddrsClass = findClass(env, "android/system/StructIfaddrs");
     structLingerClass = findClass(env, "android/system/StructLinger");
+    structMsghdrClass = findClass(env, "android/system/StructMsghdr");
     structPasswdClass = findClass(env, "android/system/StructPasswd");
     structPollfdClass = findClass(env, "android/system/StructPollfd");
     structStatClass = findClass(env, "android/system/StructStat");
@@ -148,6 +158,11 @@
     return booleanClass;
 }
 
+jclass JniConstants::GetByteBufferClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return byteBufferClass;
+}
+
 jclass JniConstants::GetDoubleClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return doubleClass;
@@ -223,6 +238,16 @@
     return packetSocketAddressClass;
 }
 
+jclass JniConstants::GetVmSocketAddressClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return vmSocketAddressClass;
+}
+
+jclass JniConstants::GetPrimitiveByteArrayClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return primitiveByteArrayClass;
+}
+
 jclass JniConstants::GetStringClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return stringClass;
@@ -233,6 +258,11 @@
     return structAddrinfoClass;
 }
 
+jclass JniConstants::GetStructCmsghdrClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return structCmsghdrClass;
+}
+
 jclass JniConstants::GetStructGroupReqClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return structGroupReqClass;
@@ -248,6 +278,11 @@
     return structLingerClass;
 }
 
+jclass JniConstants::GetStructMsghdrClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return structMsghdrClass;
+}
+
 jclass JniConstants::GetStructPasswdClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return structPasswdClass;
diff --git a/luni/src/main/native/JniConstants.h b/luni/src/main/native/JniConstants.h
old mode 100644
new mode 100755
index 868e928..bf824b8
--- a/luni/src/main/native/JniConstants.h
+++ b/luni/src/main/native/JniConstants.h
@@ -30,6 +30,7 @@
     static void Invalidate();
 
     static jclass GetBooleanClass(JNIEnv* env);
+    static jclass GetByteBufferClass(JNIEnv* env);
     static jclass GetDoubleClass(JNIEnv* env);
     static jclass GetErrnoExceptionClass(JNIEnv* env);
     static jclass GetFileDescriptorClass(JNIEnv* env);
@@ -45,12 +46,16 @@
     static jclass GetLongClass(JNIEnv* env);
     static jclass GetNetlinkSocketAddressClass(JNIEnv* env);
     static jclass GetPacketSocketAddressClass(JNIEnv* env);
+    static jclass GetVmSocketAddressClass(JNIEnv* env);
+    static jclass GetPrimitiveByteArrayClass(JNIEnv* env);
     static jclass GetStringClass(JNIEnv* env);
     static jclass GetStructAddrinfoClass(JNIEnv* env);
+    static jclass GetStructCmsghdrClass(JNIEnv* env);
     static jclass GetStructFlockClass(JNIEnv* env);
     static jclass GetStructGroupReqClass(JNIEnv* env);
     static jclass GetStructIfaddrsClass(JNIEnv* env);
     static jclass GetStructLingerClass(JNIEnv* env);
+    static jclass GetStructMsghdrClass(JNIEnv* env);
     static jclass GetStructPasswdClass(JNIEnv* env);
     static jclass GetStructPollfdClass(JNIEnv* env);
     static jclass GetStructStatClass(JNIEnv* env);
diff --git a/luni/src/main/native/JniException.cpp b/luni/src/main/native/JniException.cpp
index 164281d..01346ef 100644
--- a/luni/src/main/native/JniException.cpp
+++ b/luni/src/main/native/JniException.cpp
@@ -19,15 +19,6 @@
 #include "JniException.h"
 #include <nativehelper/JNIHelp.h>
 
-void jniThrowExceptionWithErrno(JNIEnv* env, const char* exceptionClassName, int error) {
-    char buf[BUFSIZ];
-    jniThrowException(env, exceptionClassName, jniStrError(error, buf, sizeof(buf)));
-}
-
 void jniThrowOutOfMemoryError(JNIEnv* env, const char* message) {
     jniThrowException(env, "java/lang/OutOfMemoryError", message);
 }
-
-void jniThrowSocketException(JNIEnv* env, int error) {
-    jniThrowExceptionWithErrno(env, "java/net/SocketException", error);
-}
diff --git a/luni/src/main/native/JniException.h b/luni/src/main/native/JniException.h
index 2d4a097..cff68b8 100644
--- a/luni/src/main/native/JniException.h
+++ b/luni/src/main/native/JniException.h
@@ -19,9 +19,6 @@
 
 #include "jni.h"
 
-void jniThrowExceptionWithErrno(JNIEnv* env, const char* exceptionClassName, int error);
-
 void jniThrowOutOfMemoryError(JNIEnv* env, const char* message);
-void jniThrowSocketException(JNIEnv* env, int error);
 
 #endif  // JNI_EXCEPTION_H_included
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
old mode 100644
new mode 100755
index b765c4e..cd695b7
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -21,6 +21,7 @@
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -30,6 +31,9 @@
 
 #include "JniConstants.h"
 
+#include <log/log.h>
+
+
 jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, jint* port) {
     // Convert IPv4-mapped IPv6 addresses to IPv4 addresses.
     // The RI states "Java will never return an IPv4-mapped address".
@@ -199,6 +203,325 @@
     return inetAddressToSockaddr(env, inetAddress, port, ss, sa_len, true);
 }
 
+/*
+ * Fill msg_contrl data from structCmsghdr[]
+ */
+bool structCmsghdrArrayToMsgcontrol(JNIEnv* env, jobjectArray cmsgArray, struct msghdr& mhdr) {
+    struct cmsghdr *cm = NULL;
+    int i = 0;
+    jclass structCmsghdrClass = JniConstants::GetStructCmsghdrClass(env);
+    static jfieldID cmsgDataFid = env->GetFieldID(structCmsghdrClass, "cmsg_data", "[B");
+    if (!cmsgDataFid) {
+        return false;
+    }
+    static jfieldID cmsgLevelFid = env->GetFieldID(structCmsghdrClass, "cmsg_level", "I");
+    if (!cmsgLevelFid) {
+        return false;
+    }
+    static jfieldID cmsgTypeFid = env->GetFieldID(structCmsghdrClass, "cmsg_type", "I");
+    if (!cmsgTypeFid) {
+        return false;
+    }
+
+    int cmsgArrayize = env->GetArrayLength(cmsgArray);
+    if (!cmsgArrayize) {
+        // Return true since msg_control is optional parameter.
+        return true;
+    }
+
+    for (int i = 0; i < cmsgArrayize; ++i) {
+        ScopedLocalRef<jobject> cmsg(env, env->GetObjectArrayElement(cmsgArray, i));
+        ScopedLocalRef<jbyteArray> cmsgData(env, reinterpret_cast<jbyteArray>(
+                env->GetObjectField(cmsg.get(), cmsgDataFid)));
+
+        mhdr.msg_controllen += CMSG_SPACE(env->GetArrayLength(cmsgData.get()));
+    }
+
+    mhdr.msg_control = (unsigned char*)malloc(mhdr.msg_controllen);
+    if (mhdr.msg_control == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+        return false;
+    }
+    memset(mhdr.msg_control, 0, mhdr.msg_controllen);
+
+    // Loop over each cmsghdr header and set data.
+    for (cm = CMSG_FIRSTHDR(&mhdr), i = 0; (cm != NULL); cm = CMSG_NXTHDR(&mhdr, cm), ++i)
+    {
+        size_t data_len = 0;
+        ScopedLocalRef<jobject> cmsg(env, env->GetObjectArrayElement(cmsgArray, i));
+        ScopedLocalRef<jbyteArray> cmsgData(env, reinterpret_cast<jbyteArray>(
+                env->GetObjectField(cmsg.get(), cmsgDataFid)));
+
+        cm->cmsg_level = env->GetIntField(cmsg.get(), cmsgLevelFid);
+        cm->cmsg_type  = env->GetIntField(cmsg.get(), cmsgTypeFid);
+        data_len = env->GetArrayLength(cmsgData.get());
+        cm->cmsg_len   = CMSG_LEN(data_len);
+        env->GetByteArrayRegion(cmsgData.get(), 0,
+                data_len, reinterpret_cast<jbyte*>CMSG_DATA(cm));
+    }
+    return true;
+}
+
+/*
+ * Fill structCmsghdr[] data per msgcontrol data, used when recvmsg
+ */
+bool msgcontrolToStructCmsghdrArray(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr) {
+    struct cmsghdr *cm = NULL;
+    int i = 0;
+
+    static jfieldID msgControlFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+                                                 "msg_control", "[Landroid/system/StructCmsghdr;");
+    if (!msgControlFid) {
+        return false;
+    }
+
+    static jmethodID cmsgInitMid = env->GetMethodID(JniConstants::GetStructCmsghdrClass(env),
+                                                    "<init>", "(II[B)V");
+    if (!cmsgInitMid) {
+        return false;
+    }
+
+    int cmsghdrNumber = 0;
+    for (cm = CMSG_FIRSTHDR(&mhdr); (cm != NULL); cm = CMSG_NXTHDR(&mhdr, cm)) {
+        cmsghdrNumber++;
+    }
+    if (!cmsghdrNumber)
+        return true;
+
+    jobjectArray structCmsghdrArray = env->NewObjectArray(cmsghdrNumber,
+                                          JniConstants::GetStructCmsghdrClass(env), NULL);
+    if (!structCmsghdrArray) {
+        return false;
+    }
+
+    // Loop over each cmsghdr header and set data.
+    for (cm = CMSG_FIRSTHDR(&mhdr),i=0; (cm!=NULL); cm = CMSG_NXTHDR(&mhdr, cm),i++) {
+        // copy out cmsg_data
+        ScopedLocalRef<jbyteArray> msgData(env,
+            env->NewByteArray(cm->cmsg_len - sizeof(struct cmsghdr)));
+        env->SetByteArrayRegion(msgData.get(),
+                                0,
+                                env->GetArrayLength(msgData.get()),
+                                reinterpret_cast<jbyte*>CMSG_DATA(cm));
+
+        ScopedLocalRef<jobject> objItem(env, env->NewObject(
+                JniConstants::GetStructCmsghdrClass(env),
+                cmsgInitMid, cm->cmsg_level, cm->cmsg_type, msgData.get()));
+
+        env->SetObjectArrayElement(structCmsghdrArray, i, objItem.get());
+    }
+
+    env->SetObjectField(structMsghdr, msgControlFid, structCmsghdrArray);
+
+    return true;
+}
+
+/*
+ * generate ScopedBytes object per ByteBuffer.isDirect
+ * if ByteBuffer.isDirect, generate ScopedBytes object by ByteBuffer itself;
+ * else,  generate ScopedBytes object by ByteBuffer.array;
+ *
+ * Input:  ByteBuffer object, isRW(R only or RW)
+ * Output: byte_len, length of the byte data per ByteBuffer.remaining;
+ * return value: pointer of new ScopedBytesRW or ScopedBytesRO
+ */
+static void* getScopedBytesFromByteBuffer(JNIEnv* env,
+                                          jobject byteBuffer, int& byteLen, bool isRW) {
+
+    jclass byteBufferClass = JniConstants::GetByteBufferClass(env);
+    static jmethodID isDirectMid = env->GetMethodID(byteBufferClass, "isDirect", "()Z");
+    static jmethodID remainingMid = env->GetMethodID(byteBufferClass, "remaining", "()I");
+    static jmethodID arrayMid = env->GetMethodID(byteBufferClass, "array", "()[B");
+
+    if (!isDirectMid || !remainingMid || !arrayMid) {
+        return NULL;
+    }
+
+    byteLen = env->CallIntMethod(byteBuffer, remainingMid);
+    bool isDirect = env->CallBooleanMethod(byteBuffer, isDirectMid);
+    jobject objBuff;
+    if (isDirect == true) {
+        objBuff = env->NewLocalRef(byteBuffer); // Add LocalRef to align with CallObjectMethod
+    } else {
+        // return array
+        objBuff = env->CallObjectMethod(byteBuffer, arrayMid);
+    }
+
+    if (isRW) {
+        return (void*)(new ScopedBytesRW(env, objBuff));
+    } else {
+        return (void*)(new ScopedBytesRO(env, objBuff));
+    }
+
+}
+
+/*
+ *  Convert ByteBuffer[] to mhdr.msg_iov/msg_iovlen
+ */
+bool byteBufferArrayToIOV(JNIEnv* env, jobjectArray msgiovArray, struct msghdr& mhdr,
+                         ScopedByteBufferArray& scopeBufArray) {
+    int msgIovArraySize = env->GetArrayLength(msgiovArray);
+    if (!msgIovArraySize) {
+        /* would not happen since msg_iov is marked as NonNull */
+        mhdr.msg_iov = NULL;
+        mhdr.msg_iovlen = 0;
+    }
+
+    struct iovec* iovarr = (struct iovec*)malloc(sizeof(iovec)*msgIovArraySize);
+    if (!iovarr) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+        return false;
+    }
+
+    if (scopeBufArray.initArray(msgIovArraySize) == false) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+        return false;
+    }
+
+    // Set memory of each msg_iov item by the original bytes address.
+    for (int i=0; i<msgIovArraySize; i++)
+    {
+        jobject msgiovItem = env->GetObjectArrayElement(msgiovArray, i);
+        int byteLen = 0;
+        void* ptr = getScopedBytesFromByteBuffer(env, msgiovItem, byteLen, scopeBufArray.isRW());
+        if (!ptr) {
+            jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+            return false;
+        }
+
+        scopeBufArray.setArrayItem(i, ptr);
+
+        if (scopeBufArray.isRW()) {
+            iovarr[i].iov_base = (unsigned char*)(((ScopedBytesRW*)ptr)->get());
+        }
+        else {
+            iovarr[i].iov_base = (unsigned char*)(((ScopedBytesRO*)ptr)->get());
+        }
+
+        iovarr[i].iov_len  = byteLen;
+    }
+
+    mhdr.msg_iov = iovarr;
+    mhdr.msg_iovlen = msgIovArraySize;
+
+    return true;
+}
+
+/*
+ * Function: convertStructMsghdrAndmsghdr
+ * Description: convert between Java#StructMsghdr and C#msghdr for sendmsg/recvmsg
+ *
+ * Function Parameters:
+ *   StructMsghdr, input, StructMsghdr
+ *                 for sendmsg,
+ *                   StructMsghdr.msg_name       input(mandatory),
+ *                   StructMsghdr.msg_iov        iput(mandatory)
+ *                   StructMsghdr.msg_control    input(optional)
+ *                   StructMsghdr.msg_flags      input(mandatory)
+ *                 for recvmsg,
+ *                   StructMsghdr.msg_name       input/output(optional),
+ *                   StructMsghdr.msg_iov        input/output(mandatory)
+ *                   StructMsghdr.msg_control    input/output(optional)
+ *                   StructMsghdr.msg_flags      input
+ *   mhdr, input, struct msghdr
+ *   scopeBufArray, output, store buffer array of ScopedBytesRW or ScopedBytesRO
+ *   isFromStructCMsghdrTomsghdr, input,  indicate StructMsghdr->msghdr or msghdr->StructMsghdr
+ *
+ * then in sendmsg scenario, call sequence will be:
+ *             1. convert(StructMsg->msghdr)
+ *             2. sendmsg
+ *      in recvmsg scenario, call sequence will be:
+ *             1. convert(StructMsg->msghdr)
+ *             2. recvmsg
+ *             3. convert again(msghdr->StructMsg)
+ */
+bool convertStructMsghdrAndmsghdr(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                                  ScopedByteBufferArray& scopeBufArray,
+                                  bool isFromStructCMsghdrTomsghdr) {
+    if (!structMsghdr) {
+        jniThrowNullPointerException(env, "missing structMsghdr");
+        return false;
+    }
+
+    jclass StructMsghdrClass = JniConstants::GetStructMsghdrClass(env);
+
+    // Get fieldID of each item in StructMsghdr.
+    static jfieldID msgIovFid = env->GetFieldID(StructMsghdrClass,
+                                                 "msg_iov",
+                                                 "[Ljava/nio/ByteBuffer;");
+    if (!msgIovFid) {
+        return false;
+    }
+    static jfieldID msgControlFid = env->GetFieldID(StructMsghdrClass,
+                                                    "msg_control",
+                                                    "[Landroid/system/StructCmsghdr;");
+    if (!msgControlFid) {
+        return false;
+    }
+    static jfieldID msgFlagsFid = env->GetFieldID(StructMsghdrClass,
+                                                  "msg_flags",
+                                                  "I");
+    if (!msgFlagsFid) {
+        return false;
+    }
+
+    if (isFromStructCMsghdrTomsghdr) {
+        // Pick StructMsghdr.msg_iov[].
+        jobjectArray msgIovArray = reinterpret_cast<jobjectArray>(
+                                        env->GetObjectField(structMsghdr, msgIovFid));
+        if (!msgIovArray) {
+            jniThrowNullPointerException(env, "null StructMsghdr.msg_iov");
+            return false;
+        }
+        // In case sendmsg, IOV buffer are RO to send data,
+        // in case recvmsg, IOV buffer are RW to store received data.
+        if (byteBufferArrayToIOV(env, msgIovArray, mhdr, scopeBufArray) == false) {
+            return false;
+        }
+
+        if (!scopeBufArray.isRW()) {
+            jobjectArray structCmsghdrObjArray = reinterpret_cast<jobjectArray>(
+                                               env->GetObjectField(structMsghdr, msgControlFid));
+            if (structCmsghdrObjArray != NULL) {
+                // convert StrucCmsg[] <-> msghdr.msgcontrl
+                if (structCmsghdrArrayToMsgcontrol(env, structCmsghdrObjArray, mhdr) == false) {
+                    return false;
+                }
+            }
+        } else {
+            // hardcode 512 for recvmsg/msg_controllen, it should be enough for recvmsg
+            mhdr.msg_controllen = 512;
+            mhdr.msg_control = (unsigned char*)malloc(mhdr.msg_controllen);
+        }
+
+        mhdr.msg_flags = env->GetIntField(structMsghdr, msgFlagsFid);
+    } else {
+        // StructMsghdr.msg_iov[]/msg_control[] are output paramenter.
+        // StructMsghdr.msg_iov[] data are already updated by recvmsg syscall directly.
+        // StructMsghdr.msg_control[] are set below.
+        if (msgcontrolToStructCmsghdrArray(env, structMsghdr, mhdr) == false)
+            return false;
+        env->SetIntField(structMsghdr, msgFlagsFid, mhdr.msg_flags);
+    }
+
+    return true;
+
+}
+
+// Convert Java StructMsghdr to C msghdr.
+bool msghdrJavaToC(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                          ScopedByteBufferArray& scopedBufArray) {
+    return convertStructMsghdrAndmsghdr(env, structMsghdr, mhdr,
+                                             scopedBufArray, true);
+}
+
+// Convert C msghdr to Java StructMsghdr.
+bool msghdrCToJava(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                          ScopedByteBufferArray& scopedBufArray) {
+    return convertStructMsghdrAndmsghdr(env, structMsghdr, mhdr,
+                                             scopedBufArray, false);
+}
+
 bool setBlocking(int fd, bool blocking) {
     int flags = fcntl(fd, F_GETFL);
     if (flags == -1) {
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
old mode 100644
new mode 100755
index db1ad1f..da35036
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -16,6 +16,7 @@
 
 #include "jni.h"
 #include <sys/socket.h>
+#include "ScopedByteBufferArray.h"
 
 // Convert from sockaddr_storage to Inet4Address (AF_INET) or Inet6Address (AF_INET6).
 // If 'port' is non-NULL and the address family includes a notion
@@ -38,7 +39,15 @@
 bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port,
                                    sockaddr_storage& ss, socklen_t& sa_len);
 
+// Convert from StructMsghdr to msghdr,
+// set all fields except msg_name which would be set outside since IPv4 fallback handled outside
+bool msghdrJavaToC(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                   ScopedByteBufferArray& scopedBufArray);
 
+// Convert from msghdr to StructMsghdr,
+// msg_iov/msg_control need to be set since they are output parameters;
+bool msghdrCToJava(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                   ScopedByteBufferArray& scopedBufArray);
 
 // Changes 'fd' to be blocking/non-blocking. Returns false and sets errno on failure.
 // @Deprecated - use IoUtils.setBlocking
diff --git a/luni/src/main/native/Portability.h b/luni/src/main/native/Portability.h
index 4df3353..887a47b 100644
--- a/luni/src/main/native/Portability.h
+++ b/luni/src/main/native/Portability.h
@@ -31,6 +31,17 @@
 }
 #endif
 
+#if __has_include(<linux/vm_sockets.h>)
+#include <linux/vm_sockets.h>
+#else  // __has_include(<linux/vm_sockets.h>)
+// the platform does not support virtio-vsock
+#define AF_VSOCK (-1)
+#define VMADDR_PORT_ANY (-1)
+#define VMADDR_CID_ANY (-1)
+#define VMADDR_CID_LOCAL (-1)
+#define VMADDR_CID_HOST (-1)
+#endif  // __has_include(<linux/vm_sockets.h>)
+
 #if defined(__GLIBC__) && !defined(__LP64__)
 
 #include <unistd.h>
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 17ca839..91cabf7 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -19,7 +19,7 @@
 #include <stdlib.h>
 
 #include <log/log.h>
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <nativehelper/ScopedLocalFrame.h>
 
 #include "JniConstants.h"
@@ -39,14 +39,12 @@
     //    REGISTER(register_java_lang_StringToReal);
     REGISTER(register_java_lang_invoke_MethodHandle);
     REGISTER(register_java_lang_invoke_VarHandle);
-    REGISTER(register_java_math_NativeBN);
     REGISTER(register_libcore_icu_ICU);
-    REGISTER(register_libcore_icu_TimeZoneNames);
     REGISTER(register_libcore_io_AsynchronousCloseMonitor);
     REGISTER(register_libcore_io_Linux);
     REGISTER(register_libcore_io_Memory);
+    REGISTER(register_libcore_math_NativeBN);
     REGISTER(register_libcore_util_NativeAllocationRegistry);
-    REGISTER(register_org_apache_harmony_dalvik_NativeTestTarget);
     REGISTER(register_org_apache_harmony_xml_ExpatParser);
     REGISTER(register_sun_misc_Unsafe);
 #undef REGISTER
diff --git a/luni/src/main/native/ScopedByteBufferArray.h b/luni/src/main/native/ScopedByteBufferArray.h
new file mode 100755
index 0000000..d5dd6ba
--- /dev/null
+++ b/luni/src/main/native/ScopedByteBufferArray.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <nativehelper/ScopedLocalRef.h>
+#include "ScopedBytes.h"
+
+/**
+ * ScopedByteBufferArray manages the dynamic buffer array of ScopedBytesRW/ScopedBytesRO.
+ */
+class ScopedByteBufferArray {
+public:
+    ScopedByteBufferArray(JNIEnv* env, int isRW)
+    : mEnv(env), mIsRW(isRW)
+    {
+        mArrayPtr = NULL;
+        mArraySize = 0;
+    }
+
+    ~ScopedByteBufferArray() {
+        if(!mArrayPtr) {
+            return;
+        }
+
+        // Loop over arrary and release memory.
+        for (int i = 0; i < mArraySize; ++i) {
+            if (!mArrayPtr[i])
+                continue;
+
+            if (mIsRW) {
+                jobject tmp = ((ScopedBytesRW*)mArrayPtr[i])->getObject();
+                delete (ScopedBytesRW*)mArrayPtr[i];
+                mEnv->DeleteLocalRef(tmp);
+            } else {
+                jobject tmp = ((ScopedBytesRO*)mArrayPtr[i])->getObject();
+                delete (ScopedBytesRO*)mArrayPtr[i];
+                mEnv->DeleteLocalRef(tmp);
+            }
+        }
+        delete[] mArrayPtr;
+    }
+
+    bool initArray(int size) {
+        if (mArrayPtr) {
+            return false;
+        }
+
+        mArraySize = size;
+
+        if (mIsRW) {
+            mArrayPtr = (void**)(new ScopedBytesRW*[size]);
+        }
+        else {
+            mArrayPtr = (void**)(new ScopedBytesRO*[size]);
+        }
+
+        if (!mArrayPtr) {
+            return false;
+        }
+
+        for (int i=0; i<size; ++i) {
+            mArrayPtr[i] = 0;
+        }
+
+        return true;
+    }
+
+    bool isRW() const {
+        return mIsRW;
+    }
+
+    bool setArrayItem(int itemNo, void* item) {
+        if (itemNo >= mArraySize || itemNo < 0) {
+            return false;
+        }
+        mArrayPtr[itemNo] = item;
+        return true;
+    }
+
+private:
+    JNIEnv* const mEnv;
+    int mIsRW;
+    int mArraySize;
+    void** mArrayPtr;
+};
diff --git a/luni/src/main/native/ScopedBytes.h b/luni/src/main/native/ScopedBytes.h
new file mode 100644
index 0000000..951ef4b
--- /dev/null
+++ b/luni/src/main/native/ScopedBytes.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#pragma once
+
+#include <nativehelper/JNIHelp.h>
+
+#include "JniConstants.h"
+
+/**
+ * ScopedBytesRO and ScopedBytesRW attempt to paper over the differences between byte[]s and
+ * ByteBuffers. This in turn helps paper over the differences between non-direct ByteBuffers backed
+ * by byte[]s, direct ByteBuffers backed by bytes[]s, and direct ByteBuffers not backed by byte[]s.
+ * (On Android, this last group only contains MappedByteBuffers.)
+ */
+template<bool readOnly>
+class ScopedBytes {
+public:
+    ScopedBytes(JNIEnv* env, jobject object)
+    : mEnv(env), mObject(object), mByteArray(nullptr), mPtr(nullptr)
+    {
+        if (mObject == nullptr) {
+            jniThrowNullPointerException(mEnv);
+        } else {
+            jclass byteArrayClass = JniConstants::GetPrimitiveByteArrayClass(env);
+            if (mEnv->IsInstanceOf(mObject, byteArrayClass)) {
+                mByteArray = reinterpret_cast<jbyteArray>(mObject);
+                mPtr = mEnv->GetByteArrayElements(mByteArray, nullptr);
+            } else {
+                mPtr = reinterpret_cast<jbyte*>(mEnv->GetDirectBufferAddress(mObject));
+            }
+        }
+    }
+
+    ~ScopedBytes() {
+        if (mByteArray != nullptr) {
+            mEnv->ReleaseByteArrayElements(mByteArray, mPtr, readOnly ? JNI_ABORT : 0);
+        }
+    }
+
+    jobject getObject() {
+        return mObject;
+    }
+
+private:
+    JNIEnv* const mEnv;
+    const jobject mObject;
+    jbyteArray mByteArray;
+
+protected:
+    jbyte* mPtr;
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(ScopedBytes);
+};
+
+class ScopedBytesRO : public ScopedBytes<true> {
+public:
+    ScopedBytesRO(JNIEnv* env, jobject object) : ScopedBytes<true>(env, object) {}
+    const jbyte* get() const {
+        return mPtr;
+    }
+};
+
+class ScopedBytesRW : public ScopedBytes<false> {
+public:
+    ScopedBytesRW(JNIEnv* env, jobject object) : ScopedBytes<false>(env, object) {}
+    jbyte* get() {
+        return mPtr;
+    }
+};
+
diff --git a/luni/src/main/native/ScopedIcuLocale.h b/luni/src/main/native/ScopedIcuLocale.h
deleted file mode 100644
index 851de76..0000000
--- a/luni/src/main/native/ScopedIcuLocale.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#ifndef SCOPED_ICU_LOCALE_H_included
-#define SCOPED_ICU_LOCALE_H_included
-
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include "unicode/locid.h"
-
-class ScopedIcuLocale {
- public:
-  ScopedIcuLocale(JNIEnv* env, jstring javaLocaleName) : mEnv(env) {
-    mLocale.setToBogus();
-
-    if (javaLocaleName == NULL) {
-      jniThrowNullPointerException(mEnv, "javaLocaleName == null");
-      return;
-    }
-
-    const ScopedUtfChars localeName(env, javaLocaleName);
-    if (localeName.c_str() == NULL) {
-      return;
-    }
-
-    mLocale = icu::Locale::createFromName(localeName.c_str());
-  }
-
-  ~ScopedIcuLocale() {
-  }
-
-  bool valid() const {
-    return !mLocale.isBogus();
-  }
-
-  icu::Locale& locale() {
-    return mLocale;
-  }
-
- private:
-  JNIEnv* const mEnv;
-  icu::Locale mLocale;
-
-  // Disallow copy and assignment.
-  ScopedIcuLocale(const ScopedIcuLocale&);
-  void operator=(const ScopedIcuLocale&);
-};
-
-#endif  // SCOPED_ICU_LOCALE_H_included
diff --git a/luni/src/main/native/ScopedIcuULoc.h b/luni/src/main/native/ScopedIcuULoc.h
index 5de5e36..5f7ca0a 100644
--- a/luni/src/main/native/ScopedIcuULoc.h
+++ b/luni/src/main/native/ScopedIcuULoc.h
@@ -19,7 +19,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
 
-#include "unicode/uloc.h"
+#include <unicode/uloc.h>
 
 
 static void getLocale(const char* localeName, std::string& locale, UErrorCode* status) {
diff --git a/luni/src/main/native/ScopedJavaUnicodeString.h b/luni/src/main/native/ScopedJavaUnicodeString.h
deleted file mode 100644
index a30aa7e..0000000
--- a/luni/src/main/native/ScopedJavaUnicodeString.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef SCOPED_JAVA_UNICODE_STRING_H_included
-#define SCOPED_JAVA_UNICODE_STRING_H_included
-
-#include <nativehelper/JNIHelp.h>
-#include "unicode/unistr.h"
-
-// A smart pointer that provides access to an ICU UnicodeString given a JNI
-// jstring. We give ICU a direct pointer to the characters on the Java heap.
-// It's clever enough to copy-on-write if necessary.
-class ScopedJavaUnicodeString {
- public:
-  ScopedJavaUnicodeString(JNIEnv* env, jstring s) : mEnv(env), mString(s) {
-    if (s == NULL) {
-      jniThrowNullPointerException(mEnv, NULL);
-    } else {
-      mChars = env->GetStringChars(mString, NULL);
-      const int32_t charCount = env->GetStringLength(mString);
-      mUnicodeString.setTo(false, mChars, charCount);
-    }
-  }
-
-  ~ScopedJavaUnicodeString() {
-    if (mString != NULL) {
-      mEnv->ReleaseStringChars(mString, mChars);
-    }
-  }
-
-  bool valid() const {
-    return (mString != NULL);
-  }
-
-  const icu::UnicodeString& unicodeString() const {
-    return mUnicodeString;
-  }
-
-  icu::UnicodeString& unicodeString() {
-    return mUnicodeString;
-  }
-
- private:
-  JNIEnv* mEnv;
-  jstring mString;
-  const jchar* mChars;
-  icu::UnicodeString mUnicodeString;
-
-  // Disallow copy and assignment.
-  ScopedJavaUnicodeString(const ScopedJavaUnicodeString&);
-  void operator=(const ScopedJavaUnicodeString&);
-};
-
-#endif  // SCOPED_JAVA_UNICODE_STRING_H_included
diff --git a/luni/src/main/native/ScopedMsghdr.h b/luni/src/main/native/ScopedMsghdr.h
new file mode 100755
index 0000000..1361390
--- /dev/null
+++ b/luni/src/main/native/ScopedMsghdr.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <nativehelper/JNIHelp.h>
+
+#include "JniConstants.h"
+#include "ScopedBytes.h"
+
+/**
+ * Wrapper for managing the name and length of msghdr.
+ */
+class ScopedMsghdr {
+public:
+    ScopedMsghdr() {
+    }
+
+    ~ScopedMsghdr() {
+        if (mMsghdrValue.msg_iov)
+            free(mMsghdrValue.msg_iov);
+        if (mMsghdrValue.msg_control)
+            free(mMsghdrValue.msg_control);
+    }
+
+    struct msghdr& getObject() {
+        return mMsghdrValue;
+    }
+
+    void setMsgNameAndLen(sockaddr* ss, socklen_t sa_len) {
+        mMsghdrValue.msg_name = ss;
+        mMsghdrValue.msg_namelen = sa_len;
+    }
+
+    bool isNameLenValid() const {
+        if(mMsghdrValue.msg_name == NULL) {
+            return false;
+        }
+        if ((mMsghdrValue.msg_namelen != sizeof(sockaddr_in6)) &&
+            (mMsghdrValue.msg_namelen != sizeof(sockaddr_in))) {
+            return false;
+        }
+        return true;
+    }
+
+private:
+    struct msghdr mMsghdrValue = {};
+
+};
+
+
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 6cf2c8b..50e3c10 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -69,6 +69,7 @@
     initConstant(env, c, "AF_PACKET", AF_PACKET);
     initConstant(env, c, "AF_NETLINK", AF_NETLINK);
     initConstant(env, c, "AF_UNIX", AF_UNIX);
+    initConstant(env, c, "AF_VSOCK", AF_VSOCK);
     initConstant(env, c, "AF_UNSPEC", AF_UNSPEC);
     initConstant(env, c, "AI_ADDRCONFIG", AI_ADDRCONFIG);
     initConstant(env, c, "AI_ALL", AI_ALL);
@@ -80,6 +81,10 @@
     initConstant(env, c, "AI_PASSIVE", AI_PASSIVE);
     initConstant(env, c, "AI_V4MAPPED", AI_V4MAPPED);
     initConstant(env, c, "ARPHRD_ETHER", ARPHRD_ETHER);
+    initConstant(env, c, "VMADDR_PORT_ANY", VMADDR_PORT_ANY);
+    initConstant(env, c, "VMADDR_CID_ANY", VMADDR_CID_ANY);
+    initConstant(env, c, "VMADDR_CID_LOCAL", VMADDR_CID_LOCAL);
+    initConstant(env, c, "VMADDR_CID_HOST", VMADDR_CID_HOST);
     initConstant(env, c, "ARPHRD_LOOPBACK", ARPHRD_LOOPBACK);
 #if defined(CAP_LAST_CAP)
     initConstant(env, c, "CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL);
@@ -522,6 +527,9 @@
     initConstant(env, c, "SOCK_SEQPACKET", SOCK_SEQPACKET);
     initConstant(env, c, "SOCK_STREAM", SOCK_STREAM);
     initConstant(env, c, "SOL_SOCKET", SOL_SOCKET);
+#if defined(SOL_UDP)
+    initConstant(env, c, "SOL_UDP", SOL_UDP);
+#endif
 #if defined(SO_BINDTODEVICE)
     initConstant(env, c, "SO_BINDTODEVICE", SO_BINDTODEVICE);
 #endif
@@ -598,6 +606,12 @@
     initConstant(env, c, "UDP_ENCAP", UDP_ENCAP);
     initConstant(env, c, "UDP_ENCAP_ESPINUDP_NON_IKE", UDP_ENCAP_ESPINUDP_NON_IKE);
     initConstant(env, c, "UDP_ENCAP_ESPINUDP", UDP_ENCAP_ESPINUDP);
+#if defined(UDP_GRO)
+    initConstant(env, c, "UDP_GRO", UDP_GRO);
+#endif
+#if defined(UDP_SEGMENT)
+    initConstant(env, c, "UDP_SEGMENT", UDP_SEGMENT);
+#endif
     // UNIX_PATH_MAX is mentioned in some versions of unix(7), but not actually declared.
     initConstant(env, c, "UNIX_PATH_MAX", sizeof(sockaddr_un::sun_path));
     initConstant(env, c, "WCONTINUED", WCONTINUED);
diff --git a/luni/src/main/native/java_math_NativeBN.cpp b/luni/src/main/native/java_math_NativeBN.cpp
deleted file mode 100644
index 5d085ec..0000000
--- a/luni/src/main/native/java_math_NativeBN.cpp
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "NativeBN"
-
-#include <stdio.h>
-#include <algorithm>
-#include <memory>
-
-#include <openssl/bn.h>
-#include <openssl/crypto.h>
-#include <openssl/err.h>
-
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativehelper/jni_macros.h>
-
-#include "JniException.h"
-
-struct BN_CTX_Deleter {
-  void operator()(BN_CTX* p) const {
-    BN_CTX_free(p);
-  }
-};
-typedef std::unique_ptr<BN_CTX, BN_CTX_Deleter> Unique_BN_CTX;
-
-static BIGNUM* toBigNum(jlong address) {
-  return reinterpret_cast<BIGNUM*>(static_cast<uintptr_t>(address));
-}
-
-static void throwException(JNIEnv* env) {
-  long error = ERR_get_error();
-  // OpenSSL's error queue may contain multiple errors. Clean up after them.
-  ERR_clear_error();
-
-  if (error == 0) {
-    // An operation failed but did not push to the error queue. Throw a default
-    // exception.
-    jniThrowException(env, "java/lang/ArithmeticException", "Operation failed");
-    return;
-  }
-
-  char message[256];
-  ERR_error_string_n(error, message, sizeof(message));
-  int reason = ERR_GET_REASON(error);
-  if (reason == BN_R_DIV_BY_ZERO) {
-    jniThrowException(env, "java/lang/ArithmeticException", "BigInteger division by zero");
-  } else if (reason == BN_R_NO_INVERSE) {
-    jniThrowException(env, "java/lang/ArithmeticException", "BigInteger not invertible");
-  } else if (reason == ERR_R_MALLOC_FAILURE) {
-    jniThrowOutOfMemoryError(env, message);
-  } else {
-    jniThrowException(env, "java/lang/ArithmeticException", message);
-  }
-}
-
-static int isValidHandle(JNIEnv* env, jlong handle, const char* message) {
-  if (handle == 0) {
-    jniThrowNullPointerException(env, message);
-    return JNI_FALSE;
-  }
-  return JNI_TRUE;
-}
-
-static int oneValidHandle(JNIEnv* env, jlong a) {
-  return isValidHandle(env, a, "Mandatory handle (first) passed as null");
-}
-
-static int twoValidHandles(JNIEnv* env, jlong a, jlong b) {
-  if (!oneValidHandle(env, a)) return JNI_FALSE;
-  return isValidHandle(env, b, "Mandatory handle (second) passed as null");
-}
-
-static int threeValidHandles(JNIEnv* env, jlong a, jlong b, jlong c) {
-  if (!twoValidHandles(env, a, b)) return JNI_FALSE;
-  return isValidHandle(env, c, "Mandatory handle (third) passed as null");
-}
-
-static int fourValidHandles(JNIEnv* env, jlong a, jlong b, jlong c, jlong d) {
-  if (!threeValidHandles(env, a, b, c)) return JNI_FALSE;
-  return isValidHandle(env, d, "Mandatory handle (fourth) passed as null");
-}
-
-static jlong NativeBN_BN_new(JNIEnv* env, jclass) {
-  jlong result = static_cast<jlong>(reinterpret_cast<uintptr_t>(BN_new()));
-  if (!result) {
-    throwException(env);
-  }
-  return result;
-}
-
-static jlong NativeBN_getNativeFinalizer(JNIEnv*, jclass) {
-  return static_cast<jlong>(reinterpret_cast<uintptr_t>(&BN_free));
-}
-
-static void NativeBN_BN_free(JNIEnv* env, jclass, jlong a) {
-  if (!oneValidHandle(env, a)) return;
-  BN_free(toBigNum(a));
-}
-
-static int NativeBN_BN_cmp(JNIEnv* env, jclass, jlong a, jlong b) {
-  if (!twoValidHandles(env, a, b)) return 1;
-  return BN_cmp(toBigNum(a), toBigNum(b));
-}
-
-static void NativeBN_BN_copy(JNIEnv* env, jclass, jlong to, jlong from) {
-  if (!twoValidHandles(env, to, from)) return;
-  if (!BN_copy(toBigNum(to), toBigNum(from))) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_putULongInt(JNIEnv* env, jclass, jlong a0, jlong java_dw, jboolean neg) {
-  if (!oneValidHandle(env, a0)) return;
-
-  uint64_t dw = java_dw;
-  BIGNUM* a = toBigNum(a0);
-
-  if (!BN_set_u64(a, dw)) {
-    throwException(env);
-    return;
-  }
-
-  BN_set_negative(a, neg);
-}
-
-static void NativeBN_putLongInt(JNIEnv* env, jclass cls, jlong a, jlong dw) {
-  if (dw >= 0) {
-    NativeBN_putULongInt(env, cls, a, dw, JNI_FALSE);
-  } else {
-    NativeBN_putULongInt(env, cls, a, -dw, JNI_TRUE);
-  }
-}
-
-static int NativeBN_BN_dec2bn(JNIEnv* env, jclass, jlong a0, jstring str) {
-  if (!oneValidHandle(env, a0)) return -1;
-  ScopedUtfChars chars(env, str);
-  if (chars.c_str() == NULL) {
-    return -1;
-  }
-  BIGNUM* a = toBigNum(a0);
-  int result = BN_dec2bn(&a, chars.c_str());
-  if (result == 0) {
-    throwException(env);
-  }
-  return result;
-}
-
-static int NativeBN_BN_hex2bn(JNIEnv* env, jclass, jlong a0, jstring str) {
-  if (!oneValidHandle(env, a0)) return -1;
-  ScopedUtfChars chars(env, str);
-  if (chars.c_str() == NULL) {
-    return -1;
-  }
-  BIGNUM* a = toBigNum(a0);
-  int result = BN_hex2bn(&a, chars.c_str());
-  if (result == 0) {
-    throwException(env);
-  }
-  return result;
-}
-
-static void NativeBN_BN_bin2bn(JNIEnv* env, jclass, jbyteArray arr, int len, jboolean neg, jlong ret) {
-  if (!oneValidHandle(env, ret)) return;
-  ScopedByteArrayRO bytes(env, arr);
-  if (bytes.get() == NULL) {
-    return;
-  }
-  if (!BN_bin2bn(reinterpret_cast<const unsigned char*>(bytes.get()), len, toBigNum(ret))) {
-    throwException(env);
-    return;
-  }
-
-  BN_set_negative(toBigNum(ret), neg);
-}
-
-static void NativeBN_litEndInts2bn(JNIEnv* env, jclass, jintArray arr, int len, jboolean neg, jlong ret0) {
-  if (!oneValidHandle(env, ret0)) return;
-  BIGNUM* ret = toBigNum(ret0);
-
-  ScopedIntArrayRO scopedArray(env, arr);
-
-  if (scopedArray.get() == NULL) {
-    return;
-  }
-
-  // We can simply interpret the little-endian integer stream as a
-  // little-endian byte stream and use BN_le2bn.
-  const uint8_t* tmpBytes = reinterpret_cast<const uint8_t *>(scopedArray.get());
-  size_t numBytes = len * sizeof(int);
-
-  if (!BN_le2bn(tmpBytes, numBytes, ret)) {
-    throwException(env);
-  }
-
-  BN_set_negative(ret, neg);
-}
-
-static void NativeBN_twosComp2bn(JNIEnv* env, jclass, jbyteArray arr, int bytesLen, jlong ret0) {
-  if (!oneValidHandle(env, ret0)) return;
-  BIGNUM* ret = toBigNum(ret0);
-
-  ScopedByteArrayRO bytes(env, arr);
-  if (bytes.get() == NULL) {
-    return;
-  }
-
-  if (bytesLen == 0) {
-    BN_zero(ret);
-    return;
-  }
-
-  const unsigned char* bytesTmp = reinterpret_cast<const unsigned char*>(bytes.get());
-
-  if (!BN_bin2bn(bytesTmp, bytesLen, ret)) {
-    throwException(env);
-    return;
-  }
-
-  // Use the high bit to determine the sign in twos-complement.
-  BN_set_negative(ret, (bytes[0] & 0x80) != 0);
-
-  if (BN_is_negative(ret)) {
-    // For negative values, BN_bin2bn doesn't interpret the twos-complement
-    // representation, so ret is now (- value - 2^N). We can use nnmod_pow2 to set
-    // ret to (-value).
-    if (!BN_nnmod_pow2(ret, ret, bytesLen * 8)) {
-      throwException(env);
-      return;
-    }
-
-    // And now we correct the sign.
-    BN_set_negative(ret, 1);
-  }
-}
-
-static jlong NativeBN_longInt(JNIEnv* env, jclass, jlong a0) {
-  if (!oneValidHandle(env, a0)) return -1;
-  BIGNUM* a = toBigNum(a0);
-  uint64_t word;
-
-  if (BN_get_u64(a, &word)) {
-    return BN_is_negative(a) ? -((jlong) word) : word;
-  } else {
-    // This should be unreachable if our caller checks BigInt::twosCompFitsIntoBytes(8)
-    throwException(env);
-    return 0;
-  }
-}
-
-static char* leadingZerosTrimmed(char* s) {
-    char* p = s;
-    if (*p == '-') {
-        p++;
-        while ((*p == '0') && (*(p + 1) != 0)) { p++; }
-        p--;
-        *p = '-';
-    } else {
-        while ((*p == '0') && (*(p + 1) != 0)) { p++; }
-    }
-    return p;
-}
-
-static jstring NativeBN_BN_bn2dec(JNIEnv* env, jclass, jlong a) {
-  if (!oneValidHandle(env, a)) return NULL;
-  char* tmpStr = BN_bn2dec(toBigNum(a));
-  if (tmpStr == NULL) {
-    throwException(env);
-    return NULL;
-  }
-  char* retStr = leadingZerosTrimmed(tmpStr);
-  jstring returnJString = env->NewStringUTF(retStr);
-  OPENSSL_free(tmpStr);
-  return returnJString;
-}
-
-static jstring NativeBN_BN_bn2hex(JNIEnv* env, jclass, jlong a) {
-  if (!oneValidHandle(env, a)) return NULL;
-  char* tmpStr = BN_bn2hex(toBigNum(a));
-  if (tmpStr == NULL) {
-    throwException(env);
-    return NULL;
-  }
-  char* retStr = leadingZerosTrimmed(tmpStr);
-  jstring returnJString = env->NewStringUTF(retStr);
-  OPENSSL_free(tmpStr);
-  return returnJString;
-}
-
-static jbyteArray NativeBN_BN_bn2bin(JNIEnv* env, jclass, jlong a0) {
-  if (!oneValidHandle(env, a0)) return NULL;
-  BIGNUM* a = toBigNum(a0);
-  jbyteArray result = env->NewByteArray(BN_num_bytes(a));
-  if (result == NULL) {
-    return NULL;
-  }
-  ScopedByteArrayRW bytes(env, result);
-  if (bytes.get() == NULL) {
-    return NULL;
-  }
-  BN_bn2bin(a, reinterpret_cast<unsigned char*>(bytes.get()));
-  return result;
-}
-
-static jintArray NativeBN_bn2litEndInts(JNIEnv* env, jclass, jlong a0) {
-  if (!oneValidHandle(env, a0)) return NULL;
-
-  BIGNUM* a = toBigNum(a0);
-
-  // The number of integers we need is BN_num_bytes(a) / sizeof(int), rounded up
-  int intLen = (BN_num_bytes(a) + sizeof(int) - 1) / sizeof(int);
-
-  // Allocate our result with the JNI boilerplate
-  jintArray result = env->NewIntArray(intLen);
-
-  if (result == NULL) {
-    throwException(env);
-    return NULL;
-  }
-
-  ScopedIntArrayRW ints(env, result);
-
-  unsigned int* uints = reinterpret_cast<unsigned int*>(ints.get());
-  if (uints == NULL) {
-    throwException(env);
-    return NULL;
-  }
-
-  // We can simply interpret a little-endian byte stream as a little-endian integer stream.
-  if (!BN_bn2le_padded(reinterpret_cast<uint8_t*>(uints), intLen * sizeof(int), a)) {
-    throwException(env);
-    return NULL;
-  }
-
-  return result;
-}
-
-static int NativeBN_sign(JNIEnv* env, jclass, jlong a) {
-  if (!oneValidHandle(env, a)) return -2;
-  if (BN_is_zero(toBigNum(a))) {
-      return 0;
-  } else if (BN_is_negative(toBigNum(a))) {
-    return -1;
-  }
-  return 1;
-}
-
-static void NativeBN_BN_set_negative(JNIEnv* env, jclass, jlong b, int n) {
-  if (!oneValidHandle(env, b)) return;
-  BN_set_negative(toBigNum(b), n);
-}
-
-static int NativeBN_bitLength(JNIEnv* env, jclass, jlong a0) {
-  if (!oneValidHandle(env, a0)) return JNI_FALSE;
-  BIGNUM* a = toBigNum(a0);
-
-  // If a is not negative, we can use BN_num_bits directly.
-  if (!BN_is_negative(a)) {
-    return BN_num_bits(a);
-  }
-
-  // In the negative case, the number of bits in a is the same as the number of bits in |a|,
-  // except one less when |a| is a power of two.
-  BIGNUM positiveA;
-  BN_init(&positiveA);
-
-  if (!BN_copy(&positiveA, a)) {
-    BN_free(&positiveA);
-    throwException(env);
-    return -1;
-  }
-
-  BN_set_negative(&positiveA, false);
-  int numBits = BN_is_pow2(&positiveA) ? BN_num_bits(&positiveA) - 1 : BN_num_bits(&positiveA);
-
-  BN_free(&positiveA);
-  return numBits;
-}
-
-static jboolean NativeBN_BN_is_bit_set(JNIEnv* env, jclass, jlong a, int n) {
-  if (!oneValidHandle(env, a)) return JNI_FALSE;
-
-  // NOTE: this is only called in the positive case, so BN_is_bit_set is fine here.
-  return BN_is_bit_set(toBigNum(a), n) ? JNI_TRUE : JNI_FALSE;
-}
-
-static void NativeBN_BN_shift(JNIEnv* env, jclass, jlong r, jlong a, int n) {
-  if (!twoValidHandles(env, r, a)) return;
-  int ok;
-  if (n >= 0) {
-    ok = BN_lshift(toBigNum(r), toBigNum(a), n);
-  } else {
-    ok = BN_rshift(toBigNum(r), toBigNum(a), -n);
-  }
-  if (!ok) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_add_word(JNIEnv* env, jclass, jlong a, jint w) {
-  if (!oneValidHandle(env, a)) return;
-  if (!BN_add_word(toBigNum(a), w)) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_mul_word(JNIEnv* env, jclass, jlong a, jint w) {
-  if (!oneValidHandle(env, a)) return;
-  if (!BN_mul_word(toBigNum(a), w)) {
-    throwException(env);
-  }
-}
-
-static jint NativeBN_BN_mod_word(JNIEnv* env, jclass, jlong a, jint w) {
-  if (!oneValidHandle(env, a)) return 0;
-  BN_ULONG result = BN_mod_word(toBigNum(a), w);
-  if (result == (BN_ULONG)-1) {
-    throwException(env);
-  }
-  return result;
-}
-
-static void NativeBN_BN_add(JNIEnv* env, jclass, jlong r, jlong a, jlong b) {
-  if (!threeValidHandles(env, r, a, b)) return;
-  if (!BN_add(toBigNum(r), toBigNum(a), toBigNum(b))) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_sub(JNIEnv* env, jclass, jlong r, jlong a, jlong b) {
-  if (!threeValidHandles(env, r, a, b)) return;
-  if (!BN_sub(toBigNum(r), toBigNum(a), toBigNum(b))) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_gcd(JNIEnv* env, jclass, jlong r, jlong a, jlong b) {
-  if (!threeValidHandles(env, r, a, b)) return;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  if (!BN_gcd(toBigNum(r), toBigNum(a), toBigNum(b), ctx.get())) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_mul(JNIEnv* env, jclass, jlong r, jlong a, jlong b) {
-  if (!threeValidHandles(env, r, a, b)) return;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  if (!BN_mul(toBigNum(r), toBigNum(a), toBigNum(b), ctx.get())) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_exp(JNIEnv* env, jclass, jlong r, jlong a, jlong p) {
-  if (!threeValidHandles(env, r, a, p)) return;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  if (!BN_exp(toBigNum(r), toBigNum(a), toBigNum(p), ctx.get())) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_div(JNIEnv* env, jclass, jlong dv, jlong rem, jlong m, jlong d) {
-  if (!fourValidHandles(env, (rem ? rem : dv), (dv ? dv : rem), m, d)) return;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  if (!BN_div(toBigNum(dv), toBigNum(rem), toBigNum(m), toBigNum(d), ctx.get())) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_nnmod(JNIEnv* env, jclass, jlong r, jlong a, jlong m) {
-  if (!threeValidHandles(env, r, a, m)) return;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  if (!BN_nnmod(toBigNum(r), toBigNum(a), toBigNum(m), ctx.get())) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_mod_exp(JNIEnv* env, jclass, jlong r, jlong a, jlong p, jlong m) {
-  if (!fourValidHandles(env, r, a, p, m)) return;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  if (!BN_mod_exp(toBigNum(r), toBigNum(a), toBigNum(p), toBigNum(m), ctx.get())) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_mod_inverse(JNIEnv* env, jclass, jlong ret, jlong a, jlong n) {
-  if (!threeValidHandles(env, ret, a, n)) return;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  if (!BN_mod_inverse(toBigNum(ret), toBigNum(a), toBigNum(n), ctx.get())) {
-    throwException(env);
-  }
-}
-
-static void NativeBN_BN_generate_prime_ex(JNIEnv* env, jclass, jlong ret, int bits,
-                                          jboolean safe, jlong add, jlong rem) {
-  if (!oneValidHandle(env, ret)) return;
-  if (!BN_generate_prime_ex(toBigNum(ret), bits, safe, toBigNum(add), toBigNum(rem),
-                            NULL)) {
-    throwException(env);
-  }
-}
-
-static jboolean NativeBN_BN_primality_test(JNIEnv* env, jclass, jlong candidate, int checks,
-                                           jboolean do_trial_decryption) {
-  if (!oneValidHandle(env, candidate)) return JNI_FALSE;
-  Unique_BN_CTX ctx(BN_CTX_new());
-  int is_probably_prime;
-  if (!BN_primality_test(&is_probably_prime, toBigNum(candidate), checks, ctx.get(),
-                         do_trial_decryption, NULL)) {
-    throwException(env);
-    return JNI_FALSE;
-  }
-  return is_probably_prime ? JNI_TRUE : JNI_FALSE;
-}
-
-static JNINativeMethod gMethods[] = {
-   NATIVE_METHOD(NativeBN, BN_add, "(JJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_add_word, "(JI)V"),
-   NATIVE_METHOD(NativeBN, BN_bin2bn, "([BIZJ)V"),
-   NATIVE_METHOD(NativeBN, BN_bn2bin, "(J)[B"),
-   NATIVE_METHOD(NativeBN, BN_bn2dec, "(J)Ljava/lang/String;"),
-   NATIVE_METHOD(NativeBN, BN_bn2hex, "(J)Ljava/lang/String;"),
-   NATIVE_METHOD(NativeBN, BN_cmp, "(JJ)I"),
-   NATIVE_METHOD(NativeBN, BN_copy, "(JJ)V"),
-   NATIVE_METHOD(NativeBN, BN_dec2bn, "(JLjava/lang/String;)I"),
-   NATIVE_METHOD(NativeBN, BN_div, "(JJJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_exp, "(JJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_free, "(J)V"),
-   NATIVE_METHOD(NativeBN, BN_gcd, "(JJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_generate_prime_ex, "(JIZJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_hex2bn, "(JLjava/lang/String;)I"),
-   NATIVE_METHOD(NativeBN, BN_is_bit_set, "(JI)Z"),
-   NATIVE_METHOD(NativeBN, BN_primality_test, "(JIZ)Z"),
-   NATIVE_METHOD(NativeBN, BN_mod_exp, "(JJJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_mod_inverse, "(JJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_mod_word, "(JI)I"),
-   NATIVE_METHOD(NativeBN, BN_mul, "(JJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_mul_word, "(JI)V"),
-   NATIVE_METHOD(NativeBN, BN_new, "()J"),
-   NATIVE_METHOD(NativeBN, BN_nnmod, "(JJJ)V"),
-   NATIVE_METHOD(NativeBN, BN_set_negative, "(JI)V"),
-   NATIVE_METHOD(NativeBN, BN_shift, "(JJI)V"),
-   NATIVE_METHOD(NativeBN, BN_sub, "(JJJ)V"),
-   NATIVE_METHOD(NativeBN, bitLength, "(J)I"),
-   NATIVE_METHOD(NativeBN, bn2litEndInts, "(J)[I"),
-   NATIVE_METHOD(NativeBN, getNativeFinalizer, "()J"),
-   NATIVE_METHOD(NativeBN, litEndInts2bn, "([IIZJ)V"),
-   NATIVE_METHOD(NativeBN, longInt, "(J)J"),
-   NATIVE_METHOD(NativeBN, putLongInt, "(JJ)V"),
-   NATIVE_METHOD(NativeBN, putULongInt, "(JJZ)V"),
-   NATIVE_METHOD(NativeBN, sign, "(J)I"),
-   NATIVE_METHOD(NativeBN, twosComp2bn, "([BIJ)V"),
-};
-void register_java_math_NativeBN(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "java/math/NativeBN", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 9879761..f9401d1e 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -17,83 +17,26 @@
 #define LOG_NDEBUG 1
 #define LOG_TAG "ICU"
 
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <string>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <time.h>
-#include <unistd.h>
-
 #include <memory>
 #include <vector>
 
-#include <androidicuinit/IcuRegistration.h>
 #include <log/log.h>
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedStringChars.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/jni_macros.h>
 #include <nativehelper/toStringArray.h>
 
 #include "IcuUtilities.h"
-#include "JniConstants.h"
 #include "JniException.h"
-#include "ScopedIcuLocale.h"
 #include "ScopedIcuULoc.h"
-#include "ScopedJavaUnicodeString.h"
-#include "unicode/brkiter.h"
 #include "unicode/char16ptr.h"
-#include "unicode/calendar.h"
-#include "unicode/datefmt.h"
-#include "unicode/dcfmtsym.h"
-#include "unicode/decimfmt.h"
-#include "unicode/dtfmtsym.h"
-#include "unicode/gregocal.h"
-#include "unicode/locid.h"
-#include "unicode/numfmt.h"
-#include "unicode/strenum.h"
-#include "unicode/ubrk.h"
-#include "unicode/ucal.h"
-#include "unicode/ucasemap.h"
-#include "unicode/ucol.h"
-#include "unicode/ucurr.h"
-#include "unicode/udat.h"
-#include "unicode/udatpg.h"
+#include "unicode/uchar.h"
 #include "unicode/uloc.h"
-#include "unicode/ures.h"
+#include "unicode/ulocdata.h"
 #include "unicode/ustring.h"
-#include "ureslocs.h"
-#include "valueOf.h"
+#include "unicode/uversion.h"
 
-class ScopedResourceBundle {
- public:
-  explicit ScopedResourceBundle(UResourceBundle* bundle) : bundle_(bundle) {
-  }
-
-  ~ScopedResourceBundle() {
-    if (bundle_ != NULL) {
-      ures_close(bundle_);
-    }
-  }
-
-  UResourceBundle* get() {
-    return bundle_;
-  }
-
-  bool hasKey(const char* key) {
-    UErrorCode status = U_ZERO_ERROR;
-    ures_getStringByKey(bundle_, key, NULL, &status);
-    return U_SUCCESS(status);
-  }
-
- private:
-  UResourceBundle* bundle_;
-  DISALLOW_COPY_AND_ASSIGN(ScopedResourceBundle);
-};
+#define U_ICUDATA_CURR U_ICUDATA_NAME "-" "curr"
 
 static jstring ICU_getScript(JNIEnv* env, jclass, jstring javaLocaleName) {
   ScopedIcuULoc icuLocale(env, javaLocaleName);
@@ -103,7 +46,7 @@
   // Normal script part is 4-char long. Being conservative for allocation size
   // because if the locale contains script part, it should not be longer than the locale itself.
   int32_t capacity = std::max(ULOC_SCRIPT_CAPACITY, icuLocale.locale_length() + 1);
-  std::unique_ptr<char[]> buffer(new char(capacity));
+  auto buffer = std::make_unique<char[]>(capacity);
   UErrorCode status = U_ZERO_ERROR;
   uloc_getScript(icuLocale.locale(), buffer.get(), capacity, &status);
   if (U_FAILURE(status)) {
@@ -112,81 +55,6 @@
   return env->NewStringUTF(buffer.get());
 }
 
-// TODO: rewrite this with int32_t ucurr_forLocale(const char* locale, UChar* buff, int32_t buffCapacity, UErrorCode* ec)...
-static jstring ICU_getCurrencyCode(JNIEnv* env, jclass, jstring javaCountryCode) {
-    UErrorCode status = U_ZERO_ERROR;
-    ScopedResourceBundle supplData(ures_openDirect(U_ICUDATA_CURR, "supplementalData", &status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-
-    ScopedResourceBundle currencyMap(ures_getByKey(supplData.get(), "CurrencyMap", NULL, &status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-
-    ScopedUtfChars countryCode(env, javaCountryCode);
-    ScopedResourceBundle currency(ures_getByKey(currencyMap.get(), countryCode.c_str(), NULL, &status));
-    if (U_FAILURE(status)) {
-        return NULL;
-    }
-
-    ScopedResourceBundle currencyElem(ures_getByIndex(currency.get(), 0, NULL, &status));
-    if (U_FAILURE(status)) {
-        return env->NewStringUTF("XXX");
-    }
-
-    // Check if there's a 'to' date. If there is, the currency isn't used anymore.
-    ScopedResourceBundle currencyTo(ures_getByKey(currencyElem.get(), "to", NULL, &status));
-    if (!U_FAILURE(status)) {
-        return NULL;
-    }
-    // Ignore the failure to find a 'to' date.
-    status = U_ZERO_ERROR;
-
-    ScopedResourceBundle currencyId(ures_getByKey(currencyElem.get(), "id", NULL, &status));
-    if (U_FAILURE(status)) {
-        // No id defined for this country
-        return env->NewStringUTF("XXX");
-    }
-
-    int32_t charCount;
-    const UChar* chars = ures_getString(currencyId.get(), &charCount, &status);
-    return (charCount == 0) ? env->NewStringUTF("XXX") : jniCreateString(env, chars, charCount);
-}
-
-static jstring getCurrencyName(JNIEnv* env, jstring javaLanguageTag, jstring javaCurrencyCode, UCurrNameStyle nameStyle) {
-  ScopedUtfChars languageTag(env, javaLanguageTag);
-  if (languageTag.c_str() == NULL) {
-    return NULL;
-  }
-  ScopedJavaUnicodeString currencyCode(env, javaCurrencyCode);
-  if (!currencyCode.valid()) {
-    return NULL;
-  }
-  icu::UnicodeString icuCurrencyCode(currencyCode.unicodeString());
-  UErrorCode status = U_ZERO_ERROR;
-  UBool isChoiceFormat = false;
-  int32_t charCount;
-  const UChar* chars = ucurr_getName(icuCurrencyCode.getTerminatedBuffer(), languageTag.c_str(),
-                                     nameStyle, &isChoiceFormat, &charCount, &status);
-  if (status == U_USING_DEFAULT_WARNING) {
-    if (nameStyle == UCURR_SYMBOL_NAME) {
-      // ICU doesn't distinguish between falling back to the root locale and meeting a genuinely
-      // unknown currency. The Currency class does.
-      if (!ucurr_isAvailable(icuCurrencyCode.getTerminatedBuffer(), U_DATE_MIN, U_DATE_MAX, &status)) {
-        return NULL;
-      }
-    }
-    if (nameStyle == UCURR_LONG_NAME) {
-      // ICU's default is English. We want the ISO 4217 currency code instead.
-      chars = icuCurrencyCode.getBuffer();
-      charCount = icuCurrencyCode.length();
-    }
-  }
-  return (charCount == 0) ? NULL : jniCreateString(env, chars, charCount);
-}
-
 static jstring ICU_getISO3Country(JNIEnv* env, jclass, jstring javaLanguageTag) {
   ScopedIcuULoc icuLocale(env, javaLanguageTag);
   if (!icuLocale.valid()) {
@@ -215,472 +83,53 @@
     return toStringArray(env, uloc_countAvailable, uloc_getAvailable);
 }
 
-static bool setIntegerField(JNIEnv* env, jobject obj, const char* fieldName, int value) {
-    ScopedLocalRef<jobject> integerValue(env, integerValueOf(env, value));
-    if (integerValue.get() == NULL) return false;
-    jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "Ljava/lang/Integer;");
-    env->SetObjectField(obj, fid, integerValue.get());
-    return true;
-}
-
-static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, jstring value) {
-    jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "Ljava/lang/String;");
-    env->SetObjectField(obj, fid, value);
-    env->DeleteLocalRef(value);
-}
-
-static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, jobjectArray value) {
-    jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "[Ljava/lang/String;");
-    env->SetObjectField(obj, fid, value);
-}
-
-static void setStringArrayField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString* valueArray, int32_t size) {
-    ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(size, JniConstants::GetStringClass(env), NULL));
-    for (int32_t i = 0; i < size ; i++) {
-        ScopedLocalRef<jstring> s(env, jniCreateString(env, valueArray[i].getBuffer(),valueArray[i].length()));
-        if (env->ExceptionCheck()) {
-            return;
-        }
-        env->SetObjectArrayElement(result.get(), i, s.get());
-        if (env->ExceptionCheck()) {
-            return;
-        }
-    }
-    setStringArrayField(env, obj, fieldName, result.get());
-}
-
-static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, UResourceBundle* bundle, int index) {
-  UErrorCode status = U_ZERO_ERROR;
-  int charCount;
-  const UChar* chars;
-  UResourceBundle* currentBundle = ures_getByIndex(bundle, index, NULL, &status);
-  switch (ures_getType(currentBundle)) {
-      case URES_STRING:
-         chars = ures_getString(currentBundle, &charCount, &status);
-         break;
-      case URES_ARRAY:
-         // In case there is an array, Android currently only cares about the
-         // first string of that array, the rest of the array is used by ICU
-         // for additional data ignored by Android.
-         chars = ures_getStringByIndex(currentBundle, 0, &charCount, &status);
-         break;
-      default:
-         status = U_INVALID_FORMAT_ERROR;
-  }
-  ures_close(currentBundle);
-  if (U_SUCCESS(status)) {
-    setStringField(env, obj, fieldName, jniCreateString(env, chars, charCount));
-  } else {
-    ALOGE("Error setting String field %s from ICU resource (index %d): %s", fieldName, index, u_errorName(status));
-  }
-}
-
-static void setCharField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
-    if (value.length() == 0) {
-        return;
-    }
-    jfieldID fid = env->GetFieldID(JniConstants::GetLocaleDataClass(env), fieldName, "C");
-    env->SetCharField(obj, fid, value.charAt(0));
-}
-
-static void setStringField(JNIEnv* env, jobject obj, const char* fieldName, const icu::UnicodeString& value) {
-    const UChar* chars = value.getBuffer();
-    setStringField(env, obj, fieldName, jniCreateString(env, chars, value.length()));
-}
-
-static void setNumberPatterns(JNIEnv* env, jobject obj, icu::Locale& locale) {
-    UErrorCode status = U_ZERO_ERROR;
-
-    icu::UnicodeString pattern;
-    std::unique_ptr<icu::DecimalFormat> fmt(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_CURRENCY, status)));
-    pattern = fmt->toPattern(pattern.remove());
-    setStringField(env, obj, "currencyPattern", pattern);
-
-    fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_DECIMAL, status)));
-    pattern = fmt->toPattern(pattern.remove());
-    setStringField(env, obj, "numberPattern", pattern);
-
-    fmt.reset(static_cast<icu::DecimalFormat*>(icu::NumberFormat::createInstance(locale, UNUM_PERCENT, status)));
-    pattern = fmt->toPattern(pattern.remove());
-    setStringField(env, obj, "percentPattern", pattern);
-}
-
-static void setDecimalFormatSymbolsData(JNIEnv* env, jobject obj, icu::Locale& locale) {
-    UErrorCode status = U_ZERO_ERROR;
-    icu::DecimalFormatSymbols dfs(locale, status);
-
-    setCharField(env, obj, "decimalSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol));
-    setCharField(env, obj, "groupingSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol));
-    setCharField(env, obj, "patternSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kPatternSeparatorSymbol));
-    setStringField(env, obj, "percent", dfs.getSymbol(icu::DecimalFormatSymbols::kPercentSymbol));
-    setStringField(env, obj, "perMill", dfs.getSymbol(icu::DecimalFormatSymbols::kPerMillSymbol));
-    setCharField(env, obj, "monetarySeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kMonetarySeparatorSymbol));
-    setStringField(env, obj, "minusSign", dfs.getSymbol(icu::DecimalFormatSymbols:: kMinusSignSymbol));
-    setStringField(env, obj, "exponentSeparator", dfs.getSymbol(icu::DecimalFormatSymbols::kExponentialSymbol));
-    setStringField(env, obj, "infinity", dfs.getSymbol(icu::DecimalFormatSymbols::kInfinitySymbol));
-    setStringField(env, obj, "NaN", dfs.getSymbol(icu::DecimalFormatSymbols::kNaNSymbol));
-    setCharField(env, obj, "zeroDigit", dfs.getSymbol(icu::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() {
-    if (locale_name_length_ == 0) {
-      has_next_ = false;
-    } else {
-      locale_name_length_ = uloc_getParent(locale_name_, locale_name_, sizeof(locale_name_), &status_);
-    }
-  }
-
- private:
-  UErrorCode& status_;
-  bool has_next_;
-  char locale_name_[ULOC_FULLNAME_CAPACITY];
-  int32_t locale_name_length_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocaleNameIterator);
-};
-
-static bool getAmPmMarkersNarrow(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 amPmMarkersNarrow(ures_getByKey(gregorian.get(), "AmPmMarkersNarrow", NULL, &status));
-  if (U_FAILURE(status)) {
-    return false;
-  }
-  setStringField(env, localeData, "narrowAm", amPmMarkersNarrow.get(), 0);
-  setStringField(env, localeData, "narrowPm", amPmMarkersNarrow.get(), 1);
-  return true;
-}
-
-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 icu::Locale& locale, const char* locale_name) {
-  UErrorCode status = U_ZERO_ERROR;
-  ScopedResourceBundle root(ures_open(NULL, locale_name, &status));
-  ScopedResourceBundle fields(ures_getByKey(root.get(), "fields", NULL, &status));
-  ScopedResourceBundle day(ures_getByKey(fields.get(), "day", NULL, &status));
-  ScopedResourceBundle relative(ures_getByKey(day.get(), "relative", NULL, &status));
-  if (U_FAILURE(status)) {
-    return false;
-  }
-
-  icu::UnicodeString yesterday(icu::ures_getUnicodeStringByKey(relative.get(), "-1", &status));
-  icu::UnicodeString today(icu::ures_getUnicodeStringByKey(relative.get(), "0", &status));
-  icu::UnicodeString tomorrow(icu::ures_getUnicodeStringByKey(relative.get(), "1", &status));
-  if (U_FAILURE(status)) {
-    ALOGE("Error getting yesterday/today/tomorrow for %s: %s", locale_name, u_errorName(status));
-    return false;
-  }
-
-  // We title-case the strings so they have consistent capitalization (http://b/14493853).
-  std::unique_ptr<icu::BreakIterator> brk(icu::BreakIterator::createSentenceInstance(locale, status));
-  if (U_FAILURE(status)) {
-    ALOGE("Error getting yesterday/today/tomorrow break iterator for %s: %s", locale_name, u_errorName(status));
-    return false;
-  }
-  yesterday.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
-  today.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
-  tomorrow.toTitle(brk.get(), locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
-
-  setStringField(env, localeData, "yesterday", yesterday);
-  setStringField(env, localeData, "today", today);
-  setStringField(env, localeData, "tomorrow", tomorrow);
-  return true;
-}
-
-static jboolean ICU_initLocaleDataNative(JNIEnv* env, jclass, jstring javaLanguageTag, jobject localeData) {
-    ScopedUtfChars languageTag(env, javaLanguageTag);
-    if (languageTag.c_str() == NULL) {
-        return JNI_FALSE;
-    }
-    if (languageTag.size() >= ULOC_FULLNAME_CAPACITY) {
-        return JNI_FALSE; // ICU has a fixed-length limit.
-    }
-
-    ScopedIcuLocale icuLocale(env, javaLanguageTag);
-    if (!icuLocale.valid()) {
-      return JNI_FALSE;
-    }
-
-    // Get the DateTimePatterns.
-    UErrorCode status = U_ZERO_ERROR;
-    bool foundDateTimePatterns = false;
-    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
-      if (getDateTimePatterns(env, localeData, it.Get())) {
-          foundDateTimePatterns = true;
-          break;
-      }
-    }
-    if (!foundDateTimePatterns) {
-        ALOGE("Couldn't find ICU DateTimePatterns for %s", languageTag.c_str());
-        return JNI_FALSE;
-    }
-
-    // Get the "Yesterday", "Today", and "Tomorrow" strings.
-    bool foundYesterdayTodayAndTomorrow = false;
-    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
-      if (getYesterdayTodayAndTomorrow(env, localeData, icuLocale.locale(), it.Get())) {
-        foundYesterdayTodayAndTomorrow = true;
-        break;
-      }
-    }
-    if (!foundYesterdayTodayAndTomorrow) {
-      ALOGE("Couldn't find ICU yesterday/today/tomorrow for %s", languageTag.c_str());
-      return JNI_FALSE;
-    }
-
-    // Get the narrow "AM" and "PM" strings.
-    bool foundAmPmMarkersNarrow = false;
-    for (LocaleNameIterator it(icuLocale.locale().getBaseName(), status); it.HasNext(); it.Up()) {
-      if (getAmPmMarkersNarrow(env, localeData, it.Get())) {
-        foundAmPmMarkersNarrow = true;
-        break;
-      }
-    }
-    if (!foundAmPmMarkersNarrow) {
-      ALOGE("Couldn't find ICU AmPmMarkersNarrow for %s", languageTag.c_str());
-      return JNI_FALSE;
-    }
-
-    status = U_ZERO_ERROR;
-    std::unique_ptr<icu::Calendar> cal(icu::Calendar::createInstance(icuLocale.locale(), status));
-    if (U_FAILURE(status)) {
-        return JNI_FALSE;
-    }
-    if (!setIntegerField(env, localeData, "firstDayOfWeek", cal->getFirstDayOfWeek())) {
-      return JNI_FALSE;
-    }
-    if (!setIntegerField(env, localeData, "minimalDaysInFirstWeek", cal->getMinimalDaysInFirstWeek())) {
-      return JNI_FALSE;
-    }
-
-    // Get DateFormatSymbols.
-    status = U_ZERO_ERROR;
-    icu::DateFormatSymbols dateFormatSym(icuLocale.locale(), status);
-    if (U_FAILURE(status)) {
-        return JNI_FALSE;
-    }
-
-    // Get AM/PM and BC/AD.
-    int32_t count = 0;
-    const icu::UnicodeString* amPmStrs = dateFormatSym.getAmPmStrings(count);
-    setStringArrayField(env, localeData, "amPm", amPmStrs, count);
-    const icu::UnicodeString* erasStrs = dateFormatSym.getEras(count);
-    setStringArrayField(env, localeData, "eras", erasStrs, count);
-
-    const icu::UnicodeString* longMonthNames =
-       dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
-    setStringArrayField(env, localeData, "longMonthNames", longMonthNames, count);
-    const icu::UnicodeString* shortMonthNames =
-        dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
-    setStringArrayField(env, localeData, "shortMonthNames", shortMonthNames, count);
-    const icu::UnicodeString* tinyMonthNames =
-        dateFormatSym.getMonths(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
-    setStringArrayField(env, localeData, "tinyMonthNames", tinyMonthNames, count);
-    const icu::UnicodeString* longWeekdayNames =
-        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::WIDE);
-    setStringArrayField(env, localeData, "longWeekdayNames", longWeekdayNames, count);
-    const icu::UnicodeString* shortWeekdayNames =
-        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::ABBREVIATED);
-    setStringArrayField(env, localeData, "shortWeekdayNames", shortWeekdayNames, count);
-    const icu::UnicodeString* tinyWeekdayNames =
-        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::FORMAT, icu::DateFormatSymbols::NARROW);
-    setStringArrayField(env, localeData, "tinyWeekdayNames", tinyWeekdayNames, count);
-
-    const icu::UnicodeString* longStandAloneMonthNames =
-        dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
-    setStringArrayField(env, localeData, "longStandAloneMonthNames", longStandAloneMonthNames, count);
-    const icu::UnicodeString* shortStandAloneMonthNames =
-        dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
-    setStringArrayField(env, localeData, "shortStandAloneMonthNames", shortStandAloneMonthNames, count);
-    const icu::UnicodeString* tinyStandAloneMonthNames =
-        dateFormatSym.getMonths(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
-    setStringArrayField(env, localeData, "tinyStandAloneMonthNames", tinyStandAloneMonthNames, count);
-    const icu::UnicodeString* longStandAloneWeekdayNames =
-        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::WIDE);
-    setStringArrayField(env, localeData, "longStandAloneWeekdayNames", longStandAloneWeekdayNames, count);
-    const icu::UnicodeString* shortStandAloneWeekdayNames =
-        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::ABBREVIATED);
-    setStringArrayField(env, localeData, "shortStandAloneWeekdayNames", shortStandAloneWeekdayNames, count);
-    const icu::UnicodeString* tinyStandAloneWeekdayNames =
-        dateFormatSym.getWeekdays(count, icu::DateFormatSymbols::STANDALONE, icu::DateFormatSymbols::NARROW);
-    setStringArrayField(env, localeData, "tinyStandAloneWeekdayNames", tinyStandAloneWeekdayNames, count);
-
-    status = U_ZERO_ERROR;
-
-    // For numberPatterns and symbols.
-    setNumberPatterns(env, localeData, icuLocale.locale());
-    setDecimalFormatSymbolsData(env, localeData, icuLocale.locale());
-
-    jstring countryCode = env->NewStringUTF(icuLocale.locale().getCountry());
-    jstring internationalCurrencySymbol = ICU_getCurrencyCode(env, NULL, countryCode);
-    env->DeleteLocalRef(countryCode);
-    countryCode = NULL;
-
-    jstring currencySymbol = NULL;
-    if (internationalCurrencySymbol != NULL) {
-        currencySymbol = getCurrencyName(env, javaLanguageTag, internationalCurrencySymbol, UCURR_SYMBOL_NAME);
-    } else {
-        internationalCurrencySymbol = env->NewStringUTF("XXX");
-    }
-    if (currencySymbol == NULL) {
-        // This is the UTF-8 encoding of U+00A4 (CURRENCY SIGN).
-        currencySymbol = env->NewStringUTF("\xc2\xa4");
-    }
-    setStringField(env, localeData, "currencySymbol", currencySymbol);
-    setStringField(env, localeData, "internationalCurrencySymbol", internationalCurrencySymbol);
-
-    return JNI_TRUE;
-}
-
-static jstring ICU_getBestDateTimePatternNative(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLanguageTag) {
-  ScopedIcuULoc icuLocale(env, javaLanguageTag);
-  if (!icuLocale.valid()) {
-    return NULL;
-  }
-
-  UErrorCode status = U_ZERO_ERROR;
-  std::unique_ptr<UDateTimePatternGenerator, decltype(&udatpg_close)> generator(
-    udatpg_open(icuLocale.locale(), &status), &udatpg_close);
-  if (maybeThrowIcuException(env, "udatpg_open", status)) {
-    return NULL;
-  }
-
-  const ScopedStringChars skeletonHolder(env, javaSkeleton);
-  // Convert jchar* to UChar* with the inline-able utility provided by char16ptr.h
-  // which prevents certain compiler optimization than reinterpret_cast.
-  icu::ConstChar16Ptr skeletonPtr(skeletonHolder.get());
-  const UChar* skeleton = icu::toUCharPtr(skeletonPtr.get());
-
-  int32_t patternLength;
-  // Try with fixed-size buffer. 128 chars should be enough for most patterns.
-  // If the buffer is not sufficient, run the below case of U_BUFFER_OVERFLOW_ERROR.
-  #define PATTERN_BUFFER_SIZE 128
-  {
-    UChar buffer[PATTERN_BUFFER_SIZE];
-    status = U_ZERO_ERROR;
-    patternLength = udatpg_getBestPattern(generator.get(), skeleton,
-      skeletonHolder.size(), buffer, PATTERN_BUFFER_SIZE, &status);
-    if (U_SUCCESS(status)) {
-      return jniCreateString(env, buffer, patternLength);
-    } else if (status != U_BUFFER_OVERFLOW_ERROR) {
-      maybeThrowIcuException(env, "udatpg_getBestPattern", status);
-      return NULL;
-    }
-  }
-  #undef PATTERN_BUFFER_SIZE
-
-  // Case U_BUFFER_OVERFLOW_ERROR
-  std::unique_ptr<UChar[]> buffer(new UChar[patternLength+1]);
-  status = U_ZERO_ERROR;
-  patternLength = udatpg_getBestPattern(generator.get(), skeleton,
-      skeletonHolder.size(), buffer.get(), patternLength+1, &status);
-  if (maybeThrowIcuException(env, "udatpg_getBestPattern", status)) {
-    return NULL;
-  }
-
-  return jniCreateString(env, buffer.get(), patternLength);
-}
-
-static void ICU_setDefaultLocale(JNIEnv* env, jclass, jstring javaLanguageTag) {
-  ScopedIcuULoc icuLocale(env, javaLanguageTag);
-  if (!icuLocale.valid()) {
-    return;
-  }
-
-  UErrorCode status = U_ZERO_ERROR;
-  uloc_setDefault(icuLocale.locale(), &status);
-  maybeThrowIcuException(env, "uloc_setDefault", status);
-}
-
 static jstring ICU_getDefaultLocale(JNIEnv* env, jclass) {
   return env->NewStringUTF(uloc_getDefault());
 }
 
+static jstring versionString(JNIEnv* env, const UVersionInfo& version) {
+    char versionString[U_MAX_VERSION_STRING_LENGTH];
+    u_versionToString(const_cast<UVersionInfo&>(version), &versionString[0]);
+    return env->NewStringUTF(versionString);
+}
+
+static jstring ICU_getCldrVersion(JNIEnv* env, jclass) {
+  UErrorCode status = U_ZERO_ERROR;
+  UVersionInfo cldrVersion;
+  ulocdata_getCLDRVersion(cldrVersion, &status);
+  return versionString(env, cldrVersion);
+}
+
+static jstring ICU_getIcuVersion(JNIEnv* env, jclass) {
+    UVersionInfo icuVersion;
+    u_getVersion(icuVersion);
+    return versionString(env, icuVersion);
+}
+
+static jstring ICU_getUnicodeVersion(JNIEnv* env, jclass) {
+    UVersionInfo unicodeVersion;
+    u_getUnicodeVersion(unicodeVersion);
+    return versionString(env, unicodeVersion);
+}
+
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(ICU, getAvailableLocalesNative, "()[Ljava/lang/String;"),
-    NATIVE_METHOD(ICU, getBestDateTimePatternNative, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
-    NATIVE_METHOD(ICU, getCurrencyCode, "(Ljava/lang/String;)Ljava/lang/String;"),
+    NATIVE_METHOD(ICU, getCldrVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getDefaultLocale, "()Ljava/lang/String;"),
+    NATIVE_METHOD(ICU, getIcuVersion, "()Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getISO3Country, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getISO3Language, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getISOCountriesNative, "()[Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getISOLanguagesNative, "()[Ljava/lang/String;"),
     NATIVE_METHOD(ICU, getScript, "(Ljava/lang/String;)Ljava/lang/String;"),
-    NATIVE_METHOD(ICU, initLocaleDataNative, "(Ljava/lang/String;Llibcore/icu/LocaleData;)Z"),
-    NATIVE_METHOD(ICU, setDefaultLocale, "(Ljava/lang/String;)V"),
+    NATIVE_METHOD(ICU, getUnicodeVersion, "()Ljava/lang/String;"),
 };
 
-//
-// Global initialization & Teardown for ICU Setup
-//   - Contains handlers for JNI_OnLoad and JNI_OnUnload
-//
-
-// Init ICU, configuring it and loading the data files.
 void register_libcore_icu_ICU(JNIEnv* env) {
-  androidicuinit::IcuRegistration::Register();
-
   jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
 }
 
-// De-init ICU, unloading the data files. Do the opposite of the above function.
 void unregister_libcore_icu_ICU() {
   // Skip unregistering JNI methods explicitly, class unloading takes care of
   // it.
-
-  androidicuinit::IcuRegistration::Deregister();
 }
diff --git a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp b/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
deleted file mode 100644
index db4c6f2..0000000
--- a/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#define LOG_TAG "TimeZoneNames"
-
-#include <memory>
-
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativehelper/jni_macros.h>
-
-#include "IcuUtilities.h"
-#include "JniException.h"
-#include "ScopedIcuLocale.h"
-#include "ScopedJavaUnicodeString.h"
-#include "unicode/calendar.h"
-#include "unicode/timezone.h"
-#include "unicode/tznames.h"
-
-static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const icu::UnicodeString& s) {
-  // Don't use "GMT" string, for backwards compatibility.
-  static const icu::UnicodeString kGmt("GMT", 3, US_INV);
-  if (!s.isBogus() && !s.startsWith(kGmt)) {
-    ScopedLocalRef<jstring> javaString(env, jniCreateString(env, s.getBuffer(), s.length()));
-    if (javaString.get() == NULL) {
-      return false;
-    }
-    env->SetObjectArrayElement(array, i, javaString.get());
-  }
-  return true;
-}
-
-static void TimeZoneNames_fillZoneStrings(JNIEnv* env, jclass, jstring javaLocaleName, jobjectArray result) {
-  ScopedIcuLocale icuLocale(env, javaLocaleName);
-  if (!icuLocale.valid()) {
-    return;
-  }
-
-  UErrorCode status = U_ZERO_ERROR;
-  std::unique_ptr<icu::TimeZoneNames> names(icu::TimeZoneNames::createInstance(icuLocale.locale(), status));
-  if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) {
-    return;
-  }
-
-  const UDate now(icu::Calendar::getNow());
-
-  size_t id_count = env->GetArrayLength(result);
-  for (size_t i = 0; i < id_count; ++i) {
-    ScopedLocalRef<jobjectArray> java_row(env,
-                                          reinterpret_cast<jobjectArray>(env->GetObjectArrayElement(result, i)));
-    ScopedLocalRef<jstring> java_zone_id(env,
-                                         reinterpret_cast<jstring>(env->GetObjectArrayElement(java_row.get(), 0)));
-    ScopedJavaUnicodeString zone_id(env, java_zone_id.get());
-    if (!zone_id.valid()) {
-      return;
-    }
-
-    // Canonicalize the zone ID to the one known by ICU.
-    icu::UnicodeString lookup_id;
-    icu::TimeZone::getCanonicalID(zone_id.unicodeString(), lookup_id, status);
-    if (status != U_ZERO_ERROR) {
-      // Unknown ID - just use the zone ID we have.
-      lookup_id = zone_id.unicodeString();
-    }
-
-    icu::UnicodeString long_std;
-    names->getDisplayName(lookup_id, UTZNM_LONG_STANDARD, now, long_std);
-    icu::UnicodeString short_std;
-    names->getDisplayName(lookup_id, UTZNM_SHORT_STANDARD, now, short_std);
-    icu::UnicodeString long_dst;
-    names->getDisplayName(lookup_id, UTZNM_LONG_DAYLIGHT, now, long_dst);
-    icu::UnicodeString short_dst;
-    names->getDisplayName(lookup_id, UTZNM_SHORT_DAYLIGHT, now, short_dst);
-
-    bool okay =
-        setStringArrayElement(env, java_row.get(), 1, long_std) &&
-        setStringArrayElement(env, java_row.get(), 2, short_std) &&
-        setStringArrayElement(env, java_row.get(), 3, long_dst) &&
-        setStringArrayElement(env, java_row.get(), 4, short_dst);
-    if (!okay) {
-      return;
-    }
-  }
-}
-
-static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(TimeZoneNames, fillZoneStrings, "(Ljava/lang/String;[[Ljava/lang/String;)V"),
-};
-void register_libcore_icu_TimeZoneNames(JNIEnv* env) {
-  jniRegisterNativeMethods(env, "libcore/icu/TimeZoneNames", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
index 55803b8..e99edd6 100644
--- a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
+++ b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "AsynchronousCloseMonitor"
 
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <nativehelper/jni_macros.h>
 
 #include "AsynchronousCloseMonitor.h"
diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp
old mode 100644
new mode 100755
index 9e44e63..d7b6c22
--- a/luni/src/main/native/libcore_io_Linux.cpp
+++ b/luni/src/main/native/libcore_io_Linux.cpp
@@ -29,6 +29,7 @@
 #include <pwd.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/capability.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
@@ -58,8 +59,7 @@
 #include <android-base/macros.h>
 #include <android-base/strings.h>
 #include <log/log.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedBytes.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
@@ -72,6 +72,10 @@
 #include "JniException.h"
 #include "NetworkUtilities.h"
 #include "Portability.h"
+#include "ScopedBytes.h"
+#include "ScopedByteBufferArray.h"
+#include "ScopedMsghdr.h"
+
 
 #ifndef __unused
 #define __unused __attribute__((__unused__))
@@ -256,12 +260,7 @@
 
 static void throwErrnoException(JNIEnv* env, const char* functionName) {
     int error = errno;
-    jclass errnoExceptionClass = JniConstants::GetErrnoExceptionClass(env);
-    static jmethodID ctor3 = env->GetMethodID(errnoExceptionClass,
-            "<init>", "(Ljava/lang/String;ILjava/lang/Throwable;)V");
-    static jmethodID ctor2 = env->GetMethodID(errnoExceptionClass,
-            "<init>", "(Ljava/lang/String;I)V");
-    throwException(env, errnoExceptionClass, ctor3, ctor2, functionName, error);
+    jniThrowErrnoException(env, functionName, error);
 }
 
 static void throwGaiException(JNIEnv* env, const char* functionName, int error) {
@@ -349,6 +348,18 @@
     std::vector<ScopedT*> mScopedBuffers;
 };
 
+static jobject createFileDescriptorIfOpen(JNIEnv* env, int fd) {
+    if (fd == -1) {
+        return NULL;
+    }
+    jobject jifd = jniCreateFileDescriptor(env, fd);
+    if (jifd == NULL) {
+        // OOME prevented allocation of j.i.FileDescriptor instance, close fd to avoid leak.
+        close(fd);
+    }
+    return jifd;
+}
+
 /**
  * Returns a jbyteArray containing the sockaddr_un.sun_path from ss. As per unix(7) sa_len should be
  * the length of ss as returned by getsockname(2), getpeername(2), or accept(2).
@@ -604,6 +615,30 @@
     return true;
 }
 
+#if __has_include(<linux/vm_sockets.h>)
+static bool fillVmSocketAddress(JNIEnv* env, jobject javaVmSocketAddress,
+        const sockaddr_storage& ss) {
+    static jfieldID portFid = env->GetFieldID(
+            JniConstants::GetVmSocketAddressClass(env), "svmPort", "I");
+    static jfieldID cidFid = env->GetFieldID(
+            JniConstants::GetVmSocketAddressClass(env), "svmCid", "I");
+
+    if (javaVmSocketAddress == NULL) {
+        return true;
+    }
+
+    if (ss.ss_family != AF_VSOCK) {
+        return false;
+    }
+
+    const sockaddr_vm& svm = reinterpret_cast<const sockaddr_vm&>(ss);
+    env->SetIntField(javaVmSocketAddress, portFid, svm.svm_port);
+    env->SetIntField(javaVmSocketAddress, cidFid, svm.svm_cid);
+
+    return true;
+}
+#endif
+
 static bool fillSocketAddress(JNIEnv* env, jobject javaSocketAddress, const sockaddr_storage& ss,
         const socklen_t& sa_len) {
     if (javaSocketAddress == NULL) {
@@ -614,6 +649,10 @@
         return fillInetSocketAddress(env, javaSocketAddress, ss);
     } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetUnixSocketAddressClass(env))) {
         return fillUnixSocketAddress(env, javaSocketAddress, ss, sa_len);
+#if __has_include(<linux/vm_sockets.h>)
+    } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetVmSocketAddressClass(env))) {
+        return fillVmSocketAddress(env, javaSocketAddress, ss);
+#endif
     }
     jniThrowException(env, "java/lang/UnsupportedOperationException",
             "unsupported SocketAddress subclass");
@@ -719,6 +758,24 @@
     return true;
 }
 
+#if __has_include(<linux/vm_sockets.h>)
+static bool javaVmSocketAddressToSockaddr(
+        JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+    static jfieldID portFid = env->GetFieldID(
+            JniConstants::GetVmSocketAddressClass(env), "svmPort", "I");
+    static jfieldID cidFid = env->GetFieldID(
+            JniConstants::GetVmSocketAddressClass(env), "svmCid", "I");
+
+    sockaddr_vm& svm = reinterpret_cast<sockaddr_vm&>(ss);
+    svm.svm_family = AF_VSOCK;
+    svm.svm_port = env->GetIntField(javaSocketAddress, portFid);
+    svm.svm_cid = env->GetIntField(javaSocketAddress, cidFid);
+
+    sa_len = sizeof(svm);
+    return true;
+}
+#endif
+
 static bool javaSocketAddressToSockaddr(
         JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
     if (javaSocketAddress == NULL) {
@@ -734,6 +791,11 @@
         return javaPacketSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
     } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::GetUnixSocketAddressClass(env))) {
         return javaUnixSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+#if __has_include(<linux/vm_sockets.h>)
+    } else if (env->IsInstanceOf(javaSocketAddress,
+                                 JniConstants::GetVmSocketAddressClass(env))) {
+        return javaVmSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+#endif
     }
     jniThrowException(env, "java/lang/UnsupportedOperationException",
             "unsupported SocketAddress subclass");
@@ -965,7 +1027,7 @@
         close(clientFd);
         return NULL;
     }
-    return (clientFd != -1) ? jniCreateFileDescriptor(env, clientFd) : NULL;
+    return createFileDescriptorIfOpen(env, clientFd);
 }
 
 static jboolean Linux_access(JNIEnv* env, jobject, jstring javaPath, jint mode) {
@@ -1111,7 +1173,9 @@
     jniSetFileDescriptorOfFD(env, javaFd, -1);
 
 #if defined(__BIONIC__)
-    jlong ownerId = jniGetOwnerIdFromFileDescriptor(env, javaFd);
+    static jmethodID getOwnerId = env->GetMethodID(JniConstants::GetFileDescriptorClass(env),
+                                                   "getOwnerId$", "()J");
+    jlong ownerId = env->CallLongMethod(javaFd, getOwnerId);
 
     // Close with bionic's fd ownership tracking (which returns 0 in the case of EINTR).
     throwIfMinusOne(env, "close", android_fdsan_close_with_tag(fd, ownerId));
@@ -1193,13 +1257,13 @@
 static jobject Linux_dup(JNIEnv* env, jobject, jobject javaOldFd) {
     int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd);
     int newFd = throwIfMinusOne(env, "dup", TEMP_FAILURE_RETRY(dup(oldFd)));
-    return (newFd != -1) ? jniCreateFileDescriptor(env, newFd) : NULL;
+    return createFileDescriptorIfOpen(env, newFd);
 }
 
 static jobject Linux_dup2(JNIEnv* env, jobject, jobject javaOldFd, jint newFd) {
     int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd);
     int fd = throwIfMinusOne(env, "dup2", TEMP_FAILURE_RETRY(dup2(oldFd, newFd)));
-    return (fd != -1) ? jniCreateFileDescriptor(env, fd) : NULL;
+    return createFileDescriptorIfOpen(env, fd);
 }
 
 static jobjectArray Linux_environ(JNIEnv* env, jobject) {
@@ -1737,16 +1801,12 @@
     return sockaddrToInetAddress(env, reinterpret_cast<sockaddr_storage&>(req.ifr_addr), NULL);
 }
 
-static jint Linux_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaArg) {
-    // This is complicated because ioctls may return their result by updating their argument
-    // or via their return value, so we need to support both.
+static jint Linux_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd) {
+    // Result is being stored in arg, thus simply returning it
     int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    jint arg = env->GetIntField(javaArg, int32RefValueFid);
-    int rc = throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, cmd, &arg)));
-    if (!env->ExceptionCheck()) {
-        env->SetIntField(javaArg, int32RefValueFid, arg);
-    }
-    return rc;
+    jint arg = 0;
+    throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, cmd, &arg)));
+    return arg;
 }
 
 static jint Linux_ioctlMTU(JNIEnv* env, jobject, jobject javaFd, jstring javaInterfaceName) {
@@ -1845,7 +1905,7 @@
     }
 
     int fd = throwIfMinusOne(env, "memfd_create", memfd_create(name.c_str(), flags));
-    return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
+    return createFileDescriptorIfOpen(env, fd);
 #else
     UNUSED(env, javaName, flags);
     return NULL;
@@ -1914,7 +1974,7 @@
         return NULL;
     }
     int fd = throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
-    return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
+    return createFileDescriptorIfOpen(env, fd);
 }
 
 static jobjectArray Linux_pipe2(JNIEnv* env, jobject, jint flags __unused) {
@@ -1929,11 +1989,10 @@
     }
     for (int i = 0; i < 2; ++i) {
         ScopedLocalRef<jobject> fd(env, jniCreateFileDescriptor(env, fds[i]));
-        if (fd.get() == NULL) {
-            return NULL;
-        }
         env->SetObjectArrayElement(result, i, fd.get());
-        if (env->ExceptionCheck()) {
+        if (fd.get() == NULL || env->ExceptionCheck()) {
+            close(fds[0]);
+            close(fds[1]);
             return NULL;
         }
     }
@@ -2124,6 +2183,62 @@
     return recvCount;
 }
 
+static jint Linux_recvmsg(JNIEnv* env, jobject, jobject javaFd, jobject structMsghdr, jint flags) {
+    ssize_t rc = -1;
+    ScopedMsghdr scopedMsghdrValue;
+    ScopedByteBufferArray scopedBytesArray(env, true);
+    sockaddr_storage ss = {};
+
+    static jfieldID msgNameFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+                                                  "msg_name", "Ljava/net/SocketAddress;");
+    if (!msgNameFid) {
+        return -1;
+    }
+
+    // Initialize msghdr with everything from StructCMsghdr except msg_name.
+    if (msghdrJavaToC(env, structMsghdr, scopedMsghdrValue.getObject(),
+                           scopedBytesArray) == false) {
+        return -1;
+    }
+
+    jobject javaSocketAddress = env->GetObjectField(structMsghdr, msgNameFid);
+    if (javaSocketAddress) {
+        // client want to get source address, then set msg_name and msg_namelen.
+        scopedMsghdrValue.setMsgNameAndLen(reinterpret_cast<sockaddr*>(&ss),
+                                              sizeof(sockaddr_in6));
+    }
+
+    rc = NET_FAILURE_RETRY(env, ssize_t, recvmsg, javaFd, \
+                               &scopedMsghdrValue.getObject(), flags);
+
+    if (rc < 0) {
+        return rc;
+    }
+
+    if (javaSocketAddress) {
+        sockaddr_storage* interfaceAddr = NULL;
+        if (!scopedMsghdrValue.isNameLenValid()) {
+            jniThrowException(env, "java/net/SocketException",
+                                   "unknown socket address type");
+                return -1;
+        }
+
+        interfaceAddr = reinterpret_cast<sockaddr_storage*>(scopedMsghdrValue.getObject().msg_name);
+        if (fillSocketAddress(env, javaSocketAddress, *interfaceAddr,
+                scopedMsghdrValue.getObject().msg_namelen) == false) {
+            return -1;
+        }
+    }
+
+    if (msghdrCToJava(env, structMsghdr, scopedMsghdrValue.getObject(),
+                            scopedBytesArray) == false) {
+        return -1;
+    }
+
+    return rc;
+}
+
+
 static void Linux_remove(JNIEnv* env, jobject, jstring javaPath) {
     ScopedUtfChars path(env, javaPath);
     if (path.c_str() == NULL) {
@@ -2180,6 +2295,66 @@
     return result;
 }
 
+static jint Linux_sendmsg(JNIEnv* env, jobject, jobject javaFd, jobject structMsghdr, jint flags) {
+
+    ssize_t rc = -1;
+    ScopedMsghdr scopedMsghdrValue = {};
+    ScopedByteBufferArray scopedBytesArray(env, false);
+    static jfieldID msgNameFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+                                                  "msg_name",
+                                                  "Ljava/net/SocketAddress;");
+    if (!msgNameFid) {
+        return -1;
+    }
+
+    jobject sockAddrObj = env->GetObjectField(structMsghdr, msgNameFid);
+
+    // Initialize structMsghdr with everything from C msghdr except msg_name.
+    if (msghdrJavaToC(env, structMsghdr, scopedMsghdrValue.getObject(),
+                           scopedBytesArray) == false) {
+        return -1;
+    }
+
+    sockaddr_storage ss = {};
+    socklen_t sa_len = 0;
+    if (sockAddrObj && javaSocketAddressToSockaddr(env, sockAddrObj, ss, sa_len) == false) {
+        return -1;
+    }
+
+    sockaddr* _sa = sa_len ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+    scopedMsghdrValue.setMsgNameAndLen(_sa, sa_len);
+    rc  = NET_FAILURE_RETRY(env, ssize_t, sendmsg, javaFd, \
+                                 &(scopedMsghdrValue.getObject()), flags);
+
+    if (sockAddrObj &&
+        !env->IsInstanceOf(sockAddrObj, JniConstants::GetInetSocketAddressClass(env))) {
+        // non InetSockAddress case, return now;
+        return rc;
+    }
+
+    // InetSocket and IPv6 didn't work, fallback to IPv4.
+    if (rc  == -1 && errno == EAFNOSUPPORT && sa_len && isIPv4MappedAddress(_sa)) {
+        env->ExceptionClear();
+
+        jobject javaInetAddress;
+        jint port = 0;
+        javaInetSocketAddressToInetAddressAndPort(env, sockAddrObj, javaInetAddress, port);
+
+        // Get IPv4 sockaddr.
+        if (!inetAddressToSockaddrVerbatim(env, javaInetAddress, port, ss, sa_len)) {
+            return rc;
+        }
+
+        // Use IPv4 msghdr.msg_name.
+        _sa = sa_len ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+        scopedMsghdrValue.setMsgNameAndLen(_sa, sa_len);
+        rc = NET_FAILURE_RETRY(env, ssize_t, sendmsg, javaFd, \
+                                    &(scopedMsghdrValue.getObject()), flags);
+    }
+
+    return rc;
+}
+
 static jint Linux_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetAddress, jint port) {
     ScopedBytesRO bytes(env, javaBytes);
     if (bytes.get() == NULL) {
@@ -2386,7 +2561,7 @@
         protocol = htons(protocol);  // Packet sockets specify the protocol in host byte order.
     }
     int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol)));
-    return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
+    return createFileDescriptorIfOpen(env, fd);
 }
 
 static void Linux_socketpair(JNIEnv* env, jobject, jint domain, jint type, jint protocol, jobject javaFd1, jobject javaFd2) {
@@ -2470,7 +2645,7 @@
 
 static jstring Linux_strerror(JNIEnv* env, jobject, jint errnum) {
     char buffer[BUFSIZ];
-    const char* message = jniStrError(errnum, buffer, sizeof(buffer));
+    const char* message = strerror_r(errnum, buffer, sizeof(buffer));
     return env->NewStringUTF(message);
 }
 
@@ -2628,7 +2803,7 @@
     NATIVE_METHOD(Linux, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"),
     NATIVE_METHOD(Linux, ioctlFlags, "(Ljava/io/FileDescriptor;Ljava/lang/String;)I"),
     NATIVE_METHOD(Linux, ioctlInetAddress, "(Ljava/io/FileDescriptor;ILjava/lang/String;)Ljava/net/InetAddress;"),
-    NATIVE_METHOD(Linux, ioctlInt, "(Ljava/io/FileDescriptor;ILandroid/system/Int32Ref;)I"),
+    NATIVE_METHOD(Linux, ioctlInt, "(Ljava/io/FileDescriptor;I)I"),
     NATIVE_METHOD(Linux, ioctlMTU, "(Ljava/io/FileDescriptor;Ljava/lang/String;)I"),
     NATIVE_METHOD(Linux, isatty, "(Ljava/io/FileDescriptor;)Z"),
     NATIVE_METHOD(Linux, kill, "(II)V"),
@@ -2659,10 +2834,12 @@
     NATIVE_METHOD(Linux, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(Linux, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
     NATIVE_METHOD(Linux, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"),
+    NATIVE_METHOD(Linux, recvmsg, "(Ljava/io/FileDescriptor;Landroid/system/StructMsghdr;I)I"),
     NATIVE_METHOD(Linux, remove, "(Ljava/lang/String;)V"),
     NATIVE_METHOD(Linux, removexattr, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Linux, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Linux, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/system/Int64Ref;J)J"),
+    NATIVE_METHOD(Linux, sendmsg, "(Ljava/io/FileDescriptor;Landroid/system/StructMsghdr;I)I"),
     NATIVE_METHOD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
     NATIVE_METHOD_OVERLOAD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/SocketAddress;)I", SocketAddress),
     NATIVE_METHOD(Linux, setegid, "(I)V"),
diff --git a/luni/src/main/native/libcore_io_Memory.cpp b/luni/src/main/native/libcore_io_Memory.cpp
index a5b7b72..b8a8845 100644
--- a/luni/src/main/native/libcore_io_Memory.cpp
+++ b/luni/src/main/native/libcore_io_Memory.cpp
@@ -22,12 +22,13 @@
 #include <sys/mman.h>
 
 #include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedBytes.h>
+
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/jni_macros.h>
 
 #include "JniConstants.h"
 #include "Portability.h"
+#include "ScopedBytes.h"
 
 // Use packed structures for access to unaligned data on targets with alignment restrictions.
 // The compiler will generate appropriate code to access these structures without
diff --git a/luni/src/main/native/libcore_math_NativeBN.cpp b/luni/src/main/native/libcore_math_NativeBN.cpp
new file mode 100644
index 0000000..dc4b9473
--- /dev/null
+++ b/luni/src/main/native/libcore_math_NativeBN.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NativeBN"
+
+#include <stdio.h>
+#include <algorithm>
+#include <memory>
+
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/jni_macros.h>
+
+#include "JniException.h"
+
+struct BN_CTX_Deleter {
+  void operator()(BN_CTX* p) const {
+    BN_CTX_free(p);
+  }
+};
+typedef std::unique_ptr<BN_CTX, BN_CTX_Deleter> Unique_BN_CTX;
+
+static BIGNUM* toBigNum(jlong address) {
+  return reinterpret_cast<BIGNUM*>(static_cast<uintptr_t>(address));
+}
+
+// Exception handling: We follow the usual JNI convention of "throwing" an
+// exception if anything goes wrong, and returning junk, typically null.
+// The NativeBN_ routines should only be called from Java, or from code
+// that immediately returns the result to Java, and thus the
+// Java exception should be thrown before we ever see the junk.
+// This null BNs should never become visible, and we do not have to deal with
+// junk (nulls) as input.
+static void throwException(JNIEnv* env) {
+  long error = ERR_get_error();
+  // OpenSSL's error queue may contain multiple errors. Clean up after them.
+  ERR_clear_error();
+
+  if (error == 0) {
+    // An operation failed but did not push to the error queue. Throw a default
+    // exception.
+    jniThrowException(env, "java/lang/ArithmeticException", "Operation failed");
+    return;
+  }
+
+  char message[256];
+  ERR_error_string_n(error, message, sizeof(message));
+  int reason = ERR_GET_REASON(error);
+  if (reason == BN_R_DIV_BY_ZERO) {
+    jniThrowException(env, "java/lang/ArithmeticException", "BigInteger division by zero");
+  } else if (reason == BN_R_NO_INVERSE) {
+    jniThrowException(env, "java/lang/ArithmeticException", "BigInteger not invertible");
+  } else if (reason == ERR_R_MALLOC_FAILURE) {
+    jniThrowOutOfMemoryError(env, message);
+  } else {
+    jniThrowException(env, "java/lang/ArithmeticException", message);
+  }
+}
+
+static jlong NativeBN_BN_new(JNIEnv* env, jclass) {
+  jlong result = static_cast<jlong>(reinterpret_cast<uintptr_t>(BN_new()));
+  if (!result) {
+    throwException(env);
+  }
+  return result;
+}
+
+static void NativeBN_BN_free(JNIEnv*, jclass, jlong a) {
+  // Do nothing on a zero argument.
+  BN_free(toBigNum(a));
+}
+
+static void NativeBN_litEndInts2bn(JNIEnv* env, jclass, jintArray arr, int len, jboolean neg, jlong ret0) {
+  BIGNUM* ret = toBigNum(ret0);
+
+  ScopedIntArrayRO scopedArray(env, arr);
+
+  if (scopedArray.get() == NULL) {
+    return;
+  }
+
+  // We can simply interpret the little-endian integer stream as a
+  // little-endian byte stream and use BN_le2bn.
+  const uint8_t* tmpBytes = reinterpret_cast<const uint8_t *>(scopedArray.get());
+  size_t numBytes = len * sizeof(int);
+
+  if (!BN_le2bn(tmpBytes, numBytes, ret)) {
+    throwException(env);
+  }
+
+  BN_set_negative(ret, neg);
+}
+
+static jintArray NativeBN_bn2litEndInts(JNIEnv* env, jclass, jlong a0) {
+  BIGNUM* a = toBigNum(a0);
+
+  // The number of integers we need is BN_num_bytes(a) / sizeof(int), rounded up
+  int intLen = (BN_num_bytes(a) + sizeof(int) - 1) / sizeof(int);
+
+  // Allocate our result with the JNI boilerplate
+  jintArray result = env->NewIntArray(intLen);
+
+  if (result == NULL) {
+    throwException(env);
+    return NULL;
+  }
+
+  ScopedIntArrayRW ints(env, result);
+
+  unsigned int* uints = reinterpret_cast<unsigned int*>(ints.get());
+  if (uints == NULL) {
+    throwException(env);
+    return NULL;
+  }
+
+  // We can simply interpret a little-endian byte stream as a little-endian integer stream.
+  if (!BN_bn2le_padded(reinterpret_cast<uint8_t*>(uints), intLen * sizeof(int), a)) {
+    throwException(env);
+    return NULL;
+  }
+
+  return result;
+}
+
+static void NativeBN_BN_mul(JNIEnv* env, jclass, jlong r, jlong a, jlong b) {
+  Unique_BN_CTX ctx(BN_CTX_new());
+  BN_CTX* ctxp = ctx.get();
+  if (!ctxp || !BN_mul(toBigNum(r), toBigNum(a), toBigNum(b), ctxp)) {
+    throwException(env);
+  }
+}
+
+static void NativeBN_BN_div(JNIEnv* env, jclass, jlong q, jlong rem, jlong num, jlong divisor) {
+  Unique_BN_CTX ctx(BN_CTX_new());
+  BN_CTX* ctxp = ctx.get();
+  if (!ctxp || !BN_div(toBigNum(q), toBigNum(rem), toBigNum(num), toBigNum(divisor), ctxp)) {
+    throwException(env);
+  }
+}
+
+static void NativeBN_BN_mod_exp(JNIEnv* env, jclass, jlong r, jlong a, jlong p, jlong m) {
+  Unique_BN_CTX ctx(BN_CTX_new());
+  BN_CTX* ctxp = ctx.get();
+  if (!ctxp || !BN_mod_exp(toBigNum(r), toBigNum(a), toBigNum(p), toBigNum(m), ctxp)) {
+    throwException(env);
+  }
+}
+
+static JNINativeMethod gMethods[] = {
+   NATIVE_METHOD(NativeBN, BN_div, "(JJJJ)V"),
+   NATIVE_METHOD(NativeBN, BN_free, "(J)V"),
+   NATIVE_METHOD(NativeBN, BN_mod_exp, "(JJJJ)V"),
+   NATIVE_METHOD(NativeBN, BN_mul, "(JJJ)V"),
+   NATIVE_METHOD(NativeBN, BN_new, "()J"),
+   NATIVE_METHOD(NativeBN, bn2litEndInts, "(J)[I"),
+   NATIVE_METHOD(NativeBN, litEndInts2bn, "([IIZJ)V"),
+};
+void register_libcore_math_NativeBN(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "libcore/math/NativeBN", gMethods, NELEM(gMethods));
+}
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 6df5bdd..fdabcf7 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -31,10 +31,11 @@
 #include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/jni_macros.h>
 
+#include <unicode/char16ptr.h>
+#include <unicode/ustring.h>
+
 #include "JniConstants.h"
 #include "JniException.h"
-#include "unicode/char16ptr.h"
-#include "unicode/ustring.h"
 
 #define BUCKET_COUNT 128
 
diff --git a/luni/src/module/java/module-info.java b/luni/src/module/java/module-info.java
index 6c2bc12..2d40ebb 100644
--- a/luni/src/module/java/module-info.java
+++ b/luni/src/module/java/module-info.java
@@ -106,7 +106,6 @@
     exports sun.util.resources;
     // non-oj
     exports android.system;
-    exports android.icu.impl;
     exports android.icu.math;
     exports android.icu.text;
     exports android.icu.util;
@@ -120,7 +119,6 @@
     exports libcore.net.event;
     exports libcore.net.http;
     exports libcore.reflect;
-    exports libcore.timezone;
     exports libcore.util;
     exports org.json;
 }
diff --git a/luni/src/test/annotations/Android.bp b/luni/src/test/annotations/Android.bp
index 437d7c4..ae79505 100644
--- a/luni/src/test/annotations/Android.bp
+++ b/luni/src/test/annotations/Android.bp
@@ -17,6 +17,14 @@
 //
 // Included as a resource by //libcore:core-tests and loaded into its own
 // class loader by libcore.java.lang.reflect.annotations.RetentionPolicyTest.
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_luni_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_luni_license"],
+}
+
 java_library {
     name: "annotations-test",
     visibility: [
diff --git a/luni/src/test/etc/loading-test-jar/README.txt b/luni/src/test/etc/loading-test-jar/README.txt
index c2fb191..9da43c2 100644
--- a/luni/src/test/etc/loading-test-jar/README.txt
+++ b/luni/src/test/etc/loading-test-jar/README.txt
@@ -3,6 +3,6 @@
 files are used for testing the various class loaders.
 
 The Android build system doesn't support dynamically producing
-resources in any sane way. To update the resource, use the script
+resources in any practical way. To update the resource, use the script
 build.sh in this directory, which copies resulting files into the luni
 test resources directory.
diff --git a/luni/src/test/etc/loading-test2-jar/README.txt b/luni/src/test/etc/loading-test2-jar/README.txt
index e1d8bbb..87aaa82 100644
--- a/luni/src/test/etc/loading-test2-jar/README.txt
+++ b/luni/src/test/etc/loading-test2-jar/README.txt
@@ -3,6 +3,6 @@
 files are used for testing the various class loaders.
 
 The Android build system doesn't support dynamically producing
-resources in any sane way. To update the resource, use the script
+resources in any practical way. To update the resource, use the script
 build.sh in this directory, which copies resulting files into the luni
 test resources directory.
diff --git a/luni/src/test/filesystems/Android.bp b/luni/src/test/filesystems/Android.bp
index 83cb936..3910028 100644
--- a/luni/src/test/filesystems/Android.bp
+++ b/luni/src/test/filesystems/Android.bp
@@ -16,6 +16,14 @@
 //
 // Included as a resource by //libcore:core-tests and loaded into its own
 // class loader by libcore.java.nio.file.FileSystemsTest.
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_luni_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_luni_license"],
+}
+
 java_library {
     name: "filesystemstest",
     visibility: [
diff --git a/luni/src/test/java/libcore/android/system/OsTest.java b/luni/src/test/java/libcore/android/system/OsTest.java
old mode 100644
new mode 100755
index 35019db..56487a7
--- a/luni/src/test/java/libcore/android/system/OsTest.java
+++ b/luni/src/test/java/libcore/android/system/OsTest.java
@@ -22,11 +22,14 @@
 import android.system.Os;
 import android.system.OsConstants;
 import android.system.PacketSocketAddress;
+import android.system.StructCmsghdr;
+import android.system.StructMsghdr;
 import android.system.StructRlimit;
 import android.system.StructStat;
 import android.system.StructTimeval;
 import android.system.StructUcred;
 import android.system.UnixSocketAddress;
+import android.system.VmSocketAddress;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -45,23 +48,40 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.atomic.AtomicReference;
-
-import junit.framework.TestCase;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import libcore.io.IoUtils;
 import libcore.testing.io.TestIoUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import static android.system.OsConstants.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
 
-public class OsTest extends TestCase {
+@RunWith(JUnit4.class)
+public class OsTest {
 
+    @Test
     public void testIsSocket() throws Exception {
         File f = new File("/dev/null");
         FileInputStream fis = new FileInputStream(f);
@@ -73,6 +93,7 @@
         s.close();
     }
 
+    @Test
     public void testFcntlInt() throws Exception {
         File f = File.createTempFile("OsTest", "tst");
         FileInputStream fis = null;
@@ -87,6 +108,7 @@
         }
     }
 
+    @Test
     public void testFcntlInt_udpSocket() throws Exception {
         final FileDescriptor fd = Os.socket(AF_INET, SOCK_DGRAM, 0);
         try {
@@ -104,6 +126,7 @@
         }
     }
 
+    @Test
     public void testFcntlInt_invalidCmd() throws Exception {
         final FileDescriptor fd = Os.socket(AF_INET, SOCK_DGRAM, 0);
         try {
@@ -117,7 +140,8 @@
         }
     }
 
-    public void testFcntlInt_nullFd() throws Exception {
+    @Test
+    public void testFcntlInt_nullFd() {
         try {
             Os.fcntlInt(null, F_SETFL, O_NONBLOCK);
             fail("Expected failure due to null file descriptor");
@@ -126,17 +150,20 @@
         }
     }
 
+    @Test
     public void testUnixDomainSockets_in_file_system() throws Exception {
         String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
         new File(path).delete();
         checkUnixDomainSocket(UnixSocketAddress.createFileSystem(path), false);
     }
 
+    @Test
     public void testUnixDomainSocket_abstract_name() throws Exception {
         // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
         checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
     }
 
+    @Test
     public void testUnixDomainSocket_unnamed() throws Exception {
         final FileDescriptor fd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
         // unix(7) says an unbound socket is unnamed.
@@ -170,8 +197,8 @@
                     byte[] request = new byte[256];
                     Os.read(clientFd, request, 0, request.length);
 
-                    String s = new String(request, "UTF-8");
-                    byte[] response = s.toUpperCase(Locale.ROOT).getBytes("UTF-8");
+                    String s = new String(request, StandardCharsets.UTF_8);
+                    byte[] response = s.toUpperCase(Locale.ROOT).getBytes(StandardCharsets.UTF_8);
                     Os.write(clientFd, response, 0, response.length);
 
                     Os.close(clientFd);
@@ -189,13 +216,13 @@
 
         String string = "hello, world!";
 
-        byte[] request = string.getBytes("UTF-8");
+        byte[] request = string.getBytes(StandardCharsets.UTF_8);
         assertEquals(request.length, Os.write(clientFd, request, 0, request.length));
 
         byte[] response = new byte[request.length];
         assertEquals(response.length, Os.read(clientFd, response, 0, response.length));
 
-        assertEquals(string.toUpperCase(Locale.ROOT), new String(response, "UTF-8"));
+        assertEquals(string.toUpperCase(Locale.ROOT), new String(response, StandardCharsets.UTF_8));
 
         Os.close(clientFd);
     }
@@ -221,16 +248,17 @@
         checkNoName((UnixSocketAddress) Os.getsockname(fd));
     }
 
-    public void test_strsignal() throws Exception {
+    @Test
+    public void test_strsignal() {
         assertEquals("Killed", Os.strsignal(9));
         assertEquals("Unknown signal -1", Os.strsignal(-1));
     }
 
+    @Test
     public void test_byteBufferPositions_write_pwrite() throws Exception {
         FileOutputStream fos = new FileOutputStream(new File("/dev/null"));
         FileDescriptor fd = fos.getFD();
-        final byte[] contents = new String("goodbye, cruel world")
-                .getBytes(StandardCharsets.US_ASCII);
+        final byte[] contents = "goodbye, cruel world".getBytes(StandardCharsets.US_ASCII);
         ByteBuffer byteBuffer = ByteBuffer.wrap(contents);
 
         byteBuffer.position(0);
@@ -256,6 +284,7 @@
         fos.close();
     }
 
+    @Test
     public void test_byteBufferPositions_read_pread() throws Exception {
         FileInputStream fis = new FileInputStream(new File("/dev/zero"));
         FileDescriptor fd = fis.getFD();
@@ -284,7 +313,7 @@
         fis.close();
     }
 
-    static void checkByteBufferPositions_sendto_recvfrom(
+    private static void checkByteBufferPositions_sendto_recvfrom(
             int family, InetAddress loopback) throws Exception {
         final FileDescriptor serverFd = Os.socket(family, SOCK_STREAM, 0);
         Os.bind(serverFd, loopback, 0);
@@ -292,29 +321,27 @@
 
         InetSocketAddress address = (InetSocketAddress) Os.getsockname(serverFd);
 
-        final Thread server = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    InetSocketAddress peerAddress = new InetSocketAddress();
-                    FileDescriptor clientFd = Os.accept(serverFd, peerAddress);
+        final Thread server = new Thread(() -> {
+            try {
+                InetSocketAddress peerAddress = new InetSocketAddress();
+                FileDescriptor clientFd = Os.accept(serverFd, peerAddress);
 
-                    // Attempt to receive a maximum of 24 bytes from the client, and then
-                    // close the connection.
-                    ByteBuffer buffer = ByteBuffer.allocate(16);
-                    int received = Os.recvfrom(clientFd, buffer, 0, null);
-                    assertTrue(received > 0);
-                    assertEquals(received, buffer.position());
+                // Attempt to receive a maximum of 24 bytes from the client, and then
+                // close the connection.
+                ByteBuffer buffer = ByteBuffer.allocate(16);
+                int received = Os.recvfrom(clientFd, buffer, 0, null);
+                assertTrue(received > 0);
+                assertEquals(received, buffer.position());
 
-                    ByteBuffer buffer2 = ByteBuffer.allocate(16);
-                    buffer2.position(8);
-                    received = Os.recvfrom(clientFd, buffer2, 0, null);
-                    assertTrue(received > 0);
-                    assertEquals(received + 8, buffer.position());
+                ByteBuffer buffer2 = ByteBuffer.allocate(16);
+                buffer2.position(8);
+                received = Os.recvfrom(clientFd, buffer2, 0, null);
+                assertTrue(received > 0);
+                assertEquals(received + 8, buffer.position());
 
-                    Os.close(clientFd);
-                } catch (Exception ex) {
-                    throw new RuntimeException(ex);
-                }
+                Os.close(clientFd);
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
             }
         });
 
@@ -344,9 +371,8 @@
         Os.close(clientFd);
     }
 
-    interface ExceptionalRunnable {
-
-        public void run() throws Exception;
+    private interface ExceptionalRunnable {
+        void run() throws Exception;
     }
 
     /**
@@ -375,7 +401,7 @@
     }
 
     private static void expectBindException(FileDescriptor socket, SocketAddress addr,
-            Class exClass, Integer expectedErrno) {
+            Class<? extends Exception> exClass, Integer expectedErrno) {
         String msg = String.format("bind(%s, %s)", socket, addr);
         expectException(() -> {
             Os.bind(socket, addr);
@@ -383,7 +409,7 @@
     }
 
     private static void expectConnectException(FileDescriptor socket, SocketAddress addr,
-            Class exClass, Integer expectedErrno) {
+            Class<? extends Exception> exClass, Integer expectedErrno) {
         String msg = String.format("connect(%s, %s)", socket, addr);
         expectException(() -> {
             Os.connect(socket, addr);
@@ -391,13 +417,13 @@
     }
 
     private static void expectSendtoException(FileDescriptor socket, SocketAddress addr,
-            Class exClass, Integer expectedErrno) {
+        Integer expectedErrno) {
         String msg = String.format("sendto(%s, %s)", socket, addr);
         byte[] packet = new byte[42];
         expectException(() -> {
                     Os.sendto(socket, packet, 0, packet.length, 0, addr);
                 },
-                exClass, expectedErrno, msg);
+            ErrnoException.class, expectedErrno, msg);
     }
 
     private static void expectBindConnectSendtoSuccess(FileDescriptor socket, String socketDesc,
@@ -422,7 +448,7 @@
 
                     assertEquals(addrISA.getAddress(), socknameISA.getAddress());
                     assertEquals(0, addrISA.getPort());
-                    assertFalse(0 == socknameISA.getPort());
+                    assertNotEquals(0, socknameISA.getPort());
                     addr = socknameISA;
                 }
 
@@ -432,8 +458,9 @@
                 Os.sendto(socket, packet, 0, packet.length, 0, addr);
                 // UNIX and IP sockets return different errors for this operation, so we can't check
                 // errno.
-                expectSendtoException(socket, null, ErrnoException.class, null);
-                expectSendtoException(null, null, ErrnoException.class, EBADF);
+                expectSendtoException(socket, null, null);
+                expectSendtoException(null, null, EBADF);
+                expectSendtoException(null, addr, EBADF);
 
                 // Expect that connect throws when any of its arguments are null.
                 expectConnectException(null, addr, ErrnoException.class, EBADF);
@@ -512,6 +539,7 @@
         return Os.socket(AF_UNIX, SOCK_DGRAM, 0);
     }
 
+    @Test
     public void testCrossFamilyBindConnectSendto() throws Exception {
         SocketAddress addrIpv4 = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0);
         SocketAddress addrIpv6 = new InetSocketAddress(InetAddress.getByName("::1"), 0);
@@ -537,6 +565,7 @@
         expectBindConnectSendtoSuccess(makeUnixSocket(), "unix", addrUnix);
     }
 
+    @Test
     public void testUnknownSocketAddressSubclass() throws Exception {
         class MySocketAddress extends SocketAddress {
 
@@ -575,6 +604,7 @@
         }
     }
 
+    @Test
     public void test_NetlinkSocket() throws Exception {
         FileDescriptor nlSocket = Os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
         try {
@@ -597,8 +627,10 @@
     // This test is excluded from CTS via the knownfailures.txt because it requires extra
     // permissions not available in CTS. To run it you have to use an -eng build and use a tool like
     // vogar that runs the Android runtime as a privileged user.
+    @Test
     public void test_PacketSocketAddress() throws Exception {
         NetworkInterface lo = NetworkInterface.getByName("lo");
+        assertNotNull(lo);
         FileDescriptor fd = Os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
         PacketSocketAddress addr =
                 new PacketSocketAddress(ETH_P_IPV6, lo.getIndex(), null /* sll_addr */);
@@ -612,6 +644,7 @@
 
         // The loopback address is ETH_ALEN bytes long and is all zeros.
         // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
+        assertNotNull(bound.sll_addr);
         assertEquals(6, bound.sll_addr.length);
         for (int i = 0; i < 6; i++) {
             assertEquals(0, bound.sll_addr[i]);
@@ -661,18 +694,101 @@
         Os.close(fd);
     }
 
+    @Test
+    public void test_VmSocketAddress() {
+        try {
+            final VmSocketAddress addr = new VmSocketAddress(111, 222);
+            assertEquals(111, addr.getSvmPort());
+            assertEquals(222, addr.getSvmCid());
+        } catch (UnsupportedOperationException ignore) {
+            assumeNoException(ignore);  // the platform does not support virtio-vsock
+        }
+    }
+
+    private static Thread createVmSocketEchoServer(final FileDescriptor serverFd) {
+        return new Thread(new Runnable() {
+            public void run() {
+                final VmSocketAddress peer =
+                    new VmSocketAddress(VMADDR_PORT_ANY, VMADDR_CID_ANY);
+
+                try {
+                    final FileDescriptor clientFd = Os.accept(serverFd, peer);
+                    try {
+                        final byte[] requestBuf = new byte[256];
+                        final int len = Os.read(clientFd, requestBuf, 0, requestBuf.length);
+                        final String request =
+                            new String(requestBuf, 0, len, StandardCharsets.UTF_8);
+                        final byte[] responseBuf =
+                            request.toUpperCase(Locale.ROOT).getBytes(StandardCharsets.UTF_8);
+                        Os.write(clientFd, responseBuf, 0, responseBuf.length);
+                    } finally {
+                        Os.close(clientFd);
+                    }
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+    }
+
+    @Test
+    public void test_VmSocket() throws Exception {
+        try {
+            final VmSocketAddress serverAddr = new VmSocketAddress(12345, VMADDR_CID_LOCAL);
+
+            final FileDescriptor serverFd = Os.socket(AF_VSOCK, SOCK_STREAM, 0);
+
+            try {
+                Os.bind(serverFd, serverAddr);
+                Os.listen(serverFd, 3);
+
+                final Thread server = createVmSocketEchoServer(serverFd);
+                server.start();
+
+                final FileDescriptor clientFd = Os.socket(AF_VSOCK, SOCK_STREAM, 0);
+                try {
+                    Os.connect(clientFd, serverAddr);
+
+                    final String request = "hello, world!";
+                    final byte[] requestBuf = request.getBytes(StandardCharsets.UTF_8);
+
+                    assertEquals(requestBuf.length,
+                                 Os.write(clientFd, requestBuf, 0, requestBuf.length));
+
+                    final byte[] responseBuf = new byte[requestBuf.length];
+                    assertEquals(responseBuf.length,
+                                 Os.read(clientFd, responseBuf, 0, responseBuf.length));
+
+                    final String response = new String(responseBuf, StandardCharsets.UTF_8);
+
+                    assertEquals(request.toUpperCase(Locale.ROOT), response);
+                } finally {
+                    Os.close(clientFd);
+                }
+            } finally {
+                Os.close(serverFd);
+            }
+        } catch (UnsupportedOperationException ignore) {
+            assumeNoException(ignore);  // the platform does not support virtio-vsock
+        } catch (ErrnoException e) {
+            // the platform does not support vsock
+            assumeTrue(e.errno != EAFNOSUPPORT && e.errno != EACCES);
+            throw e;
+        }
+    }
+
     private static byte[] getIPv6AddressBytesAtOffset(byte[] packet, int offsetIndex) {
         byte[] address = new byte[16];
-        for (int i = 0; i < 16; i++) {
-            address[i] = packet[i + offsetIndex];
-        }
+        System.arraycopy(packet, offsetIndex, address, 0, 16);
         return address;
     }
 
+    @Test
     public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
         checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
     }
 
+    @Test
     public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
         checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
     }
@@ -685,7 +801,8 @@
 
         InetSocketAddress to = ((InetSocketAddress) Os.getsockname(recvFd));
         FileDescriptor sendFd = Os.socket(family, SOCK_DGRAM, 0);
-        byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
+        byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes(
+            StandardCharsets.UTF_8);
         int len = msg.length;
 
         assertEquals(len, Os.sendto(sendFd, msg, 0, len, 0, to));
@@ -695,14 +812,273 @@
         assertEquals(loopback, from.getAddress());
     }
 
+    @Test
     public void test_sendtoSocketAddress_af_inet() throws Exception {
         checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
     }
 
+    @Test
     public void test_sendtoSocketAddress_af_inet6() throws Exception {
         checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
     }
 
+    /*
+     * Test case for sendmsg with/without GSO in loopback iface,
+     * recvmsg/gro would not happen since in loopback
+     */
+    private void checkSendmsgSocketAddress(int family, InetSocketAddress loopbackAddr,
+            StructMsghdr sendmsgHdr, StructMsghdr recvmsgHdr, int sendSize) throws Exception {
+
+        FileDescriptor sendFd = Os.socket(family, SOCK_DGRAM, 0);
+        FileDescriptor recvFd = Os.socket(family, SOCK_DGRAM, 0);
+        int rc = 0;
+
+        //recvmsg cleanup data
+        if (loopbackAddr.getAddress() instanceof Inet6Address) {
+            Os.bind(recvFd, Inet6Address.ANY, loopbackAddr.getPort());
+        } else {
+            Os.bind(recvFd, Inet4Address.ANY, loopbackAddr.getPort());
+        }
+
+        StructTimeval tv = StructTimeval.fromMillis(20);
+        Os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+        Os.setsockoptInt(recvFd, IPPROTO_UDP, UDP_GRO, 1); //enable GRO
+        Os.setsockoptInt(recvFd, SOL_SOCKET, SO_RCVBUF, 1024 * 1024);
+
+        try {
+            assertEquals(sendSize, Os.sendmsg(sendFd, sendmsgHdr, 0));
+            rc = 0;
+            do {
+                int temp_rc = Os.recvmsg(recvFd, recvmsgHdr, OsConstants.MSG_TRUNC);
+                rc += temp_rc;
+                if (recvmsgHdr.msg_control != null && recvmsgHdr.msg_control.length > 0) {
+                    byte[] sendCmsgByte = sendmsgHdr.msg_control[0].cmsg_data;
+                    byte[] recvCmsgByte = recvmsgHdr.msg_control[0].cmsg_data;
+                    /* Note:
+                     * GSO: is set with Short(2Byte) values;
+                     * GRO: IP stack return with Int(4Bytes) value;
+                     */
+                    assertEquals(
+                            ByteBuffer.wrap(sendCmsgByte).order(
+                                    ByteOrder.nativeOrder()).getShort(0),
+                            ByteBuffer.wrap(recvCmsgByte).order(
+                                    ByteOrder.nativeOrder()).getInt(0));
+                }
+
+                recvmsgHdr = new StructMsghdr(recvmsgHdr.msg_name, recvmsgHdr.msg_iov,
+                                              null,
+                                              recvmsgHdr.msg_flags);
+            }while(rc < sendSize);
+        } finally {
+            Os.close(sendFd);
+            Os.close(recvFd);
+        }
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_4K() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg/recvmsg with 1*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArrayRecv[0] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet6_4K() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("::1"), 10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg/recvmsg with 1*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArrayRecv[0] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET6, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet6_4K_directBuffer() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg/recvmsg with 1*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocateDirect(4096); // DirectBuffer
+        bufferArrayRecv[0] = ByteBuffer.allocateDirect(4096); // DirectBuffer
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET6, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_recvparts() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg with 4*4K ByteBuffer, recv with 1*4K ByteBuffer(already with MSG_TRUNC option)
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+        bufferArrayRecv[0] = ByteBuffer.allocate(4096); //receive only part of data
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_reciveall() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        // Create sendmsg/recvmsg with 4*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(), bufferArray, null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_receiveall_without_recv_msgname() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        // Create sendmsg/recvmsg with 4*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, cmsg, 0);
+        // msg_name is unnecessary.
+        StructMsghdr recvmsgHdr = new StructMsghdr(null, bufferArray, null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_without_send_msgcontrl() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+
+        // Create sendmsg/recvmsg with 4*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+
+        // GSO will not happen without msgcontrol.
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, null, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(null, bufferArray, null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_abnormal() throws Exception {
+        //sendmsg socket set
+        InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                          10234);
+        FileDescriptor sendFd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(8192);
+
+        try {
+            StructMsghdr msgHdr = new StructMsghdr(address, null, null, 0);
+            Os.sendmsg(sendFd, msgHdr, 0);
+            fail("Expected NullPointerException due to invalid StructMsghdr.msg_iov(NULL)");
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            StructMsghdr msgHdr = new StructMsghdr(null, bufferArray, null, 0);
+            Os.sendmsg(sendFd, msgHdr, 0);
+            fail("Expected ErrnoException due to invalid StructMsghdr.msg_name(NULL)");
+        } catch (ErrnoException expected) {
+            assertEquals("Expected EDESTADDRREQ binding IPv4 socket to ::", EDESTADDRREQ,
+                    expected.errno);
+        }
+
+    }
+
+    @Test
     public void test_socketFamilies() throws Exception {
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
         Os.bind(fd, InetAddress.getByName("::"), 0);
@@ -727,11 +1103,6 @@
         }
     }
 
-    private static void assertArrayEquals(byte[] expected, byte[] actual) {
-        assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
-                Arrays.equals(expected, actual));
-    }
-
     private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
             byte type, byte responseType, boolean useSendto) throws Exception {
         int len = packet.length;
@@ -753,12 +1124,13 @@
         assertEquals(received[5], (byte) (icmpId & 0xff));
 
         received = Arrays.copyOf(received, len);
-        received[0] = (byte) type;
+        received[0] = type;
         received[2] = received[3] = 0;  // Checksum.
         received[4] = received[5] = 0;  // ICMP ID.
         assertArrayEquals(packet, received);
     }
 
+    @Test
     public void test_socketPing() throws Exception {
         final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
         final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
@@ -777,6 +1149,7 @@
         checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
     }
 
+    @Test
     public void test_Ipv4Fallback() throws Exception {
         // This number of iterations gives a ~60% chance of creating the conditions that caused
         // http://b/23088314 without making test times too long. On a hammerhead running MRZ37C
@@ -794,6 +1167,7 @@
         }
     }
 
+    @Test
     public void test_unlink() throws Exception {
         File f = File.createTempFile("OsTest", "tst");
         assertTrue(f.exists());
@@ -809,6 +1183,7 @@
     }
 
     // b/27294715
+    @Test
     public void test_recvfrom_concurrentShutdown() throws Exception {
         final FileDescriptor serverFd = Os.socket(AF_INET, SOCK_DGRAM, 0);
         Os.bind(serverFd, InetAddress.getByName("127.0.0.1"), 0);
@@ -816,22 +1191,20 @@
         StructTimeval tv = StructTimeval.fromMillis(4000);
         Os.setsockoptTimeval(serverFd, SOL_SOCKET, SO_RCVTIMEO, tv);
 
-        final AtomicReference<Exception> killerThreadException = new AtomicReference<Exception>(
-                null);
-        final Thread killer = new Thread(new Runnable() {
-            public void run() {
+        final AtomicReference<Exception> killerThreadException = new AtomicReference<>(
+            null);
+        final Thread killer = new Thread(() -> {
+            try {
+                Thread.sleep(2000);
                 try {
-                    Thread.sleep(2000);
-                    try {
-                        Os.shutdown(serverFd, SHUT_RDWR);
-                    } catch (ErrnoException expected) {
-                        if (OsConstants.ENOTCONN != expected.errno) {
-                            killerThreadException.set(expected);
-                        }
+                    Os.shutdown(serverFd, SHUT_RDWR);
+                } catch (ErrnoException expected) {
+                    if (OsConstants.ENOTCONN != expected.errno) {
+                        killerThreadException.set(expected);
                     }
-                } catch (Exception ex) {
-                    killerThreadException.set(ex);
                 }
+            } catch (Exception ex) {
+                killerThreadException.set(ex);
             }
         });
         killer.start();
@@ -839,13 +1212,14 @@
         ByteBuffer buffer = ByteBuffer.allocate(16);
         InetSocketAddress srcAddress = new InetSocketAddress();
         int received = Os.recvfrom(serverFd, buffer, 0, srcAddress);
-        assertTrue(received == 0);
+        assertEquals(0, received);
         Os.close(serverFd);
 
         killer.join();
         assertNull(killerThreadException.get());
     }
 
+    @Test
     public void test_xattr() throws Exception {
         final String NAME_TEST = "user.meow";
 
@@ -897,6 +1271,7 @@
         }
     }
 
+    @Test
     public void test_xattr_NPE() throws Exception {
         File file = File.createTempFile("xattr", "test");
         final String path = file.getAbsolutePath();
@@ -952,7 +1327,8 @@
         }
     }
 
-    public void test_xattr_Errno() throws Exception {
+    @Test
+    public void test_xattr_Errno() {
         final String NAME_TEST = "user.meow";
         final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
 
@@ -1014,6 +1390,7 @@
         }
     }
 
+    @Test
     public void test_realpath() throws Exception {
         File tmpDir = new File(System.getProperty("java.io.tmpdir"));
         // This is a chicken and egg problem. We have no way of knowing whether
@@ -1044,10 +1421,57 @@
         }
     }
 
+    private int[] getKernelVersion() {
+        // Example:
+        // 4.9.29-g958411d --> 4.9
+        String release = Os.uname().release;
+        Matcher m = Pattern.compile("^(\\d+)\\.(\\d+)").matcher(release);
+        assertTrue("No pattern in release string: " + release, m.find());
+        return new int[]{ Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)) };
+    }
+
+    private boolean kernelIsAtLeast(int major, int minor) {
+        int[] version = getKernelVersion();
+        return version[0] > major || (version[0] == major && version[1] >= minor);
+    }
+
+    @Test
+    public void test_socket_udpGro_setAndGet() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        assumeTrue(kernelIsAtLeast(5, 4));
+
+        final FileDescriptor fd = Os.socket(AF_INET6, SOCK_DGRAM, 0);
+        try {
+            final int setValue = 1;
+            Os.setsockoptInt(fd, IPPROTO_UDP, UDP_GRO, setValue);
+            // getsockopt(IPPROTO_UDP, UDP_GRO) is not implemented.
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    @Test
+    public void test_socket_udpGso_set() throws Exception {
+        // UDP GSO not required to be enabled on kernels prior to 4.19.
+        assumeTrue(kernelIsAtLeast(4, 19));
+
+        final FileDescriptor fd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+        try {
+            assertEquals(0, Os.getsockoptInt(fd, IPPROTO_UDP, UDP_SEGMENT));
+
+            final int setValue = 1452;
+            Os.setsockoptInt(fd, IPPROTO_UDP, UDP_SEGMENT, setValue);
+            assertEquals(setValue, Os.getsockoptInt(fd, IPPROTO_UDP, UDP_SEGMENT));
+        } finally {
+            Os.close(fd);
+        }
+    }
+
     /**
      * Tests that TCP_USER_TIMEOUT can be set on a TCP socket, but doesn't test
      * that it behaves as expected.
      */
+    @Test
     public void test_socket_tcpUserTimeout_setAndGet() throws Exception {
         final FileDescriptor fd = Os.socket(AF_INET, SOCK_STREAM, 0);
         try {
@@ -1068,6 +1492,7 @@
         }
     }
 
+    @Test
     public void test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket() throws Exception {
         final FileDescriptor fd = Os.socket(AF_INET, SOCK_DGRAM, 0);
         try {
@@ -1081,6 +1506,7 @@
         }
     }
 
+    @Test
     public void test_socket_sockoptTimeval_readWrite() throws Exception {
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
         try {
@@ -1102,9 +1528,13 @@
         }
     }
 
+    @Test
     public void test_socket_setSockoptTimeval_effective() throws Exception {
-        int timeoutValueMillis = 50;
-        int allowedTimeoutMillis = 500;
+        // b/176104885 Older devices can return a few ms early, add a tolerance for them
+        long timeoutTolerance = kernelIsAtLeast(3, 18) ? 0 : 10;
+
+        int timeoutValueMillis = 250;
+        int allowedTimeoutMillis = 3000;
 
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
         try {
@@ -1116,20 +1546,27 @@
             long startTime = System.nanoTime();
             expectException(() -> Os.read(fd, request, 0, request.length),
                     ErrnoException.class, EAGAIN, "Expected timeout");
-            long endTime = System.nanoTime();
-            assertTrue(Duration.ofNanos(endTime - startTime).toMillis() < allowedTimeoutMillis);
+            long durationMillis = Duration.ofNanos(System.nanoTime() - startTime).toMillis();
+            assertTrue("Timeout of " + timeoutValueMillis + "ms returned after "
+                    + durationMillis +"ms",
+                durationMillis >= timeoutValueMillis - timeoutTolerance);
+            assertTrue("Timeout of " + timeoutValueMillis + "ms failed to return within "
+                    + allowedTimeoutMillis  + "ms",
+                durationMillis < allowedTimeoutMillis);
         } finally {
             Os.close(fd);
         }
     }
 
-    public void test_socket_setSockoptTimeval_nullFd() throws Exception {
+    @Test
+    public void test_socket_setSockoptTimeval_nullFd() {
         StructTimeval tv = StructTimeval.fromMillis(500);
         expectException(
                 () -> Os.setsockoptTimeval(null, SOL_SOCKET, SO_RCVTIMEO, tv),
                 ErrnoException.class, EBADF, "setsockoptTimeval(null, ...)");
     }
 
+    @Test
     public void test_socket_setSockoptTimeval_fileFd() throws Exception {
         File testFile = createTempFile("test_socket_setSockoptTimeval_invalidFd", "");
         try (FileInputStream fis = new FileInputStream(testFile)) {
@@ -1142,6 +1579,7 @@
         }
     }
 
+    @Test
     public void test_socket_setSockoptTimeval_badFd() throws Exception {
         StructTimeval tv = StructTimeval.fromMillis(500);
         FileDescriptor invalidFd = Os.socket(AF_INET6, SOCK_STREAM, 0);
@@ -1152,6 +1590,7 @@
                 ErrnoException.class, EBADF, "setsockoptTimeval(<closed fd>, ...)");
     }
 
+    @Test
     public void test_socket_setSockoptTimeval_invalidLevel() throws Exception {
         StructTimeval tv = StructTimeval.fromMillis(500);
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
@@ -1165,6 +1604,7 @@
         }
     }
 
+    @Test
     public void test_socket_setSockoptTimeval_invalidOpt() throws Exception {
         StructTimeval tv = StructTimeval.fromMillis(500);
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
@@ -1178,6 +1618,7 @@
         }
     }
 
+    @Test
     public void test_socket_setSockoptTimeval_nullTimeVal() throws Exception {
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
         try {
@@ -1189,6 +1630,7 @@
         }
     }
 
+    @Test
     public void test_socket_getSockoptTimeval_invalidOption() throws Exception {
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
         try {
@@ -1201,19 +1643,22 @@
         }
     }
 
+    @Test
     public void test_if_nametoindex_if_indextoname() throws Exception {
-        List<NetworkInterface> nis = Collections.list(NetworkInterface.getNetworkInterfaces());
+        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+        assertNotNull(networkInterfaces);
+        List<NetworkInterface> nis = Collections.list(networkInterfaces);
 
         assertTrue(nis.size() > 0);
         for (NetworkInterface ni : nis) {
             int index = ni.getIndex();
             String name = ni.getName();
             assertEquals(index, Os.if_nametoindex(name));
-            assertTrue(Os.if_indextoname(index).equals(name));
+            assertEquals(Os.if_indextoname(index), name);
         }
 
         assertEquals(0, Os.if_nametoindex("this-interface-does-not-exist"));
-        assertEquals(null, Os.if_indextoname(-1000));
+        assertNull(Os.if_indextoname(-1000));
 
         try {
             Os.if_nametoindex(null);
@@ -1231,6 +1676,7 @@
         }
     }
 
+    @Test
     public void test_readlink() throws Exception {
         File path = new File(TestIoUtils.createTemporaryDirectory("test_readlink"), "symlink");
 
@@ -1241,17 +1687,18 @@
         // failure are still on 3.18, far from current). Given that we don't
         // really care here, just use 2048 instead. http://b/33306057.
         int size = 2048;
-        String xs = "";
+        StringBuilder xs = new StringBuilder();
         for (int i = 0; i < size - 1; ++i) {
-            xs += "x";
+            xs.append("x");
         }
 
-        Os.symlink(xs, path.getPath());
+        Os.symlink(xs.toString(), path.getPath());
 
-        assertEquals(xs, Os.readlink(path.getPath()));
+        assertEquals(xs.toString(), Os.readlink(path.getPath()));
     }
 
     // Address should be correctly set for empty packets. http://b/33481605
+    @Test
     public void test_recvfrom_EmptyPacket() throws Exception {
         try (DatagramSocket ds = new DatagramSocket();
              DatagramSocket srcSock = new DatagramSocket()) {
@@ -1267,6 +1714,7 @@
         }
     }
 
+    @Test
     public void test_fstat_times() throws Exception {
         File file = File.createTempFile("OsTest", "fstattest");
         FileOutputStream fos = new FileOutputStream(file);
@@ -1285,6 +1733,7 @@
         assertEquals(0, structStat1.st_atim.compareTo(structStat2.st_atim));
     }
 
+    @Test
     public void test_getrlimit() throws Exception {
         StructRlimit rlimit = Os.getrlimit(OsConstants.RLIMIT_NOFILE);
         // We can't really make any assertions about these values since they might vary from
@@ -1295,7 +1744,8 @@
     }
 
     // http://b/65051835
-    public void test_pipe2_errno() throws Exception {
+    @Test
+    public void test_pipe2_errno() {
         try {
             // flag=-1 is not a valid value for pip2, will EINVAL
             Os.pipe2(-1);
@@ -1305,7 +1755,8 @@
     }
 
     // http://b/65051835
-    public void test_sendfile_errno() throws Exception {
+    @Test
+    public void test_sendfile_errno() {
         try {
             // FileDescriptor.out is not open for input, will cause EBADF
             Int64Ref offset = new Int64Ref(10);
@@ -1315,6 +1766,7 @@
         }
     }
 
+    @Test
     public void test_sendfile_null() throws Exception {
         File in = createTempFile("test_sendfile_null", "Hello, world!");
         try {
@@ -1325,6 +1777,7 @@
         }
     }
 
+    @Test
     public void test_sendfile_offset() throws Exception {
         File in = createTempFile("test_sendfile_offset", "Hello, world!");
         try {
@@ -1364,6 +1817,7 @@
         return f;
     }
 
+    @Test
     public void test_odirect() throws Exception {
         File testFile = createTempFile("test_odirect", "");
         try {
@@ -1380,6 +1834,7 @@
         }
     }
 
+    @Test
     public void test_splice() throws Exception {
         FileDescriptor[] pipe = Os.pipe2(0);
         File in = createTempFile("splice1", "foobar");
@@ -1412,6 +1867,7 @@
         Os.close(pipe[1]);
     }
 
+    @Test
     public void test_splice_errors() throws Exception {
         File in = createTempFile("splice3", "");
         File out = createTempFile("splice4", "");
@@ -1452,6 +1908,7 @@
         Os.close(pipe[1]);
     }
 
+    @Test
     public void testCloseNullFileDescriptor() throws Exception {
         try {
             Os.close(null);
@@ -1460,6 +1917,7 @@
         }
     }
 
+    @Test
     public void testSocketpairNullFileDescriptor1() throws Exception {
         try {
             Os.socketpair(AF_UNIX, SOCK_STREAM, 0, null, new FileDescriptor());
@@ -1468,6 +1926,7 @@
         }
     }
 
+    @Test
     public void testSocketpairNullFileDescriptor2() throws Exception {
         try {
             Os.socketpair(AF_UNIX, SOCK_STREAM, 0, new FileDescriptor(), null);
@@ -1476,6 +1935,7 @@
         }
     }
 
+    @Test
     public void testSocketpairNullFileDescriptorBoth() throws Exception {
         try {
             Os.socketpair(AF_UNIX, SOCK_STREAM, 0, null, null);
@@ -1484,30 +1944,35 @@
         }
     }
 
+    @Test
     public void testInetPtonIpv4() {
         String srcAddress = "127.0.0.1";
         InetAddress inetAddress = Os.inet_pton(AF_INET, srcAddress);
         assertEquals(srcAddress, inetAddress.getHostAddress());
     }
 
+    @Test
     public void testInetPtonIpv6() {
         String srcAddress = "1123:4567:89ab:cdef:fedc:ba98:7654:3210";
         InetAddress inetAddress = Os.inet_pton(AF_INET6, srcAddress);
         assertEquals(srcAddress, inetAddress.getHostAddress());
     }
 
+    @Test
     public void testInetPtonInvalidFamily() {
         String srcAddress = "127.0.0.1";
         InetAddress inetAddress = Os.inet_pton(AF_UNIX, srcAddress);
         assertNull(inetAddress);
     }
 
+    @Test
     public void testInetPtonWrongFamily() {
         String srcAddress = "127.0.0.1";
         InetAddress inetAddress = Os.inet_pton(AF_INET6, srcAddress);
         assertNull(inetAddress);
     }
 
+    @Test
     public void testInetPtonInvalidData() {
         String srcAddress = "10.1";
         InetAddress inetAddress = Os.inet_pton(AF_INET, srcAddress);
@@ -1517,6 +1982,7 @@
     /**
      * Verifies the {@link OsConstants#MAP_ANONYMOUS}.
      */
+    @Test
     public void testMapAnonymous() throws Exception {
         final long size = 4096;
         final long address = Os.mmap(0, size, PROT_READ,
@@ -1525,6 +1991,7 @@
         Os.munmap(address, size);
     }
 
+    @Test
     public void testMemfdCreate() throws Exception {
         FileDescriptor fd = null;
         try {
@@ -1553,6 +2020,7 @@
         }
     }
 
+    @Test
     public void testMemfdCreateFlags() throws Exception {
         FileDescriptor fd = null;
 
@@ -1562,8 +2030,8 @@
             assertNotNull(fd);
             assertTrue(fd.valid());
             int flags = Os.fcntlVoid(fd, F_GETFD);
-            assertTrue("Expected flags to not include " + FD_CLOEXEC + ", actual value: " + flags,
-                    0 == (flags & FD_CLOEXEC));
+            assertEquals("Expected flags to not include " + FD_CLOEXEC + ", actual value: " + flags,
+                0, (flags & FD_CLOEXEC));
         } finally {
             if (fd != null) {
                 Os.close(fd);
@@ -1585,7 +2053,8 @@
         }
     }
 
-    public void testMemfdCreateErrno() throws Exception {
+    @Test
+    public void testMemfdCreateErrno() {
         expectException(() -> Os.memfd_create(null, 0), NullPointerException.class, null,
                 "memfd_create(null, 0)");
 
diff --git a/luni/src/test/java/libcore/android/system/StructTimevalTest.java b/luni/src/test/java/libcore/android/system/StructTimevalTest.java
index f427980..b5ca9e5 100644
--- a/luni/src/test/java/libcore/android/system/StructTimevalTest.java
+++ b/luni/src/test/java/libcore/android/system/StructTimevalTest.java
@@ -42,6 +42,8 @@
         // An array of { testMillisValue, expectedSeconds, expectedMicros }
         return new Object[][] {
                 { 0L, 0L, 0L },
+                { 1000L, 1L, 0L },
+                { -1000L, -1L, 0L },
 
                 // +ve and -ve cases close to zero seconds.
                 { 23L, 0L, 23L * US_PER_MS /* 23000 */ },
diff --git a/luni/src/test/java/libcore/dalvik/system/ClassLoaderTestSupport.java b/luni/src/test/java/libcore/dalvik/system/ClassLoaderTestSupport.java
index 4adb4b6..27f5d7c 100644
--- a/luni/src/test/java/libcore/dalvik/system/ClassLoaderTestSupport.java
+++ b/luni/src/test/java/libcore/dalvik/system/ClassLoaderTestSupport.java
@@ -58,15 +58,17 @@
             return;
         }
 
-        File[] files = dir.listFiles();
-        for (File file : files) {
-            if (file.isDirectory()) {
-                cleanUpDir(file);
-            } else {
-                assertTrue(file.delete());
+        // The runtime may create files in the background. Loop until we remove all such files.
+        while (!dir.delete()) {
+            File[] files = dir.listFiles();
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    cleanUpDir(file);
+                } else {
+                    assertTrue(file.delete());
+                }
             }
         }
-        assertTrue(dir.delete());
     }
 
     /**
diff --git a/luni/src/test/java/libcore/highmemorytest/java/text/DateFormatTest.java b/luni/src/test/java/libcore/highmemorytest/java/text/DateFormatTest.java
new file mode 100644
index 0000000..d611807
--- /dev/null
+++ b/luni/src/test/java/libcore/highmemorytest/java/text/DateFormatTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.highmemorytest.java.text;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Tests {@link DateFormat} but runs in a separate java package because loading data in all locales
+ * creates a lot of garbage / permanent heap growth in ICU4J and causes gcstress test failing
+ * in ART test environment. See http://b/161420453. No known users/apps load all locale data like
+ * this test.
+ */
+@RunWith(Parameterized.class)
+public class DateFormatTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Locale[] getAllLocales() {
+        return Locale.getAvailableLocales();
+    }
+
+    @Parameterized.Parameter(0)
+    public Locale locale;
+
+    // 1 January 2022 00:00:00 GMT+00:00
+    private static final Date TEST_DATE = new Date(1640995200000L);
+
+    /**
+     * Test {@link DateFormat#format(Date)} does not crash on available locales.
+     */
+    @Test
+    public void test_format_allLocales() {
+        for (int formatStyle = DateFormat.FULL; formatStyle <= DateFormat.SHORT;
+                formatStyle++) {
+            try {
+                assertNonEmpty(DateFormat.getDateInstance(formatStyle, locale).format(TEST_DATE));
+                assertNonEmpty(DateFormat.getTimeInstance(formatStyle, locale).format(TEST_DATE));
+                assertNonEmpty(DateFormat.getDateTimeInstance(formatStyle, formatStyle, locale)
+                        .format(TEST_DATE));
+            } catch (RuntimeException cause) {
+                throw new RuntimeException("locale:" + locale +
+                        " formatStyle:" + formatStyle, cause);
+            }
+        }
+    }
+
+    /**
+     * Test {@link SimpleDateFormat#toPattern()} contains only supported symbols.
+     */
+    @Test
+    public void test_toPattern_allLocales() {
+        for (int formatStyle = DateFormat.FULL; formatStyle <= DateFormat.SHORT;
+                formatStyle++) {
+            try {
+                assertSupportedSymbols(DateFormat.getDateInstance(formatStyle, locale), locale);
+                assertSupportedSymbols(DateFormat.getTimeInstance(formatStyle, locale), locale);
+                assertSupportedSymbols(DateFormat.getDateTimeInstance(
+                        formatStyle, formatStyle, locale), locale);
+            } catch (RuntimeException cause) {
+                throw new RuntimeException("locale:" + locale +
+                        " formatStyle:" + formatStyle, cause);
+            }
+        }
+    }
+
+    private static final Set<Character> SUPPORTED_SYMBOLS = "GyYMLwWDdFEuaHkKhmsSzZXLc".chars()
+            .mapToObj(c -> (char)c)
+            .collect(Collectors.toSet());
+
+    private static void assertSupportedSymbols(DateFormat dateFormat, Locale locale) {
+        SimpleDateFormat simpleDateFormat = (SimpleDateFormat) dateFormat;
+        String pattern = simpleDateFormat.toPattern();
+        // The string in the quotation is not interpreted.
+        boolean inQuotation = false;
+        for (int i = 0; i < pattern.length(); i++) {
+            char curr = pattern.charAt(i);
+            if (curr == '\'') {
+                inQuotation = !inQuotation;
+                continue;
+            }
+            if (inQuotation) {
+                continue;
+            }
+
+            if ((curr >= 'a' && curr <= 'z') || (curr >= 'A' && curr <= 'Z')) { // ASCII alphabets
+                assertTrue("Locale:" + locale + " Pattern:" + pattern + " has unsupported symbol "
+                                + curr, SUPPORTED_SYMBOLS.contains(curr));
+            }
+        }
+    }
+
+    private static void assertNonEmpty(String s) {
+        assertNotNull(s);
+        assertTrue(s.length() > 0);
+    }
+}
diff --git a/luni/src/test/java/libcore/highmemorytest/java/text/DecimalFormatTest.java b/luni/src/test/java/libcore/highmemorytest/java/text/DecimalFormatTest.java
new file mode 100644
index 0000000..9baf972
--- /dev/null
+++ b/luni/src/test/java/libcore/highmemorytest/java/text/DecimalFormatTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 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.highmemorytest.java.text;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Currency;
+import java.util.Locale;
+
+/**
+ * Runs in a separate java package because loading data in all locales creates a lot of garbage /
+ * permanent heap growth in ICU4J and causes gcstress test failing in ART test environment.
+ * See http://b/161420453.
+ */
+@RunWith(Parameterized.class)
+public class DecimalFormatTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Locale[] getAllLocales() {
+        return Locale.getAvailableLocales();
+    }
+
+    @Parameterized.Parameter(0)
+    public Locale locale;
+
+    /**
+     * Test no extra spacing between currency symbol and the numeric amount
+     */
+    @Test
+    public void testCurrencySymbolSpacing() {
+        Currency currency = Currency.getInstance(Locale.US);
+        for (Locale locale : Locale.getAvailableLocales()) {
+            DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
+            String formattedZero = new DecimalFormat("0", dfs).format(0);
+
+            assertCurrencyFormat("USD" + formattedZero, "\u00a4\u00a40", dfs, currency, locale);
+            assertCurrencyFormat(formattedZero + "USD", "0\u00a4\u00a4", dfs, currency, locale);
+            assertCurrencyFormat(currency.getSymbol(locale) + formattedZero, "\u00a40", dfs,
+                    currency, locale);
+            assertCurrencyFormat(formattedZero + currency.getSymbol(locale), "0\u00a4", dfs,
+                    currency, locale);
+        }
+    }
+
+    private static void assertCurrencyFormat(String expected, String pattern,
+            DecimalFormatSymbols dfs, Currency currency, Locale locale) {
+        DecimalFormat df = new DecimalFormat(pattern, dfs);
+        df.setCurrency(currency);
+        df.setMaximumFractionDigits(0);
+        assertEquals("Not formatted as expected with pattern " + pattern + " in locale " + locale,
+                expected, df.format(0));
+    }
+}
diff --git a/luni/src/test/java/libcore/highmemorytest/java/text/SimpleDateFormatTest.java b/luni/src/test/java/libcore/highmemorytest/java/text/SimpleDateFormatTest.java
new file mode 100644
index 0000000..63ec023
--- /dev/null
+++ b/luni/src/test/java/libcore/highmemorytest/java/text/SimpleDateFormatTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 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.highmemorytest.java.text;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Tests {@link SimpleDateFormat} but runs in a separate java package because loading data in all
+ * locales creates a lot of garbage / permanent heap growth in ICU4J and causes gcstress test
+ * failing in ART test environment. See http://b/161420453.
+ */
+@RunWith(Parameterized.class)
+public class SimpleDateFormatTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Locale[] getAllLocales() {
+        return Locale.getAvailableLocales();
+    }
+
+    @Parameterized.Parameter(0)
+    public Locale locale;
+
+    @Test
+    public void testLocales() {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzzz", locale);
+        sdf.format(new Date(0));
+    }
+}
diff --git a/luni/src/test/java/libcore/highmemorytest/java/time/format/DateTimeFormatterTest.java b/luni/src/test/java/libcore/highmemorytest/java/time/format/DateTimeFormatterTest.java
new file mode 100644
index 0000000..e2d28ec
--- /dev/null
+++ b/luni/src/test/java/libcore/highmemorytest/java/time/format/DateTimeFormatterTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 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.highmemorytest.java.time.format;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.chrono.Chronology;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.time.temporal.TemporalAccessor;
+import java.util.Locale;
+
+/**
+ * Additional tests for {@link DateTimeFormatter}. Runs in a separate java package because
+ * loading data in all locales creates a lot of garbage / permanent heap growth in ICU4J and
+ * causes gcstress test failing in ART test environment. See http://b/161420453.
+ *
+ * @see tck.java.time.format.TCKDateTimeFormatter
+ * @see test.java.time.format.TestDateTimeFormatter
+ * @see libcore.java.time.format.DateTimeFormatterTest
+ */
+@RunWith(Parameterized.class)
+public class DateTimeFormatterTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Locale[] getAllLocales() {
+        return Locale.getAvailableLocales();
+    }
+
+    @Parameterized.Parameter
+    public Locale locale;
+
+    // 1 January 2022 00:00:00 GMT+00:00
+    private static final Instant TEST_INSTANT = Instant.ofEpochSecond(1640995200L);
+
+    /**
+     * Test {@link DateTimeFormatter#format(TemporalAccessor)} does not crash on available locales.
+     */
+    @Test
+    public void test_format_allLocales() {
+        for (FormatStyle formatStyle : FormatStyle.values()) {
+            try {
+                DateTimeFormatter.ofLocalizedTime(formatStyle)
+                        .withLocale(locale)
+                        .withZone(ZoneOffset.UTC)
+                        .format(TEST_INSTANT);
+
+                DateTimeFormatter.ofLocalizedDate(formatStyle)
+                        .withLocale(locale)
+                        .withZone(ZoneOffset.UTC)
+                        .format(TEST_INSTANT);
+
+                DateTimeFormatter.ofLocalizedDateTime(formatStyle)
+                        .withLocale(locale)
+                        .withZone(ZoneOffset.UTC)
+                        .format(TEST_INSTANT);
+            } catch (RuntimeException cause) {
+                throw new RuntimeException("formatStyle:" + formatStyle.name(), cause);
+            }
+        }
+    }
+
+    /**
+     * Test {@link DateTimeFormatter#format(TemporalAccessor)} does not crash on available locales
+     * with all possible Chronologies.
+     */
+    @Test
+    public void test_format_allLocales_allChronologies() {
+        for (Chronology chronology : Chronology.getAvailableChronologies()) {
+            for (FormatStyle formatStyle : FormatStyle.values()) {
+                try {
+                    DateTimeFormatter.ofLocalizedTime(formatStyle)
+                            .withLocale(locale)
+                            .withChronology(chronology)
+                            .withZone(ZoneOffset.UTC)
+                            .format(TEST_INSTANT);
+
+                    DateTimeFormatter.ofLocalizedDate(formatStyle)
+                            .withLocale(locale)
+                            .withChronology(chronology)
+                            .withZone(ZoneOffset.UTC)
+                            .format(TEST_INSTANT);
+
+                    DateTimeFormatter.ofLocalizedDateTime(formatStyle)
+                            .withLocale(locale)
+                            .withChronology(chronology)
+                            .withZone(ZoneOffset.UTC)
+                            .format(TEST_INSTANT);
+                } catch (RuntimeException cause) {
+                    throw new RuntimeException("Chronology:" + chronology +
+                            " formatStyle:" + formatStyle.name(), cause);
+                }
+            }
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/highmemorytest/java/util/CalendarTest.java b/luni/src/test/java/libcore/highmemorytest/java/util/CalendarTest.java
new file mode 100644
index 0000000..036ed9f
--- /dev/null
+++ b/luni/src/test/java/libcore/highmemorytest/java/util/CalendarTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 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.highmemorytest.java.util;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+
+/**
+ * Runs in a separate java package because loading data in all locales creates a lot of garbage /
+ * permanent heap growth in ICU4J and causes gcstress test failing in ART test environment.
+ * See http://b/161420453.
+ */
+@RunWith(Parameterized.class)
+public class CalendarTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Locale[] getAllLocales() {
+        return Locale.getAvailableLocales();
+    }
+
+    @Parameterized.Parameter(0)
+    public Locale locale;
+
+    /**
+     * Ensures that Gregorian is the default Calendar for all Locales in Android. This is the
+     * historic behavior on Android; this test exists to avoid unintentional regressions.
+     * http://b/80294184
+     */
+    @Test
+    public void testAllDefaultCalendar_Gregorian() {
+        assertTrue("Default calendar should be Gregorian: " + locale,
+                Calendar.getInstance(locale) instanceof GregorianCalendar);
+    }
+
+}
diff --git a/luni/src/test/java/libcore/highmemorytest/java/util/CurrencyTest.java b/luni/src/test/java/libcore/highmemorytest/java/util/CurrencyTest.java
new file mode 100644
index 0000000..d1cbf5e
--- /dev/null
+++ b/luni/src/test/java/libcore/highmemorytest/java/util/CurrencyTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 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.highmemorytest.java.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Currency;
+import java.util.Locale;
+
+/**
+ * Runs in a separate java package because loading data in all locales creates a lot of garbage /
+ * permanent heap growth in ICU4J and causes gcstress test failing in ART test environment.
+ * See http://b/161420453.
+ */
+@RunWith(Parameterized.class)
+public class CurrencyTest {
+    @Parameterized.Parameters(name = "{0}")
+    public static Locale[] getAllLocales() {
+        return Locale.getAvailableLocales();
+    }
+
+    @Parameterized.Parameter(0)
+    public Locale locale;
+
+    @Test
+    public void test_currencyCodeIcuConsistency() {
+        Currency javaCurrency = getCurrency(locale);
+        if (javaCurrency == null) {
+            return;
+        }
+        assertEquals("Currency code is not consistent:" + locale,
+                android.icu.util.Currency.getInstance(locale).getCurrencyCode(),
+                javaCurrency.getCurrencyCode());
+    }
+
+    private static Currency getCurrency(Locale l) {
+        try {
+            return Currency.getInstance(l);
+        } catch (IllegalArgumentException e) {
+            // The locale could have no country or does not have currency for other reasons.
+            return null;
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/highmemorytest/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/highmemorytest/libcore/icu/LocaleDataTest.java
new file mode 100644
index 0000000..ab396f9
--- /dev/null
+++ b/luni/src/test/java/libcore/highmemorytest/libcore/icu/LocaleDataTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.highmemorytest.libcore.icu;
+
+import static org.junit.Assert.assertTrue;
+
+import libcore.icu.LocaleData;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Locale;
+
+@RunWith(Parameterized.class)
+public class LocaleDataTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Locale[] getAllLocales() {
+        return Locale.getAvailableLocales();
+    }
+
+    @Parameterized.Parameter(0)
+    public Locale locale;
+
+    @Test
+    public void testLongDateTimeFormat() {
+        LocaleData d = LocaleData.get(locale);
+        int dateLength = d.longDateFormat.length();
+        assertTrue("LocaleData.longDateFormat has zero length: " + dateLength, dateLength != 0);
+        int timeLength = d.longTimeFormat.length();
+        assertTrue("LocaleData.longTimeFormat has zero length: " + timeLength, timeLength != 0);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/awt/font/NumericShaperTest.java b/luni/src/test/java/libcore/java/awt/font/NumericShaperTest.java
new file mode 100644
index 0000000..0fedceb
--- /dev/null
+++ b/luni/src/test/java/libcore/java/awt/font/NumericShaperTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 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.awt.font;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.awt.font.NumericShaper;
+import java.awt.font.NumericShaper.Range;
+import java.util.EnumSet;
+
+@RunWith(JUnit4.class)
+public class NumericShaperTest {
+
+    @Test
+    public void testShape() {
+        NumericShaper ns = NumericShaper.getShaper(Range.ARABIC);
+        String input = "abc 123";
+        String expected = "abc \u0661\u0662\u0663";
+
+        char[] chars = input.toCharArray();
+        ns.shape(chars, 0, chars.length);
+        assertEquals(expected, new String(chars));
+
+        chars = input.toCharArray();
+        ns.shape(chars, 0, chars.length, NumericShaper.EUROPEAN);
+        assertEquals(expected, new String(chars));
+
+        chars = input.toCharArray();
+        ns.shape(chars, 0, chars.length, Range.EUROPEAN);
+        assertEquals(expected, new String(chars));
+    }
+
+    @Test
+    public void testGetRanges() {
+        NumericShaper ns = NumericShaper.getContextualShaper(NumericShaper.ARABIC);
+        assertEquals(NumericShaper.ARABIC, ns.getRanges());
+    }
+
+    @Test
+    public void testGetRangeSet() {
+        NumericShaper ns = NumericShaper.getContextualShaper(
+                NumericShaper.ARABIC | NumericShaper.DEVANAGARI);
+        assertEquals(EnumSet.of(Range.ARABIC, Range.DEVANAGARI), ns.getRangeSet());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
index e722b4f..dbc8a02 100644
--- a/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/FileInputStreamTest.java
@@ -16,17 +16,7 @@
 
 package libcore.java.io;
 
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
 import android.system.ErrnoException;
-import android.system.Int32Ref;
 import android.system.Os;
 import android.system.OsConstants;
 import android.system.StructStatVfs;
@@ -40,6 +30,15 @@
 import org.junit.Rule;
 import org.junit.rules.TestRule;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
 public final class FileInputStreamTest extends TestCaseWithRules {
     @Rule
     public TestRule guardRule = ResourceLeakageDetector.getRule();
@@ -224,7 +223,7 @@
         }
 
         try (FileInputStream input = new FileInputStream(file)) {
-            android.system.Os.ioctlInt(input.getFD(), OsConstants.FIONREAD, new Int32Ref(0));
+            android.system.Os.ioctlInt(input.getFD(), OsConstants.FIONREAD);
             fail();
         } catch (ErrnoException expected) {
             assertEquals("FIONREAD should have returned ENOTTY for the file. If it doesn't return"
diff --git a/luni/src/test/java/libcore/java/io/OldObjectStreamFieldTest.java b/luni/src/test/java/libcore/java/io/OldObjectStreamFieldTest.java
index ee09221..71dd4046 100644
--- a/luni/src/test/java/libcore/java/io/OldObjectStreamFieldTest.java
+++ b/luni/src/test/java/libcore/java/io/OldObjectStreamFieldTest.java
@@ -23,7 +23,7 @@
 
 public class OldObjectStreamFieldTest extends junit.framework.TestCase {
 
-    static class DummyClass implements Serializable {
+    static class FakeClass implements Serializable {
         private static final long serialVersionUID = 999999999999998L;
 
         boolean bField = true;
@@ -170,7 +170,7 @@
     }
 
     protected void setUp() {
-        osc = ObjectStreamClass.lookup(DummyClass.class);
+        osc = ObjectStreamClass.lookup(FakeClass.class);
         bamField = osc.getField("bam");
         samField = osc.getField("sam");
         hamField = osc.getField("ham");
diff --git a/luni/src/test/java/libcore/java/lang/BootstrapMethodErrorTest.java b/luni/src/test/java/libcore/java/lang/BootstrapMethodErrorTest.java
new file mode 100644
index 0000000..613ad00
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/BootstrapMethodErrorTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BootstrapMethodErrorTest {
+
+    private final String causeMessage = "Cause";
+    private final String errorMessage = "Error message";
+
+    @Test
+    public void constructor() {
+        BootstrapMethodError error = new BootstrapMethodError();
+
+        assertNull(error.getCause());
+        assertNull(error.getMessage());
+    }
+
+    @Test
+    public void constructorLString() {
+        BootstrapMethodError error = new BootstrapMethodError(errorMessage);
+
+        assertEquals(errorMessage, error.getMessage());
+        assertNull(error.getCause());
+    }
+
+    @Test
+    public void constructorLString_withNull() {
+        BootstrapMethodError error = new BootstrapMethodError((String) null);
+
+        assertNull(error.getMessage());
+        assertNull(error.getCause());
+    }
+
+    @Test
+    public void constructorLString_LThrowable() {
+        Exception cause = new Exception(causeMessage);
+        BootstrapMethodError error = new BootstrapMethodError(errorMessage, cause);
+
+        assertEquals(cause, error.getCause());
+        assertEquals(errorMessage, error.getMessage());
+    }
+
+    @Test
+    public void constructorLString_LThrowable_nullMessage() {
+        Exception cause = new Exception(causeMessage);
+        BootstrapMethodError error = new BootstrapMethodError(null, cause);
+
+        assertNull(error.getMessage());
+        assertEquals(cause, error.getCause());
+    }
+
+    @Test
+    public void constructorLString_LThrowable_nullCause() {
+        BootstrapMethodError error = new BootstrapMethodError(errorMessage, null /* cause */);
+
+        assertNull(error.getCause());
+        assertEquals(errorMessage, error.getMessage());
+    }
+
+    @Test
+    public void constructorLString_LThrowable_bothArgumentsNull() {
+        BootstrapMethodError error = new BootstrapMethodError(null, null);
+
+        assertNull(error.getCause());
+        assertNull(error.getMessage());
+    }
+
+    @Test
+    public void constructorLThrowable() {
+        Exception cause = new Exception(causeMessage);
+        BootstrapMethodError error = new BootstrapMethodError(cause);
+
+        assertEquals(cause, error.getCause());
+        assertEquals(cause.toString(), error.getMessage());
+    }
+
+    @Test
+    public void constructorLThrowable_withNull() {
+        BootstrapMethodError error = new BootstrapMethodError((Throwable) null /* cause */);
+
+        assertNull(error.getCause());
+        assertNull(error.getMessage());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/ByteTest.java b/luni/src/test/java/libcore/java/lang/ByteTest.java
index f051b91..821d060 100644
--- a/luni/src/test/java/libcore/java/lang/ByteTest.java
+++ b/luni/src/test/java/libcore/java/lang/ByteTest.java
@@ -57,4 +57,37 @@
             assertEquals(b, Long.valueOf(b).byteValue());
         }
     }
+
+    public void testCompareUnsigned() {
+        // Ascending order of unsigned(value)
+        final byte a = 0;
+        final byte b = 3;
+        final byte y = -2; // 254
+        final byte z = -1; // 255
+
+        assertTrue(Byte.compareUnsigned(a, b) < 0);
+        assertTrue(Byte.compareUnsigned(a, y) < 0);
+        assertTrue(Byte.compareUnsigned(a, z) < 0);
+        assertTrue(Byte.compareUnsigned(b, y) < 0);
+        assertTrue(Byte.compareUnsigned(b, z) < 0);
+        assertTrue(Byte.compareUnsigned(y, z) < 0);
+
+        assertTrue(Byte.compareUnsigned(b, a) > 0);
+        assertTrue(Byte.compareUnsigned(y, a) > 0);
+        assertTrue(Byte.compareUnsigned(y, b) > 0);
+        assertTrue(Byte.compareUnsigned(z, a) > 0);
+        assertTrue(Byte.compareUnsigned(z, b) > 0);
+        assertTrue(Byte.compareUnsigned(z, y) > 0);
+
+        assertTrue(Byte.compareUnsigned(a, a) == 0);
+        assertTrue(Byte.compareUnsigned(b, b) == 0);
+        assertTrue(Byte.compareUnsigned(y, y) == 0);
+        assertTrue(Byte.compareUnsigned(z, z) == 0);
+
+        assertTrue(Byte.compareUnsigned(Byte.MIN_VALUE, (byte)128) == 0);
+        assertTrue(Byte.compareUnsigned(Byte.MAX_VALUE, (byte)127) == 0);
+        assertTrue(Byte.compareUnsigned(Byte.MIN_VALUE, Byte.MAX_VALUE) > 0);
+        assertTrue(Byte.compareUnsigned(Byte.MIN_VALUE, z) < 0);
+        assertTrue(Byte.compareUnsigned(Byte.MAX_VALUE, z) < 0);
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/ErrorTest.java b/luni/src/test/java/libcore/java/lang/ErrorTest.java
new file mode 100644
index 0000000..1962b07
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/ErrorTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ErrorTest {
+
+    @Test
+    public void withNonWritableStackTrace_getStackTraceIsEmpty() {
+        Error error = new TestError("message", null /* cause */,
+                false /* enableSuppression */,
+                false /* writableStackTrace */);
+
+        assertEquals(0, error.getStackTrace().length);
+    }
+
+    @Test
+    public void withWritableStackTrace_nonEmptyGetStackTrace() {
+        Error error = new TestError("message", null /* cause */,
+                false /* enableSuppression */,
+                true /* writableStackTrace */);
+
+        assertNotEquals(0, error.getStackTrace().length);
+
+        StackTraceElement topStackTraceElement = error.getStackTrace()[0];
+        assertEquals("withWritableStackTrace_nonEmptyGetStackTrace",
+                topStackTraceElement.getMethodName());
+    }
+
+    @Test
+    public void whenSuppressionDisabled_addSuppressHasNoEffect() {
+        Error error = new TestError("Message", null, false /* enableSuppression */, true);
+
+        assertEquals(0, error.getSuppressed().length);
+        error.addSuppressed(new Exception("suppressed exception"));
+        assertEquals(0, error.getSuppressed().length);
+    }
+
+    @Test
+    public void whenSuppressionEnabled_addsSuppressed() {
+        Error error = new TestError("message", null /* cause */, true /* enableSuppression */,
+                true /* writableStackTrace */);
+
+        assertEquals(0, error.getSuppressed().length);
+
+        Exception suppressed = new Exception("suppressed");
+        error.addSuppressed(suppressed);
+        assertArrayEquals(new Throwable[] {suppressed}, error.getSuppressed());
+    }
+
+
+    private static final class TestError extends Error {
+        protected TestError(String message, Throwable cause, boolean enableSuppression,
+                boolean writableStackTrace) {
+            super(message, cause, enableSuppression, writableStackTrace);
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/InheritableThreadLocalTest.java b/luni/src/test/java/libcore/java/lang/InheritableThreadLocalTest.java
new file mode 100644
index 0000000..afba40a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/InheritableThreadLocalTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class InheritableThreadLocalTest {
+
+    @Test
+    public void childValue_shouldReturnInputArgument() {
+        TestInheritableThreadLocal<Object> threadLocal = new TestInheritableThreadLocal<>();
+
+        assertNull(threadLocal.childValue(null));
+        Object parentValue = new Object();
+        assertEquals(parentValue, threadLocal.childValue(parentValue));
+    }
+
+    private static final class TestInheritableThreadLocal<T> extends InheritableThreadLocal<T> {
+
+        @Override
+        public T childValue(T parentValue) {
+            return super.childValue(parentValue);
+        }
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/lang/InternalErrorTest.java b/luni/src/test/java/libcore/java/lang/InternalErrorTest.java
new file mode 100644
index 0000000..acf588e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/InternalErrorTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class InternalErrorTest {
+
+    @Test
+    public void constructorLThrowable() {
+        Throwable cause = new Exception("cause");
+        InternalError error = new InternalError(cause);
+
+        assertEquals(cause, error.getCause());
+        assertEquals("java.lang.Exception: cause", error.getMessage());
+    }
+
+    @Test
+    public void constructorLThrowable_withNull() {
+        InternalError error = new InternalError((Throwable) null);
+
+        assertNull(error.getCause());
+        assertNull(error.getMessage());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java b/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java
index 7fb29e6..2e15d33 100644
--- a/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java
+++ b/luni/src/test/java/libcore/java/lang/LambdaImplementationTest.java
@@ -302,7 +302,7 @@
     private static void assertMultipleDefinitionCharacteristics(
             Callable<String> r1, Callable<String> r2) throws Exception {
 
-        // Sanity check that the lambdas do the same thing.
+        // Check that the lambdas do the same thing.
         assertEquals(r1.call(), r2.call());
 
         // Two lambdas from different definitions can share the same class or may not so there are
@@ -324,7 +324,7 @@
     private static void assertMultipleInstanceCharacteristics(
             Callable<String> r1, Callable<String> r2) throws Exception {
 
-        // Sanity check that the lambdas do the same thing.
+        // Check that the lambdas do the same thing.
         assertEquals(r1.call(), r2.call());
 
         // There doesn't appear to be anything else that is safe to assert here. Two lambdas
diff --git a/luni/src/test/java/libcore/java/lang/LinkageErrorTest.java b/luni/src/test/java/libcore/java/lang/LinkageErrorTest.java
new file mode 100644
index 0000000..2a853d7
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/LinkageErrorTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class LinkageErrorTest {
+
+    private final String errorMessage = "error message";
+
+    @Test
+    public void constructorLString_LThrowable() {
+        Exception cause = new Exception();
+        LinkageError error = new LinkageError(errorMessage, cause);
+
+        assertEquals(cause, error.getCause());
+        assertEquals(errorMessage, error.getMessage());
+    }
+
+    @Test
+    public void constructorLString_LThrowable_nullMessage() {
+        Exception cause = new Exception();
+        LinkageError error = new LinkageError(null, cause);
+
+        assertNull(error.getMessage());
+        assertEquals(cause, error.getCause());
+    }
+
+    @Test
+    public void constructorLString_LThrowable_nullCause() {
+        LinkageError error = new LinkageError(errorMessage, null /* cause */);
+
+        assertEquals(errorMessage, error.getMessage());
+        assertNull(error.getCause());
+    }
+
+    @Test
+    public void constructorLString_LThrowable_bothArgumentsNull() {
+        LinkageError error = new LinkageError(null, null);
+
+        assertNull(error.getCause());
+        assertNull(error.getMessage());
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/lang/MathTest.java b/luni/src/test/java/libcore/java/lang/MathTest.java
index f7ebaa6..ee0dd73 100644
--- a/luni/src/test/java/libcore/java/lang/MathTest.java
+++ b/luni/src/test/java/libcore/java/lang/MathTest.java
@@ -300,4 +300,37 @@
             }
         }
     }
+
+    private static long multiplyFullBigInt(int x, int y) {
+        return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y)).longValue();
+    }
+
+    public void testMultiplyFull() {
+        int[][] v = new int[][]{
+            {0, 0},
+            {-1, 0},
+            {0, -1},
+            {1, 0},
+            {0, 1},
+            {-1, -1},
+            {-1, 1},
+            {1, -1},
+            {1, 1},
+            {Integer.MAX_VALUE, Integer.MAX_VALUE},
+            {Integer.MAX_VALUE, -Integer.MAX_VALUE},
+            {-Integer.MAX_VALUE, Integer.MAX_VALUE},
+            {-Integer.MAX_VALUE, -Integer.MAX_VALUE},
+            {Integer.MAX_VALUE, Integer.MIN_VALUE},
+            {Integer.MIN_VALUE, Integer.MAX_VALUE},
+            {Integer.MIN_VALUE, Integer.MIN_VALUE}
+        };
+
+        for (int[] xy : v) {
+            int x = xy[0];
+            int y = xy[1];
+            long p1 = multiplyFullBigInt(x, y);
+            long p2 = Math.multiplyFull(x, y);
+            assertEquals(p1, p2);
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/OldClassTest.java b/luni/src/test/java/libcore/java/lang/OldClassTest.java
index f5bc787..a4af7d1 100644
--- a/luni/src/test/java/libcore/java/lang/OldClassTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldClassTest.java
@@ -306,7 +306,7 @@
         }
     }
 
-    // AndroidOnly: Class.forName method throws ClassNotFoundException on Android.
+    // Android-note: Class.forName method throws ClassNotFoundException on Android.
     public void test_forNameLjava_lang_StringLbooleanLClassLoader_AndroidOnly() throws Exception {
 
         // Android doesn't support loading class files from a jar.
@@ -460,7 +460,7 @@
         assertEquals(AbstractList.class, type.getRawType());
     }
 
-    // AndroidOnly: Uses dalvik.system.PathClassLoader.
+    // Android-note: Uses dalvik.system.PathClassLoader.
     // Different behavior between cts host and run-core-test")
     public void test_getPackage() {
 
@@ -469,7 +469,7 @@
                       thisPackage.getName());
 
       Package stringPackage = String.class.getPackage();
-      assertNotNull("java.lang", stringPackage.getName());
+      assertEquals("java.lang", stringPackage.getName());
 
       String hyts_package_name = "hyts_package_dex.jar";
       File resources = Support_Resources.createTempFolder();
@@ -517,6 +517,66 @@
       }
     }
 
+    // Android-note: Uses dalvik.system.PathClassLoader.
+    // Different behavior between cts host and run-core-test")
+    public void test_getPackageName() {
+
+      String thisPackage = getClass().getPackageName();
+      assertEquals("libcore.java.lang", thisPackage);
+
+      String stringPackage = String.class.getPackageName();
+      assertEquals("java.lang", stringPackage);
+  
+      String stringArrayPackage = String[].class.getPackageName();
+      assertEquals("java.lang", stringArrayPackage);
+
+      String stringArrayArrayPackage = String[][].class.getPackageName();
+      assertEquals("java.lang", stringArrayArrayPackage);
+
+      String intPackage = int.class.getPackageName();
+      assertEquals("java.lang", intPackage);
+
+      String intArrayPackage = int[].class.getPackageName();
+      assertEquals("java.lang", intPackage);
+
+      String hyts_package_name = "hyts_package_dex.jar";
+      File resources = Support_Resources.createTempFolder();
+      Support_Resources.copyFile(resources, "Package", hyts_package_name);
+
+      String resPath = resources.toString();
+      if (resPath.charAt(0) == '/' || resPath.charAt(0) == '\\')
+          resPath = resPath.substring(1);
+
+      try {
+
+          URL resourceURL = new URL("file:/" + resPath + "/Package/"
+                  + hyts_package_name);
+
+          ClassLoader cl = Support_ClassLoader.getInstance(resourceURL,
+                  getClass().getClassLoader());
+
+          Class clazz = cl.loadClass("C");
+          assertEquals("", clazz.getPackageName());
+
+          clazz = Class.forName("[LC;", false, cl);
+          assertEquals("", clazz.getPackageName());
+
+          clazz = Class.forName("[[LC;", false, cl);
+          assertEquals("", clazz.getPackageName());
+
+          clazz = cl.loadClass("a.b.C");
+          assertEquals("a.b", clazz.getPackageName());
+
+          clazz = Class.forName("[La.b.C;", false, cl);
+          assertEquals("a.b", clazz.getPackageName());
+
+          clazz = Class.forName("[[La.b.C;", false, cl);
+          assertEquals("a.b", clazz.getPackageName());
+      } catch(Exception e) {
+          fail("Unexpected exception was thrown: " + e.toString());
+      }
+    }
+
     public void test_getSigners() {
         assertNull(void.class.getSigners());
         assertNull(PublicTestClass.class.getSigners());
diff --git a/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java b/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java
index 35e366e..4b74cec 100644
--- a/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldThreadGroupTest.java
@@ -278,7 +278,7 @@
     public void test_resume() {
         ThreadGroup group = new ThreadGroup("Foo");
 
-        Thread thread = launchFiveSecondDummyThread(group);
+        Thread thread = launchFiveSecondFakeThread(group);
 
         try {
             Thread.sleep(1000);
@@ -293,7 +293,7 @@
         }
     }
 
-    private Thread launchFiveSecondDummyThread(ThreadGroup group) {
+    private Thread launchFiveSecondFakeThread(ThreadGroup group) {
         Thread thread = new Thread(group, "Bar") {
             public void run() {
                 try {
diff --git a/luni/src/test/java/libcore/java/lang/OldThreadTest.java b/luni/src/test/java/libcore/java/lang/OldThreadTest.java
index 75b230d..ab3f7df 100644
--- a/luni/src/test/java/libcore/java/lang/OldThreadTest.java
+++ b/luni/src/test/java/libcore/java/lang/OldThreadTest.java
@@ -154,7 +154,7 @@
         }
     }
 
-    private Thread launchFiveSecondDummyThread() {
+    private Thread launchFiveSecondFakeThread() {
         Thread thread = new Thread() {
             public void run() {
                 try {
@@ -389,7 +389,7 @@
 
     @SuppressWarnings("deprecation")
     public void test_stop() {
-        Thread thread = launchFiveSecondDummyThread();
+        Thread thread = launchFiveSecondFakeThread();
 
         try {
             Thread.sleep(1000);
@@ -442,7 +442,7 @@
 
     @SuppressWarnings("deprecation")
     public void test_suspend() {
-        Thread thread = launchFiveSecondDummyThread();
+        Thread thread = launchFiveSecondFakeThread();
 
         try {
             Thread.sleep(1000);
diff --git a/luni/src/test/java/libcore/java/lang/PackageTest.java b/luni/src/test/java/libcore/java/lang/PackageTest.java
index 672af5d..ff1a9c3 100644
--- a/luni/src/test/java/libcore/java/lang/PackageTest.java
+++ b/luni/src/test/java/libcore/java/lang/PackageTest.java
@@ -17,6 +17,8 @@
 package libcore.java.lang;
 
 import dalvik.system.VMRuntime;
+
+import java.net.URL;
 import java.util.Arrays;
 import java.util.List;
 import libcore.junit.junit3.TestCaseWithRules;
@@ -24,6 +26,7 @@
 import libcore.junit.util.SwitchTargetSdkVersionRule.TargetSdkVersion;
 import org.junit.Rule;
 import org.junit.rules.TestRule;
+import org.mockito.internal.matchers.Null;
 
 public final class PackageTest extends TestCaseWithRules {
 
@@ -46,6 +49,12 @@
         assertEquals(getClass().getPackage(), libcoreJavaLang);
     }
 
+    public void testGetPackageName() {
+        Package libcoreJavaLang = Package.getPackage("libcore.java.lang");
+        assertEquals("libcore.java.lang", getClass().getPackageName());
+        assertEquals(getClass().getPackageName(), libcoreJavaLang.getName());
+    }
+
     // http://b/28057303
     @TargetSdkVersion(24)
     public void test_toString_targetSdkVersion_24() throws Exception {
@@ -64,4 +73,137 @@
     public void testGetPackages() {
         assertTrue(packages.contains(getClass().getPackage()));
     }
+
+    public void testGetAnnotationsByType() {
+        Package libcoreJavaLang = Package.getPackage("libcore.java.lang");
+
+        assertEquals(1, libcoreJavaLang.getAnnotationsByType(TestPackageAnnotation.class).length);
+        assertEquals(0, libcoreJavaLang.getAnnotationsByType(Override.class).length);
+    }
+
+    public void testGetAnnotationsByType_shouldThrowNPE_whenNullIsPassed() {
+        Package libcoreJavaLang = Package.getPackage("libcore.java.lang");
+
+        try {
+            libcoreJavaLang.getAnnotationsByType(null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    public void testIsSealed_nonSealedPackage() throws Exception {
+        TestClassLoader testClassLoader = new TestClassLoader();
+        Package nonSealedPackage = testClassLoader.definePackage(
+                "libcore.java.lang.nonsealed",
+                "spec title",
+                "spec version",
+                "spec vendor",
+                "impl title",
+                "impl version",
+                "impl vendor",
+                null /* sealBase */);
+
+        assertFalse(nonSealedPackage.isSealed());
+        assertFalse(nonSealedPackage.isSealed(new URL("file://libcore/java/lang/nonsealed")));
+    }
+
+    public void testIsSealed_sealedPackage() throws Exception {
+        TestClassLoader testClassLoader = new TestClassLoader();
+        URL sealBase = new URL("file://libcore/java/lang/sealed");
+        Package sealedPackage = testClassLoader.definePackage(
+                "libcore.java.lang.sealed",
+                "spec title",
+                "spec version",
+                "spec vendor",
+                "impl title",
+                "impl version",
+                "impl vendor",
+                sealBase);
+
+        assertTrue(sealedPackage.isSealed());
+        assertTrue(sealedPackage.isSealed(sealBase));
+        assertFalse(sealedPackage.isSealed(new URL("file://libcore/java/lang")));
+        try {
+            sealedPackage.isSealed(null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    public void testGetSpecificationVendor() {
+        String specVendor = "specification vendor";
+        TestClassLoader testClassLoader = new TestClassLoader();
+        Package aPackage = testClassLoader.definePackage(
+                "libcore.java.lang.nonsealed",
+                "spec title",
+                "spec version",
+                specVendor,
+                "impl title",
+                "impl version",
+                "impl vendor",
+                null /* sealBase */);
+
+        assertEquals(specVendor, aPackage.getSpecificationVendor());
+    }
+
+    public void testGetSpecificationVersion() {
+        String specVersion = "specification version";
+        TestClassLoader testClassLoader = new TestClassLoader();
+        Package aPackage = testClassLoader.definePackage(
+                "libcore.java.lang.nonsealed",
+                "spec title",
+                specVersion,
+                "spec vendor",
+                "impl title",
+                "impl version",
+                "impl vendor",
+                null /* sealBase */);
+
+        assertEquals(specVersion, aPackage.getSpecificationVersion());
+    }
+
+    public void testIsCompatibleWith() {
+        String specVersion = "2.3.1";
+        TestClassLoader testClassLoader = new TestClassLoader();
+        Package aPackage = testClassLoader.definePackage(
+                "libcore.java.lang.nonsealed",
+                "spec title",
+                specVersion,
+                "spec vendor",
+                "impl title",
+                "impl version",
+                "impl vendor",
+                null /* sealBase */);
+
+        assertTrue(aPackage.isCompatibleWith(specVersion));
+        assertTrue(aPackage.isCompatibleWith("2.2.99.1"));
+        assertTrue(aPackage.isCompatibleWith("1.0"));
+        assertTrue(aPackage.isCompatibleWith("2.3.1.0"));
+        assertTrue(aPackage.isCompatibleWith("2.3"));
+        assertFalse(aPackage.isCompatibleWith("2.4"));
+        try {
+            aPackage.isCompatibleWith(null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+
+    /**
+     * {@link java.lang.Package} constructors are package-private and
+     * {@link java.lang.ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}
+     * is other way to create instance of {@link java.lang.Package}
+     */
+    private static final class TestClassLoader extends ClassLoader {
+        @Override
+        public Package definePackage(String name, String specTitle, String specVersion,
+                String specVendor, String implTitle, String implVersion, String implVendor,
+                URL sealBase) throws IllegalArgumentException {
+            return super.definePackage(name, specTitle, specVersion, specVendor, implTitle,
+                    implVersion, implVendor, sealBase);
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
index 07ce6dd..1615188 100644
--- a/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
+++ b/luni/src/test/java/libcore/java/lang/ProcessBuilderTest.java
@@ -356,7 +356,7 @@
     }
 
     /**
-     * Tests that {@link Redirect}'s equals() and hashCode() is sane.
+     * Tests that {@link Redirect}'s equals() and hashCode() is useful.
      */
     public void testRedirect_equals() {
         File fileA = new File("/tmp/fileA");
diff --git a/luni/src/test/java/libcore/java/lang/RuntimeExceptionTest.java b/luni/src/test/java/libcore/java/lang/RuntimeExceptionTest.java
new file mode 100644
index 0000000..35ab3d4
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/RuntimeExceptionTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RuntimeExceptionTest {
+
+    @Test
+    public void withNonWritableStackTrace_getStackTraceIsEmpty() {
+        RuntimeException exception = new TestRuntimeException("message", null /* cause */,
+                false /* enableSuppression */,
+                false /* writableStackTrace */);
+
+        assertEquals(0, exception.getStackTrace().length);
+    }
+
+    @Test
+    public void withWritableStackTrace_nonEmptyGetStackTrace() {
+        RuntimeException exception = new TestRuntimeException("message", null /* cause */,
+                false /* enableSuppression */,
+                true /* writableStackTrace */);
+
+        assertNotEquals(0, exception.getStackTrace().length);
+
+        StackTraceElement topStackTraceElement = exception.getStackTrace()[0];
+        assertEquals("withWritableStackTrace_nonEmptyGetStackTrace",
+                topStackTraceElement.getMethodName());
+    }
+
+    @Test
+    public void whenSuppressionDisabled_addSuppressHasNoEffect() {
+        RuntimeException exception = new TestRuntimeException("Message", null,
+                false /* enableSuppression */, true /* writableStackTrace */);
+
+        assertEquals(0, exception.getSuppressed().length);
+        exception.addSuppressed(new Exception("suppressed exception"));
+        assertEquals(0, exception.getSuppressed().length);
+    }
+
+    @Test
+    public void whenSuppressionEnabled_addsSuppressed() {
+        RuntimeException error = new TestRuntimeException("message", null /* cause */,
+                true /* enableSuppression */, true /* writableStackTrace */);
+
+        assertEquals(0, error.getSuppressed().length);
+
+        Exception suppressed = new Exception("suppressed");
+        error.addSuppressed(suppressed);
+        assertArrayEquals(new Throwable[] {suppressed}, error.getSuppressed());
+    }
+
+    private static final class TestRuntimeException extends RuntimeException {
+        TestRuntimeException(String message, Throwable cause, boolean enableSuppression,
+                boolean writableStackTrace) {
+            super(message, cause, enableSuppression, writableStackTrace);
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/RuntimePermissionTest.java b/luni/src/test/java/libcore/java/lang/RuntimePermissionTest.java
new file mode 100644
index 0000000..33c0898
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/RuntimePermissionTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RuntimePermissionTest {
+
+    @Test
+    public void constructorLString_LString() {
+        RuntimePermission runtimePermission = new RuntimePermission(null, null);
+        assertEquals("", runtimePermission.getActions());
+        assertEquals("", runtimePermission.getName());
+
+        runtimePermission = new RuntimePermission("non empty", "non empty");
+        assertEquals("", runtimePermission.getActions());
+        assertEquals("", runtimePermission.getName());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/SecurityManagerTest.java b/luni/src/test/java/libcore/java/lang/SecurityManagerTest.java
new file mode 100644
index 0000000..99af64d
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/SecurityManagerTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.FileDescriptor;
+import java.net.Inet4Address;
+import java.security.AllPermission;
+
+/**
+ * Android doesn't support SecurityManager. This test checks that the implementation does nothing
+ * or doesn't throw arbitrary exceptions.
+ */
+@RunWith(JUnit4.class)
+public class SecurityManagerTest {
+
+    private static final String SAMPLE_HOST = "www.example.com";
+    private static final int SAMPLE_PORT = 80;
+
+    /**
+     * Expose protected method from {@link SecurityManager} for test.
+     */
+    private static class TestSecurityManager extends SecurityManager {
+        @Override
+        protected Class[] getClassContext() {
+            return super.getClassContext();
+        }
+
+        @Override
+        protected boolean inClass(String name) {
+            return super.inClass(name);
+        }
+
+        @Override
+        protected boolean inClassLoader() {
+            return super.inClassLoader();
+        }
+
+        @Override
+        protected Class<?> currentLoadedClass() {
+            return super.currentLoadedClass();
+        }
+
+        @Override
+        protected ClassLoader currentClassLoader() {
+            return super.currentClassLoader();
+        }
+
+        @Override
+        protected int classDepth(String name) {
+            return super.classDepth(name);
+        }
+
+        @Override
+        protected int classLoaderDepth() {
+            return super.classLoaderDepth();
+        }
+    }
+
+    private TestSecurityManager sm = new TestSecurityManager();
+
+    @Test
+    public void testSystemSecurityManager() {
+        Assert.assertNull(System.getSecurityManager());
+        // Set null allowed
+        System.setSecurityManager(null);
+        try {
+            System.setSecurityManager(sm);
+            Assert.fail("System.setSecurityManager(sm) doesn't throw.");
+        } catch (SecurityException e) {
+            // expected
+        }
+        Assert.assertNull(System.getSecurityManager());
+    }
+
+    @Test
+    public void testCheckAccept() {
+        sm.checkAccept(SAMPLE_HOST, SAMPLE_PORT);
+    }
+
+    @Test
+    public void testCheckAccess() {
+        sm.checkAccess(Thread.currentThread());
+    }
+
+    @Test
+    public void testCheckAccessThreadGroup() {
+        sm.checkAccess(sm.getThreadGroup());
+    }
+
+    @Test
+    public void testCheckAwtEventQueueAccess() {
+        sm.checkAwtEventQueueAccess();
+    }
+
+    @Test
+    public void testCheckConnect() {
+        sm.checkConnect(SAMPLE_HOST, SAMPLE_PORT);
+    }
+
+    @Test
+    public void testCheckConnectWithObject() {
+        sm.checkConnect(SAMPLE_HOST, SAMPLE_PORT, null);
+    }
+
+    @Test
+    public void testCheckCreateClassLoader() {
+        sm.checkCreateClassLoader();
+    }
+
+    @Test
+    public void testCheckDelete() {
+        sm.checkDelete(null);
+        sm.checkDelete("/FILE_NOT_EXIST");
+    }
+
+    @Test
+    public void testCheckExec() {
+        sm.checkExec("invalid command");
+    }
+
+    @Test
+    public void testCheckExit() {
+        sm.checkExit(0);
+        sm.checkExit(1);
+        sm.checkExit(127);
+    }
+
+    @Test
+    public void testCheckLink() {
+        sm.checkLink("libinvalid.so");
+    }
+
+    @Test
+    public void testCheckListen() {
+        sm.checkListen(SAMPLE_PORT);
+    }
+
+    @Test
+    public void testCheckMemberAccess() {
+        sm.checkMemberAccess(System.class, 0);
+    }
+
+    @Test
+    public void testCheckMulticast() {
+        sm.checkMulticast(Inet4Address.LOOPBACK);
+    }
+
+    @Test
+    public void testCheckMulticastWithTtl() {
+        sm.checkMulticast(Inet4Address.LOOPBACK, (byte) 0);
+    }
+
+    @Test
+    public void testCheckPackageAccess() {
+        sm.checkPackageAccess("java.lang");
+        sm.checkPackageAccess("android.invalid.package");
+    }
+
+    @Test
+    public void testCheckPackageDefinition() {
+        sm.checkPackageDefinition("java.lang");
+        sm.checkPackageDefinition("android.invalid.package");
+    }
+
+    @Test
+    public void testCheckPermission() {
+        sm.checkPermission(new AllPermission(), this);
+    }
+
+    @Test
+    public void testCheckPrintJobAccess() {
+        sm.checkPrintJobAccess();
+    }
+
+    @Test
+    public void testCheckPropertiesAccess() {
+        sm.checkPropertiesAccess();
+    }
+
+    @Test
+    public void testCheckPropertyAccess() {
+        sm.checkPropertyAccess(null);
+        sm.checkPropertyAccess("system.invalid.property");
+    }
+
+    @Test
+    public void testCheckRead() {
+        sm.checkRead((FileDescriptor) null);
+        sm.checkRead(FileDescriptor.in);
+        sm.checkRead(FileDescriptor.out);
+        sm.checkRead(FileDescriptor.err);
+    }
+
+    @Test
+    public void testCheckReadPath() {
+        sm.checkRead((String) null);
+        sm.checkRead("/");
+        sm.checkRead("/invalid_path");
+    }
+
+    @Test
+    public void testCheckReadWithObject() {
+        sm.checkRead("/", this);
+        sm.checkRead("/invalid_path", this);
+    }
+
+    @Test
+    public void testCheckSecurityAccess() {
+        sm.checkSecurityAccess(null);
+        sm.checkSecurityAccess("");
+    }
+
+    @Test
+    public void testCheckSetFactory() {
+        sm.checkSetFactory();
+    }
+
+    @Test
+    public void testCheckSystemClipboardAccess() {
+        sm.checkSystemClipboardAccess();
+    }
+
+    @Test
+    public void testCheckTopLevelWindow() {
+        sm.checkTopLevelWindow(null);
+        sm.checkTopLevelWindow(this);
+    }
+
+    @Test
+    public void testCheckWrite() {
+        sm.checkWrite((FileDescriptor) null);
+        sm.checkWrite(FileDescriptor.in);
+        sm.checkWrite(FileDescriptor.out);
+        sm.checkWrite(FileDescriptor.err);
+    }
+
+    @Test
+    public void testCheckWriteWithPath() {
+        sm.checkWrite("/");
+        sm.checkWrite("/invalid path");
+    }
+
+    @Test
+    public void testClassDepth() {
+        sm.classDepth(null);
+    }
+
+    @Test
+    public void testClassLoaderDepth() {
+        sm.classLoaderDepth();
+    }
+
+    @Test
+    public void testCurrentClassLoader() {
+        sm.currentClassLoader();
+    }
+
+    @Test
+    public void testCurrentLoadedClass() {
+        sm.currentLoadedClass();
+    }
+
+    @Test
+    public void testGetInCheck() {
+        sm.getInCheck();
+    }
+
+    @Test
+    public void testGetSecurityContext() {
+        sm.getSecurityContext();
+    }
+
+    @Test
+    public void testInClass() {
+        sm.inClass(this.getClass().getName());
+    }
+
+    @Test
+    public void testInClassLoader() {
+        sm.inClassLoader();
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/ShortTest.java b/luni/src/test/java/libcore/java/lang/ShortTest.java
index a3db71a..d805f8e 100644
--- a/luni/src/test/java/libcore/java/lang/ShortTest.java
+++ b/luni/src/test/java/libcore/java/lang/ShortTest.java
@@ -57,4 +57,37 @@
             assertEquals(b, Long.valueOf(b).shortValue());
         }
     }
+
+    public void testCompareUnsigned() {
+        // Ascending order of unsigned(value)
+        final short a = 0;
+        final short b = 3;
+        final short y = -2; // 65534
+        final short z = -1; // 65535
+
+        assertTrue(Short.compareUnsigned(a, b) < 0);
+        assertTrue(Short.compareUnsigned(a, y) < 0);
+        assertTrue(Short.compareUnsigned(a, z) < 0);
+        assertTrue(Short.compareUnsigned(b, y) < 0);
+        assertTrue(Short.compareUnsigned(b, z) < 0);
+        assertTrue(Short.compareUnsigned(y, z) < 0);
+
+        assertTrue(Short.compareUnsigned(b, a) > 0);
+        assertTrue(Short.compareUnsigned(y, a) > 0);
+        assertTrue(Short.compareUnsigned(y, b) > 0);
+        assertTrue(Short.compareUnsigned(z, a) > 0);
+        assertTrue(Short.compareUnsigned(z, b) > 0);
+        assertTrue(Short.compareUnsigned(z, y) > 0);
+
+        assertTrue(Short.compareUnsigned(a, a) == 0);
+        assertTrue(Short.compareUnsigned(b, b) == 0);
+        assertTrue(Short.compareUnsigned(y, y) == 0);
+        assertTrue(Short.compareUnsigned(z, z) == 0);
+
+        assertTrue(Short.compareUnsigned(Short.MIN_VALUE, (short)32768) == 0);
+        assertTrue(Short.compareUnsigned(Short.MAX_VALUE, (short)32767) == 0);
+        assertTrue(Short.compareUnsigned(Short.MIN_VALUE, Short.MAX_VALUE) > 0);
+        assertTrue(Short.compareUnsigned(Short.MIN_VALUE, z) < 0);
+        assertTrue(Short.compareUnsigned(Short.MAX_VALUE, z) < 0);
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/StrictMathTest.java b/luni/src/test/java/libcore/java/lang/StrictMathTest.java
index 2509af8..fc6701f 100644
--- a/luni/src/test/java/libcore/java/lang/StrictMathTest.java
+++ b/luni/src/test/java/libcore/java/lang/StrictMathTest.java
@@ -228,4 +228,37 @@
             }
         }
     }
+
+    private static long multiplyFullBigInt(int x, int y) {
+        return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y)).longValue();
+    }
+
+    public void testMultiplyFull() {
+        int[][] v = new int[][]{
+            {0, 0},
+            {-1, 0},
+            {0, -1},
+            {1, 0},
+            {0, 1},
+            {-1, -1},
+            {-1, 1},
+            {1, -1},
+            {1, 1},
+            {Integer.MAX_VALUE, Integer.MAX_VALUE},
+            {Integer.MAX_VALUE, -Integer.MAX_VALUE},
+            {-Integer.MAX_VALUE, Integer.MAX_VALUE},
+            {-Integer.MAX_VALUE, -Integer.MAX_VALUE},
+            {Integer.MAX_VALUE, Integer.MIN_VALUE},
+            {Integer.MIN_VALUE, Integer.MAX_VALUE},
+            {Integer.MIN_VALUE, Integer.MIN_VALUE}
+        };
+
+        for (int[] xy : v) {
+            int x = xy[0];
+            int y = xy[1];
+            long p1 = multiplyFullBigInt(x, y);
+            long p2 = StrictMath.multiplyFull(x, y);
+            assertEquals(p1, p2);
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/SystemTest.java b/luni/src/test/java/libcore/java/lang/SystemTest.java
index 56b6558..258f8a1 100644
--- a/luni/src/test/java/libcore/java/lang/SystemTest.java
+++ b/luni/src/test/java/libcore/java/lang/SystemTest.java
@@ -160,7 +160,7 @@
     }
 
     public void testSystemProperties_immutable() {
-        // Android-specific: The RI does not have a concept of immutable properties.
+        // Android-note: The RI does not have a concept of immutable properties.
 
         // user.dir is an immutable property
         String userDir = System.getProperty("user.dir");
@@ -223,10 +223,10 @@
 
         System.setProperties(newProperties);
 
-        // Android-specific: The RI makes the setProperties() argument the system properties object,
+        // Android-note: The RI makes the setProperties() argument the system properties object.
         // Android makes a new Properties object and copies the properties.
         assertNotSame(newProperties, System.getProperties());
-        // Android-specific: The RI does not have a concept of immutable properties.
+        // Android-note: The RI does not have a concept of immutable properties.
         assertEquals(userDir, System.getProperty("user.dir"));
 
         assertEquals("v2", System.getProperty("p1"));
@@ -242,11 +242,26 @@
 
         properties.clear();
 
-        // Android-specific: The RI clears everything, Android resets to immutable defaults.
+        // Android-note: The RI clears everything, Android resets to immutable defaults.
         assertEquals(userDir, System.getProperty("user.dir"));
         assertNull(System.getProperty("p1"));
     }
 
+    /**
+     * Assert that the following ICU-related system properties exist
+     */
+    public void testSystemProperties_getProperties_icu() {
+        String icuVersion = System.getProperty("android.icu.library.version");
+        assertNotNull(icuVersion);
+        assertTrue(icuVersion.length() > 0);
+        String unicodeVersion = System.getProperty("android.icu.unicode.version");
+        assertNotNull(unicodeVersion);
+        assertTrue(unicodeVersion.length() > 0);
+        String cldrVersion = System.getProperty("android.icu.cldr.version");
+        assertNotNull(cldrVersion);
+        assertTrue(cldrVersion.length() > 0);
+    }
+
     public void testSystem_setSecurityManager_null_noException() {
         System.setSecurityManager(null);
     }
@@ -258,4 +273,12 @@
         } catch (SecurityException expected) {
         }
     }
+
+    /**
+     * Overall {@link System#console()} return value depends on how exactly runtime was started, but
+     * for Android apps it will be null.
+     */
+    public void testSystem_console() {
+        assertNull(System.console());
+    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/ThreadGroupTest.java b/luni/src/test/java/libcore/java/lang/ThreadGroupTest.java
new file mode 100644
index 0000000..4076bd1
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/ThreadGroupTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+public class ThreadGroupTest {
+
+    private CountDownLatch mLatch;
+
+    @Before
+    public void setup() {
+        mLatch = new CountDownLatch(2);
+    }
+
+    @Test
+    public void interrupt_shouldInterruptAllThreadsInAGroup() throws Exception {
+        ThreadGroup group = new ThreadGroup("group under test");
+
+        Thread first = createHangThread(group);
+        Thread second = createHangThread(group);
+
+        first.start();
+        second.start();
+
+        group.interrupt();
+
+        assertTrue("Some thread was not interrupted", mLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void interrupt_shouldInterruptThreadsInSubgroups() throws Exception {
+        ThreadGroup parentGroup = new ThreadGroup("parent thread group");
+        ThreadGroup childGroup = new ThreadGroup(parentGroup, "child thread group");
+
+        Thread first = createHangThread(parentGroup);
+        Thread second = createHangThread(childGroup);
+
+        first.start();
+        second.start();
+
+        parentGroup.interrupt();
+
+        assertTrue("Some thread was not interrupted", mLatch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void interrupt_shouldNotInterruptThreadsInParentGroup() throws Exception {
+        ThreadGroup parentGroup = new ThreadGroup("parent thread group");
+        ThreadGroup childGroup = new ThreadGroup(parentGroup, "child thread group");
+
+        Thread first = createHangThread(parentGroup);
+        Thread second = createHangThread(childGroup);
+
+        first.start();
+        second.start();
+
+        childGroup.interrupt();
+
+        assertFalse("Both threads were interrupted", mLatch.await(5, TimeUnit.SECONDS));
+
+        assertEquals("Thread from parent group was interrupted", 1, mLatch.getCount());
+    }
+
+    @Test
+    public void suspend_shouldThrowUnsupportedOperationException() throws Exception {
+        ThreadGroup threadGroup = new ThreadGroup("test group");
+
+        Thread thread = createHangThread(threadGroup);
+        thread.start();
+
+        try {
+            threadGroup.suspend();
+            fail("suspend() didn't throw UnsupportedOperationException");
+        } catch (UnsupportedOperationException ignored) {
+            // expected
+        } finally {
+            thread.join();
+        }
+    }
+
+    @Test
+    public void stop_shouldThrowUnsupportedOperationException() throws Exception {
+        ThreadGroup threadGroup = new ThreadGroup("test group");
+
+        Thread thread = createHangThread(threadGroup);
+        thread.start();
+
+        try {
+            threadGroup.stop();
+            fail("stop() didn't throw UnsupportedOperationException");
+        } catch (UnsupportedOperationException ignored) {
+            // expected
+        } finally {
+            thread.join();
+        }
+    }
+
+    private Thread createHangThread(ThreadGroup threadGroup) {
+        return new Thread(threadGroup, () -> {
+            try {
+                Thread.sleep(20_000);
+            } catch (InterruptedException e) {
+                mLatch.countDown();
+            }
+        });
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/lang/ThreadTest.java b/luni/src/test/java/libcore/java/lang/ThreadTest.java
index 4e60891..19ca2b6 100644
--- a/luni/src/test/java/libcore/java/lang/ThreadTest.java
+++ b/luni/src/test/java/libcore/java/lang/ThreadTest.java
@@ -16,6 +16,13 @@
 
 package libcore.java.lang;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertNotNull;
+
 import dalvik.system.InMemoryDexClassLoader;
 
 import java.io.InputStream;
@@ -31,13 +38,17 @@
 import junit.framework.Assert;
 import junit.framework.TestCase;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.InOrder;
 import org.mockito.Mockito;
 
 import libcore.io.Streams;
 import libcore.java.lang.ref.FinalizationTester;
 
-public final class ThreadTest extends TestCase {
+@RunWith(JUnit4.class)
+public final class ThreadTest {
     static {
         System.loadLibrary("javacoretests");
     }
@@ -46,12 +57,14 @@
      * getContextClassLoader returned a non-application class loader.
      * http://code.google.com/p/android/issues/detail?id=5697
      */
-    public void testJavaContextClassLoader() throws Exception {
+    @Test
+    public void javaContextClassLoader() throws Exception {
         Assert.assertNotNull("Must have a Java context ClassLoader",
                 Thread.currentThread().getContextClassLoader());
     }
 
-    public void testLeakingStartedThreads() {
+    @Test
+    public void leakingStartedThreads() {
         final AtomicInteger finalizedThreadsCount = new AtomicInteger();
         for (int i = 0; true; i++) {
             try {
@@ -64,7 +77,8 @@
         assertTrue("Started threads were never finalized!", finalizedThreadsCount.get() > 0);
     }
 
-    public void testLeakingUnstartedThreads() {
+    @Test
+    public void leakingUnstartedThreads() {
         final AtomicInteger finalizedThreadsCount = new AtomicInteger();
         for (int i = 0; true; i++) {
             try {
@@ -77,7 +91,8 @@
         assertTrue("Unstarted threads were never finalized!", finalizedThreadsCount.get() > 0);
     }
 
-    public void testThreadSleep() throws Exception {
+    @Test
+    public void threadSleep() throws Exception {
         int millis = 1000;
         long start = System.currentTimeMillis();
 
@@ -89,7 +104,8 @@
         assertTrue("Actual sleep off by " + offBy + " ms", offBy <= 250);
     }
 
-    public void testThreadInterrupted() throws Exception {
+    @Test
+    public void threadInterrupted() throws Exception {
         Thread.currentThread().interrupt();
         try {
             Thread.sleep(0);
@@ -99,7 +115,8 @@
         }
     }
 
-    public void testThreadSleepIllegalArguments() throws Exception {
+    @Test
+    public void threadSleepIllegalArguments() throws Exception {
 
         try {
             Thread.sleep(-1);
@@ -120,7 +137,8 @@
         }
     }
 
-    public void testThreadWakeup() throws Exception {
+    @Test
+    public void threadWakeup() throws Exception {
         WakeupTestThread t1 = new WakeupTestThread();
         WakeupTestThread t2 = new WakeupTestThread();
 
@@ -135,22 +153,26 @@
         assertTrue("Threads did not finish", t1.done && t2.done);
     }
 
-    public void testContextClassLoaderIsNotNull() {
+    @Test
+    public void contextClassLoaderIsNotNull() {
         assertNotNull(Thread.currentThread().getContextClassLoader());
     }
 
-    public void testContextClassLoaderIsInherited() {
+    @Test
+    public void contextClassLoaderIsInherited() {
         Thread other = new Thread();
         assertSame(Thread.currentThread().getContextClassLoader(), other.getContextClassLoader());
     }
 
-    public void testSetPriority_unstarted() throws Exception {
+    @Test
+    public void setPriority_unstarted() {
         Thread thread = new Thread();
         checkSetPriority_inBounds_succeeds(thread);
         checkSetPriority_outOfBounds_fails(thread);
     }
 
-    public void testSetPriority_starting() throws Exception {
+    @Test
+    public void setPriority_starting() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Thread thread = new Thread("starting thread") {
             @Override public void run() { try { latch.await(); } catch (Exception e) { } }
@@ -167,7 +189,8 @@
         thread.join();
     }
 
-    public void testSetPriority_started() throws Exception {
+    @Test
+    public void setPriority_started() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Thread startedThread = new Thread("started thread") {
             @Override public void run() { try { latch.await(); } catch (Exception e) { } }
@@ -179,7 +202,8 @@
         startedThread.join();
     }
 
-    public void testSetPriority_joined() throws Exception {
+    @Test
+    public void setPriority_joined() throws Exception {
         Thread joinedThread = new Thread();
         joinedThread.start();
         joinedThread.join();
@@ -223,7 +247,8 @@
         assertEquals(oldPriority, thread.getPriority()); // priority shouldn't have changed
     }
 
-    public void testUncaughtExceptionPreHandler_calledBeforeDefaultHandler() {
+    @Test
+    public void uncaughtExceptionPreHandler_calledBeforeDefaultHandler() {
         UncaughtExceptionHandler initialHandler = Mockito.mock(UncaughtExceptionHandler.class);
         UncaughtExceptionHandler defaultHandler = Mockito.mock(UncaughtExceptionHandler.class);
         InOrder inOrder = Mockito.inOrder(initialHandler, defaultHandler);
@@ -245,7 +270,8 @@
         }
     }
 
-    public void testUncaughtExceptionPreHandler_noDefaultHandler() {
+    @Test
+    public void uncaughtExceptionPreHandler_noDefaultHandler() {
         UncaughtExceptionHandler initialHandler = Mockito.mock(UncaughtExceptionHandler.class);
         UncaughtExceptionHandler originalDefaultHandler
                 = Thread.getDefaultUncaughtExceptionHandler();
@@ -266,7 +292,8 @@
     /**
      * Thread.getStackTrace() is broken. http://b/1252043
      */
-    public void testGetStackTrace() throws Exception {
+    @Test
+    public void getStackTrace() throws Exception {
         Thread t1 = new Thread("t1") {
             @Override public void run() {
                 doSomething();
@@ -294,7 +321,8 @@
      * (source-filename, line number) hard-coded in a class loaded from
      * pre-built test resources.
      */
-    public void testGetStackTrace_debugInfo() throws Exception {
+    @Test
+    public void getStackTrace_debugInfo() throws Exception {
         StackTraceElement ste = getStackTraceElement("debugInfo");
 
         // Verify that this StackTraceElement appears as we expect it to
@@ -315,7 +343,8 @@
      * a line number when debug info is missing for a method; the method is
      * declared on a class loaded from pre-built test resources.
      */
-    public void testGetStackTrace_noDebugInfo() throws Exception {
+    @Test
+    public void getStackTrace_noDebugInfo() throws Exception {
         StackTraceElement ste = getStackTraceElement("noDebugInfo");
 
         // Verify that this StackTraceElement appears as we expect it to
@@ -360,14 +389,15 @@
         // - ThreadTestHelper.{debugInfo,noDebugInfo}
         StackTraceElement result = stes[3];
 
-        // Sanity check before we return
+        // Confidence check before we return
         assertEquals(result.getClassName(), className);
         assertEquals(result.getMethodName(), methodName);
         assertFalse(result.isNativeMethod());
         return result;
     }
 
-    public void testGetAllStackTracesIncludesAllGroups() throws Exception {
+    @Test
+    public void getAllStackTracesIncludesAllGroups() throws Exception {
         final AtomicInteger visibleTraces = new AtomicInteger();
         ThreadGroup group = new ThreadGroup("1");
         Thread t2 = new Thread(group, "t2") {
@@ -383,7 +413,8 @@
     }
 
     // http://b/27748318
-    public void testNativeThreadNames() throws Exception {
+    @Test
+    public void nativeThreadNames() {
         String testResult = nativeTestNativeThreadNames();
         // Not using assertNull here because this results in a better error message.
         if (testResult != null) {
@@ -392,7 +423,8 @@
     }
 
     // http://b/29746125
-    public void testParkUntilWithUnderflowValue() throws Exception {
+    @Test
+    public void parkUntilWithUnderflowValue() throws Exception {
         final Thread current = Thread.currentThread();
 
         // watchdog to unpark the tread in case it will be parked
@@ -424,11 +456,52 @@
         watchdog.join();
     }
 
+    private class LongParker {
+        long nanos;
+        Thread parkerThread;
+        volatile boolean shouldPark = true;
+        LongParker(long nanos) {
+            this.nanos = nanos;
+            parkerThread = new Thread() {
+                @Override public void run() {
+                    while (shouldPark) {
+                        LockSupport.parkNanos(nanos);
+                    }
+                }
+            };
+            parkerThread.start();
+        }
+        void quit() throws InterruptedException {
+            shouldPark = false;
+            LockSupport.unpark(parkerThread);
+            parkerThread.join();
+        }
+    }
+
+    /**
+     * Test that large timeout arguments don't cause crashes. b/161006928 .
+     */
+    @Test
+    public void parkNanosHugeTimeout() throws InterruptedException {
+        for (long i = 0; i < 5; ++i) {
+            LongParker p = new LongParker(Long.MAX_VALUE - 800_000_000L * i);
+            Thread.sleep(10);
+            p.quit();
+        }
+        for (long i = 0; i < 5; ++i) {
+            LongParker p = new LongParker(Long.MAX_VALUE - 800_000_000_000_000_000L * i);
+            Thread.sleep(10);
+            p.quit();
+        }
+    }
+
+
     /**
      * Check that call Thread.start for already started thread
      * throws {@code IllegalThreadStateException}
      */
-    public void testThreadDoubleStart() {
+    @Test
+    public void threadDoubleStart() {
         final ReentrantLock lock = new ReentrantLock();
         Thread thread = new Thread() {
             public void run() {
@@ -460,7 +533,8 @@
      * Check that call Thread.start for already finished thread
      * throws {@code IllegalThreadStateException}
      */
-    public void testThreadRestart() {
+    @Test
+    public void threadRestart() {
         Thread thread = new Thread();
         thread.start();
         try {
@@ -474,6 +548,18 @@
         }
     }
 
+    @Test
+    public void resume_shouldThrowUnsupportedOperationException() {
+        Thread thread = new Thread();
+
+        try {
+            thread.resume();
+            fail();
+        } catch (UnsupportedOperationException ignored) {
+            // expected
+        }
+    }
+
     // This method returns {@code null} if all tests pass, or a non-null String containing
     // failure details if an error occured.
     private static native String nativeTestNativeThreadNames();
@@ -489,7 +575,7 @@
     }
 
     private class WakeupTestThread extends Thread {
-        public boolean done;
+        public volatile boolean done;
 
         public void run() {
             done = false;
diff --git a/luni/src/test/java/libcore/java/lang/UnicodeScriptTest.java b/luni/src/test/java/libcore/java/lang/UnicodeScriptTest.java
new file mode 100644
index 0000000..1ea57ab
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/UnicodeScriptTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.lang.Character.UnicodeScript;
+
+@RunWith(JUnit4.class)
+public class UnicodeScriptTest {
+
+    @Test
+    public void forName_shouldThrowIllegalArgumentException_whenNameIsUnknown() {
+        try {
+            UnicodeScript.forName("NON EXISTING SCRIPT NAME");
+            fail();
+        } catch (IllegalArgumentException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void forName_shouldThrowNPE_whenNullIsPassed() {
+        try {
+            UnicodeScript.forName(null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void forName_shouldAcceptAllEnumValues() {
+        for (UnicodeScript unicodeScript : UnicodeScript.values()) {
+            assertEquals(unicodeScript, UnicodeScript.forName(unicodeScript.name()));
+        }
+    }
+
+    @Test
+    public void of_shouldThrowIllegalArgumentException_whenInvalidUnicodePointPassed() {
+        try {
+            UnicodeScript.of(-1);
+            fail();
+        } catch (IllegalArgumentException ignored) {
+            // expected
+        }
+
+        try {
+            UnicodeScript.of(Character.MAX_CODE_POINT + 1);
+            fail();
+        } catch (IllegalArgumentException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void of_onWellKnownScripts() {
+        int asciiZero = '\u0030';
+        assertEquals(UnicodeScript.COMMON, UnicodeScript.of(asciiZero));
+        int asciiA = '\u0041';
+        assertEquals(UnicodeScript.LATIN, UnicodeScript.of(asciiA));
+        int asciiTilda = '\u007E';
+        assertEquals(UnicodeScript.COMMON, UnicodeScript.of(asciiTilda));
+        int asciiGbp = '\u00A3';
+        assertEquals(UnicodeScript.COMMON, UnicodeScript.of(asciiGbp));
+        int greekCapitalAlpha = '\u0391';
+        assertEquals(UnicodeScript.GREEK, UnicodeScript.of(greekCapitalAlpha));
+        int cyrillicCapitalA = '\u0410';
+        assertEquals(UnicodeScript.CYRILLIC, UnicodeScript.of(cyrillicCapitalA));
+        assertEquals(UnicodeScript.HAN, UnicodeScript.of('\u4E00'));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/VirtualMachineErrorTest.java b/luni/src/test/java/libcore/java/lang/VirtualMachineErrorTest.java
new file mode 100644
index 0000000..268584f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/VirtualMachineErrorTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.lang;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class VirtualMachineErrorTest {
+    @Test
+    public void constructorLThrowable() {
+        Throwable cause = new Exception("cause");
+        VirtualMachineError error = new TestVirtualMachineError(cause);
+
+        assertEquals(cause, error.getCause());
+        assertEquals("java.lang.Exception: cause", error.getMessage());
+    }
+
+    @Test
+    public void constructorLThrowable_withNull() {
+        VirtualMachineError error = new TestVirtualMachineError(null /* cause */);
+
+        assertNull(error.getCause());
+        assertNull(error.getMessage());
+    }
+
+    private static final class TestVirtualMachineError extends VirtualMachineError {
+        public TestVirtualMachineError(Throwable cause) {
+            super(cause);
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java b/luni/src/test/java/libcore/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java
index 7e3e982..c428fda 100644
--- a/luni/src/test/java/libcore/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java
+++ b/luni/src/test/java/libcore/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java
@@ -29,18 +29,4 @@
         assertSame(m, ex.element());
         assertEquals("poop", ex.foundType());
     }
-
-    public void testSerialization() throws Exception {
-        Method m = String.class.getMethod("length");
-        AnnotationTypeMismatchException original = new AnnotationTypeMismatchException(m, "poop");
-
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        try {
-            // AnnotationTypeMismatchException is broken: it's Serializable but has a non-transient
-            // non-serializable field of type Method.
-            new ObjectOutputStream(out).writeObject(original);
-            fail();
-        } catch (NotSerializableException expected) {
-        }
-    }
 }
diff --git a/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java
index 5ba87f9..5debdcb 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/CallSitesTest.java
@@ -69,7 +69,7 @@
         MutableCallSite site = new MutableCallSite(type);
         assertEquals(type, site.type());
         try {
-            int dummy = (int) site.getTarget().invokeExact(1, 1);
+            int fake = (int) site.getTarget().invokeExact(1, 1);
             fail();
         } catch (IllegalStateException e) {
             assertEquals("uninitialized call site", e.getMessage());
@@ -85,7 +85,7 @@
         VolatileCallSite site = new VolatileCallSite(type);
         assertEquals(type, site.type());
         try {
-            int dummy = (int) site.getTarget().invokeExact(1, 1);
+            int fake = (int) site.getTarget().invokeExact(1, 1);
             fail();
         } catch (IllegalStateException e) {
             assertEquals("uninitialized call site", e.getMessage());
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
index a8cfa24..aa3e983 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleCombinersTest.java
@@ -1357,7 +1357,7 @@
         } catch (WrongMethodTypeException expected) {
         }
 
-        // Sanity checks on other array types.
+        // Checks on other array types.
 
         MethodHandle target = MethodHandles.lookup().findStatic(
                 MethodHandleCombinersTest.class, "collectBoolean",
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
index 8e08a59..973c994 100644
--- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java
@@ -1212,14 +1212,14 @@
 
         // boolean -> int
         try {
-            int dummy = (int) mh.invoke("True");
+            int fake = (int) mh.invoke("True");
             fail();
         } catch (WrongMethodTypeException expected) {
         }
 
         // boolean -> Integer
         try {
-            Integer dummy = (Integer) mh.invoke("True");
+            Integer fake = (Integer) mh.invoke("True");
             fail();
         } catch (WrongMethodTypeException expected) {
         }
@@ -1232,14 +1232,14 @@
 
         // Boolean -> int
         try {
-            int dummy = (int) mh.invoke(false);
+            int fake = (int) mh.invoke(false);
             fail();
         } catch (WrongMethodTypeException expected) {
         }
 
         // Boolean -> Integer
         try {
-            Integer dummy = (Integer) mh.invoke("True");
+            Integer fake = (Integer) mh.invoke("True");
             fail();
         } catch (WrongMethodTypeException expected) {
         }
diff --git a/luni/src/test/java/libcore/java/lang/ref/ReferenceQueueTest.java b/luni/src/test/java/libcore/java/lang/ref/ReferenceQueueTest.java
index fba4781..399b360 100644
--- a/luni/src/test/java/libcore/java/lang/ref/ReferenceQueueTest.java
+++ b/luni/src/test/java/libcore/java/lang/ref/ReferenceQueueTest.java
@@ -36,6 +36,19 @@
         }
     }
 
+    /**
+     * Check that an actual execution time in msecs is "close enough" to expected.
+     * In general things should not finish early, so we allow little slop in that direction.
+     * However things can easily get delayed, so we allow more slop in that direction.
+     */
+    private void checkDuration(long expected, long actual) {
+        // The main need for slack is because tasks may not get scheduled right
+        // away. This is independent of the sleep/wait time, so we're using
+        // absolute rather than relative tolerances here.
+        assertTrue("Duration too short: " + actual + "ms", actual > expected - 10);
+        assertTrue("Duration too long: " + actual + "ms", actual < expected + 450);
+    }
+
     public void testRemoveWithVeryLargeTimeout() throws Exception {
         ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
         enqueueLater(referenceQueue, 500);
@@ -53,11 +66,12 @@
             }
         }, 500);
 
-        long startNanos = System.nanoTime();
-        referenceQueue.remove(1000);
-        long durationNanos = System.nanoTime() - startNanos;
-        long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
-        assertTrue(durationMillis > 750 && durationMillis < 1250);
+        final long startNanos = System.nanoTime();
+        final long timeoutMsec = 1000L;
+        referenceQueue.remove(timeoutMsec);
+        final long durationNanos = System.nanoTime() - startNanos;
+        final long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
+        checkDuration(timeoutMsec, durationMillis);
     }
 
     public void testRemoveWithImmediateResultAndNoTimeout() throws Exception {
@@ -74,22 +88,30 @@
 
     public void testRemoveWithDelayedResultAndNoTimeout() throws Exception {
         ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
-        enqueueLater(referenceQueue, 500);
-        long startNanos = System.nanoTime();
-        referenceQueue.remove();
-        long durationNanos = System.nanoTime() - startNanos;
-        long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
-        assertTrue(durationMillis > 250 && durationMillis < 750);
+        final long enqueueDelayMsec = 500L;
+        final long startNanos = System.nanoTime();
+        enqueueLater(referenceQueue, enqueueDelayMsec);
+        Object result = referenceQueue.remove();
+        assertNotNull(result);
+        final long durationNanos = System.nanoTime() - startNanos;
+        final long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
+        checkDuration(enqueueDelayMsec, durationMillis);
     }
 
     public void testRemoveWithDelayedResultAndTimeout() throws Exception {
         ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
-        enqueueLater(referenceQueue, 500);
-        long startNanos = System.nanoTime();
-        referenceQueue.remove(1000);
-        long durationNanos = System.nanoTime() - startNanos;
-        long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
-        assertTrue(durationMillis > 250 && durationMillis < 750);
+        final long enqueueDelayMsec = 500L;
+        final long startNanos = System.nanoTime();
+        enqueueLater(referenceQueue, enqueueDelayMsec);
+        Object result = referenceQueue.remove(1000);
+        if (result == null) {
+          // Also report the actual queue status.
+          assertNotNull(referenceQueue.poll());
+          assertNotNull(result);
+        }
+        final long durationNanos = System.nanoTime() - startNanos;
+        final long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
+        checkDuration(enqueueDelayMsec, durationMillis);
     }
 
     public void testCleanersCleaned() {
@@ -120,13 +142,13 @@
         assertTrue(countedDown);
     }
 
-    private void runLater(Runnable runnable, int delayMillis) {
-        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
+    private void runLater(Runnable runnable, long delayMillis) {
+        final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
         executor.schedule(runnable, delayMillis, TimeUnit.MILLISECONDS);
         executor.shutdown();
     }
 
-    private void enqueueLater(final ReferenceQueue<Object> queue, int delayMillis) {
+    private void enqueueLater(final ReferenceQueue<Object> queue, long delayMillis) {
         runLater(new Runnable() {
             @Override public void run() {
                 enqueue(queue);
diff --git a/luni/src/test/java/libcore/java/math/BigIntegerTest.java b/luni/src/test/java/libcore/java/math/BigIntegerTest.java
index 6c1d11e..4cb5ced 100644
--- a/luni/src/test/java/libcore/java/math/BigIntegerTest.java
+++ b/luni/src/test/java/libcore/java/math/BigIntegerTest.java
@@ -199,14 +199,14 @@
     private void try_gcd_variants(BigInteger arg1, BigInteger arg2, BigInteger result)
             throws Exception {
         // Test both argument orders, and all 4 combinations of negation.
-        assertEquals(arg1.gcd(arg2), result);
-        assertEquals(arg2.gcd(arg1), result);
-        assertEquals(arg1.negate().gcd(arg2), result);
-        assertEquals(arg2.gcd(arg1.negate()), result);
-        assertEquals(arg1.gcd(arg2.negate()), result);
-        assertEquals(arg2.negate().gcd(arg1), result);
-        assertEquals(arg1.negate().gcd(arg2.negate()), result);
-        assertEquals(arg2.negate().gcd(arg1.negate()), result);
+        assertEquals(result, arg1.gcd(arg2));
+        assertEquals(result, arg2.gcd(arg1));
+        assertEquals(result, arg1.negate().gcd(arg2));
+        assertEquals(result, arg2.gcd(arg1.negate()));
+        assertEquals(result, arg1.gcd(arg2.negate()));
+        assertEquals(result, arg2.negate().gcd(arg1));
+        assertEquals(result, arg1.negate().gcd(arg2.negate()));
+        assertEquals(result, arg2.negate().gcd(arg1.negate()));
     }
 
     /**
@@ -226,4 +226,91 @@
         try_gcd_variants(large, BigInteger.valueOf(5), BigInteger.ONE);
         try_gcd_variants(large, BigInteger.ZERO, large);
     }
+
+    public void test_byteValueExact() throws Exception {
+        for (int i = -300; i != 300; i += 10) {
+            try {
+                assertEquals(i, BigInteger.valueOf(i).byteValueExact());
+                assertTrue("Missing byteValueExact exception on " + i,
+                        i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE);
+            } catch (ArithmeticException e) {
+                assertTrue("Unexpected byteValueExact exception on " + i,
+                        i < Byte.MIN_VALUE || i > Byte.MAX_VALUE);
+            }
+        }
+        try {
+            BigInteger.ONE.shiftLeft(1000).byteValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+        try {
+            BigInteger.ONE.negate().shiftLeft(1000).byteValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+    }
+
+    public void test_shortValueExact() throws Exception {
+        for (int i = -100_000; i != 100_000; i += 10_000) {
+            try {
+                assertEquals(i, BigInteger.valueOf(i).shortValueExact());
+                assertTrue("Missing shortValueExact exception on " + i,
+                        i >= Short.MIN_VALUE && i <= Short.MAX_VALUE);
+            } catch (ArithmeticException e) {
+                assertTrue("Unexpected shortValueExact exception on " + i,
+                        i < Short.MIN_VALUE || i > Short.MAX_VALUE);
+            }
+        }
+        try {
+            BigInteger.ONE.shiftLeft(1000).shortValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+        try {
+            BigInteger.ONE.negate().shiftLeft(1000).shortValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+    }
+
+    public void test_intValueExact() throws Exception {
+        for (long i = -10_000_000_000L; i != 10_000_000_000L; i += 1_000_000_000L) {
+            try {
+                assertEquals(i, BigInteger.valueOf(i).intValueExact());
+                assertTrue("Missing intValueExact exception on " + i,
+                        i >= Integer.MIN_VALUE && i <= Integer.MAX_VALUE);
+            } catch (ArithmeticException e) {
+                assertTrue("Unexpected intValueExact exception on " + i,
+                        i < Integer.MIN_VALUE || i > Integer.MAX_VALUE);
+            }
+        }
+        try {
+            BigInteger.ONE.shiftLeft(1000).intValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+        try {
+            BigInteger.ONE.negate().shiftLeft(1000).intValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+    }
+
+    public void test_longValueExact() throws Exception {
+        BigInteger min = BigInteger.valueOf(Long.MIN_VALUE);
+        BigInteger max = BigInteger.valueOf(Long.MAX_VALUE);
+        for (long i = -16; i != 16; ++i) {
+            BigInteger big = BigInteger.valueOf(i).shiftLeft(61);
+            try {
+                assertEquals(i << 61, big.longValueExact());
+                assertTrue("Missing longValueExact exception on " + i,
+                        big.compareTo(min) >= 0 && big.compareTo(max) <= 0);
+            } catch (ArithmeticException e) {
+                assertTrue("Unexpected longValueExact exception on " + i,
+                        big.compareTo(min) < 0 || big.compareTo(max) > 0);
+            }
+        }
+        try {
+            BigInteger.ONE.shiftLeft(1000).longValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+        try {
+            BigInteger.ONE.negate().shiftLeft(1000).longValueExact();
+            fail("Expected ArithmeticException");
+        } catch (ArithmeticException e) {}
+    }
 }
diff --git a/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java b/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java
index 77eb571..7c47206 100644
--- a/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java
+++ b/luni/src/test/java/libcore/java/net/AbstractCookiesTest.java
@@ -1310,16 +1310,16 @@
                 new String[][] { cookies[0], cookies[1] }, responseHeaders,
                 null);
 
-        HashMap<String, List<String>> dummyMap = new HashMap<String, List<String>>();
+        HashMap<String, List<String>> fakeMap = new HashMap<String, List<String>>();
         Map<String, List<String>> map = manager.get(new URI("http://a.b.c/"),
-                dummyMap);
+                fakeMap);
 
         assertEquals(1, map.size());
         List<String> list = map.get("Cookie");
         assertEquals(1, list.size());
 
         // requires path of cookie is the prefix of uri
-        map = manager.get(new URI("http://a.b.c/te"), dummyMap);
+        map = manager.get(new URI("http://a.b.c/te"), fakeMap);
         list = map.get("Cookie");
         assertEquals(1, list.size());
         assertTrue(list.get(0).contains("PREF=test"));
@@ -1330,7 +1330,7 @@
         // ,no matter the value cookie-key
         responseHeaders = addCookie(new String[][] { cookies[2] });
         manager = store(new String[][] { cookies[2] }, responseHeaders, null);
-        map = manager.get(new URI("http://a.beg.com/test"), dummyMap);
+        map = manager.get(new URI("http://a.beg.com/test"), fakeMap);
         list = map.get("Cookie");
         assertEquals(1, list.size());
         assertTrue(list.get(0).startsWith("$Version=\"1\""));
@@ -1390,9 +1390,9 @@
      * @since 1.6
      */
     public void testCookieManager_LCookieStore_LCookiePolicy() {
-        class DummyStore implements CookieStore {
+        class FakeStore implements CookieStore {
             public String getName() {
-                return "A dummy store";
+                return "A fake store";
             }
 
             public void add(URI uri, HttpCookie cookie) {
@@ -1419,10 +1419,10 @@
                 return false;
             }
         }
-        CookieStore store = new DummyStore();
+        CookieStore store = new FakeStore();
         CookieManager cookieManager = new CookieManager(store,
                 CookiePolicy.ACCEPT_ALL);
-        assertEquals("A dummy store", ((DummyStore) cookieManager
+        assertEquals("A fake store", ((FakeStore) cookieManager
                 .getCookieStore()).getName());
         assertSame(store, cookieManager.getCookieStore());
     }
diff --git a/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java b/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java
index 74948f6..b3855ec 100644
--- a/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/FtpURLConnectionTest.java
@@ -141,7 +141,7 @@
         URL fileUrl = addFileEntry(FILE_PATH, fileContents);
         URLConnection connection = fileUrl.openConnection(proxy);
         assertContents(fileContents, connection.getInputStream());
-        // Sanity check that NO_PROXY covers the Type.DIRECT case
+        // Check that NO_PROXY covers the Type.DIRECT case
         assertEquals(Proxy.Type.DIRECT, proxy.type());
     }
 
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 6bbfe01..7d59ec0 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -266,9 +266,9 @@
     public void test_isReachable_neverThrows() throws Exception {
         InetAddress inetAddress = InetAddress.getByName("www.google.com");
 
-        final NetworkInterface netIf = NetworkInterface.getByName("dummy0");
+        final NetworkInterface netIf = NetworkInterface.getByName("fake0");
         if (netIf == null) {
-            System.logI("Skipping test_isReachable_neverThrows because dummy0 isn't available");
+            System.logI("Skipping test_isReachable_neverThrows because fake0 isn't available");
             return;
         }
 
diff --git a/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java b/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java
index 8375421..555654e 100644
--- a/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java
+++ b/luni/src/test/java/libcore/java/net/NetworkInterfaceTest.java
@@ -230,7 +230,7 @@
         Os originalOs = Libcore.getOs();
         Os mockOs = Mockito.mock(Os.class);
 
-        try {
+        try {    
             Mockito.when(mockOs.getifaddrs()).thenReturn(new StructIfaddrs[] {
                 new StructIfaddrs("dummy0:1", 0, null, null, null, null),
             });
diff --git a/luni/src/test/java/libcore/java/net/OldSocketTest.java b/luni/src/test/java/libcore/java/net/OldSocketTest.java
index 644fe52..f28edd0 100644
--- a/luni/src/test/java/libcore/java/net/OldSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldSocketTest.java
@@ -1621,7 +1621,7 @@
         // run these tests. For now run on all platforms until we find those
         // that do not support urgent data
         String platform = System.getProperty("os.name");
-        if (!platform.equals("Dummy")) {
+        if (!platform.equals("Fake")) {
             // validate that when OOBInline is false that any urgent data
             // is silently ignored
             String urgentData = "U";
diff --git a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
index 4171062..397ad359 100644
--- a/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
+++ b/luni/src/test/java/libcore/java/net/ServerSocketConcurrentCloseTest.java
@@ -167,8 +167,8 @@
                         + "Server thread's stackTrace: " + serverStackTrace);
             }
             assertTrue(serverRunnable.isShutdown());
-            // Sanity check to ensure the threads don't live into the next iteration. This should
-            // be quick because we only get here if shutdownLatch reached 0 within the time limit.
+            // Ensure the threads don't live into the next iteration. This should be quick because
+            // we only get here if shutdownLatch reached 0 within the time limit.
             serverThread.join();
             clientThread.join();
             return serverRunnable.numSuccessfulConnections.get();
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index 3e1c1a6..df12a29 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -598,13 +598,12 @@
     public void testSocketTestAllAddresses() throws Exception {
         // Socket Ctor should try all sockets.
         //
-        // This test creates a server socket bound to 127.0.0.1 or ::1 only, and connects using a
+        // This test creates server sockets bound to 127.0.0.1 and ::1, and connects using a
         // hostname that resolves to both addresses. We should be able to connect to the server
         // socket in either setup.
         final String loopbackHost = ALL_LOOPBACK_HOSTNAME;
 
-        assertTrue("Loopback DNS record is unreachable or is invalid.", checkLoopbackHost(
-                loopbackHost));
+        checkLoopbackHost(loopbackHost);
 
         final int port = 9999;
         for (InetAddress addr : new InetAddress[]{ Inet4Address.LOOPBACK, Inet6Address.LOOPBACK }) {
@@ -622,15 +621,16 @@
         }
     }
 
-    /** Confirm the supplied hostname maps to only loopback addresses. */
-    private static boolean checkLoopbackHost(String host) {
-        try {
-            List<InetAddress> addrs = Arrays.asList(InetAddress.getAllByName(host));
-            return addrs.stream().allMatch(InetAddress::isLoopbackAddress) &&
-                    addrs.contains(Inet4Address.LOOPBACK) && addrs.contains(Inet6Address.LOOPBACK);
-        } catch (UnknownHostException e) {
-            return false;
-        }
+    /** Confirm the supplied hostname maps to only loopback addresses, both IPv4 and IPv6. */
+    private static void checkLoopbackHost(String host) throws UnknownHostException {
+        InetAddress[] addrArray = InetAddress.getAllByName(host);
+        final String addressesString = Arrays.toString(addrArray);
+        List<InetAddress> addrs = Arrays.asList(addrArray);
+        final String msg = ALL_LOOPBACK_HOSTNAME
+                + " must only return loopback addresses, both IPv4 and IPv6. Got: "
+                + addressesString;
+        assertTrue(msg, addrs.stream().allMatch(InetAddress::isLoopbackAddress)
+                && addrs.contains(Inet4Address.LOOPBACK) && addrs.contains(Inet6Address.LOOPBACK));
     }
 
     private static boolean canConnect(String host, int port) {
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index ddd447a..aecc1ad 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -900,9 +900,9 @@
 
     @Test public void getFileNameMap_compositeExtension_customMimeMap() {
         MimeMap testMimeMap = MimeMap.builder()
-                .put("text/html", "html")
-                .put("application/gzip", "gz")
-                .put("application/tar+gzip", "tar.gz")
+                .addMimeMapping("text/html", "html")
+                .addMimeMapping("application/gzip", "gz")
+                .addMimeMapping("application/tar+gzip", "tar.gz")
                 .build();
         MimeMap defaultMimeMap = MimeMap.getDefault();
         MimeMap.setDefaultSupplier(() -> testMimeMap);
@@ -3176,7 +3176,7 @@
      * Android has its own API / implementation for certificate pinning. We can't
      * easily test that there is *no* code path that would invoke OkHttp's certificate
      * pinning logic, so this test only covers the *common* code path of a
-     * HttpsURLConnection as a sanity check.
+     * HttpsURLConnection as a confidence check.
      *
      * <p>To check whether OkHttp performs certificate pinning under the hood, this
      * test disables two {@link Platform} methods. In OkHttp 2.7.5, these two methods
diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
index 24fdc74..a151da3 100644
--- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelMulticastTest.java
@@ -36,6 +36,8 @@
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.concurrent.TimeUnit;
+import java.net.ProtocolFamily;
+import java.net.StandardProtocolFamily;
 
 import libcore.io.IoBridge;
 
@@ -83,7 +85,7 @@
     @Override
     protected void setUp() throws Exception {
         // The loopback interface isn't actually useful for sending/receiving multicast messages
-        // but it can be used as a dummy for tests where that does not matter.
+        // but it can be used as a fake for tests where that does not matter.
         loopbackInterface = NetworkInterface.getByInetAddress(InetAddress.getLoopbackAddress());
         assertNotNull(loopbackInterface);
         assertTrue(loopbackInterface.isLoopback());
@@ -219,25 +221,25 @@
     }
 
     public void test_joinAnySource_IPv4() throws Exception {
-        check_joinAnySource(GOOD_MULTICAST_IPv4, BAD_MULTICAST_IPv4, ipv4NetworkInterface);
+        check_joinAnySource(GOOD_MULTICAST_IPv4, BAD_MULTICAST_IPv4, ipv4NetworkInterface, StandardProtocolFamily.INET);
     }
 
     public void test_joinAnySource_IPv6() throws Exception {
-        check_joinAnySource(GOOD_MULTICAST_IPv6, BAD_MULTICAST_IPv6, ipv6NetworkInterface);
+        check_joinAnySource(GOOD_MULTICAST_IPv6, BAD_MULTICAST_IPv6, ipv6NetworkInterface, StandardProtocolFamily.INET6);
     }
 
     private void check_joinAnySource(InetAddress group, InetAddress group2,
-            NetworkInterface networkInterface) throws Exception {
+            NetworkInterface networkInterface, ProtocolFamily protocolFamily) throws Exception {
         if (!supportsMulticast) {
             return;
         }
         // Set up a receiver join the group on ipv4NetworkInterface
-        DatagramChannel receiverChannel = createReceiverChannel();
+        DatagramChannel receiverChannel = createReceiverChannel(protocolFamily);
         InetSocketAddress localAddress = (InetSocketAddress) receiverChannel.getLocalAddress();
         receiverChannel.join(group, networkInterface);
 
         String msg = "Hello World";
-        createChannelAndSendMulticastMessage(group, localAddress.getPort(), msg, networkInterface);
+        createChannelAndSendMulticastMessage(group, localAddress.getPort(), msg, networkInterface, protocolFamily);
 
         // now verify that we received the data as expected
         ByteBuffer recvBuffer = ByteBuffer.allocate(100);
@@ -247,7 +249,7 @@
         // now verify that we didn't receive the second message
         String msg2 = "Hello World - Different Group";
         createChannelAndSendMulticastMessage(
-                group2, localAddress.getPort(), msg2, networkInterface);
+                group2, localAddress.getPort(), msg2, networkInterface, protocolFamily);
         recvBuffer.position(0);
         checkNoDatagramReceived(receiverChannel);
 
@@ -396,6 +398,13 @@
         return dc;
     }
 
+    private DatagramChannel createReceiverChannel(ProtocolFamily protocolFamily) throws Exception {
+        DatagramChannel dc = DatagramChannel.open(protocolFamily);
+        dc.bind(null /* leave the OS to determine the port, and use the wildcard address */);
+        configureChannelForReceiving(dc);
+        return dc;
+    }
+
     public void test_joinAnySource_multiple_joins_IPv4()
             throws Exception {
         check_joinAnySource_multiple_joins(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
@@ -425,21 +434,21 @@
     }
 
     public void test_joinAnySource_multicastLoopOption_IPv4() throws Exception {
-        check_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv4, ipv4NetworkInterface);
+        check_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv4, ipv4NetworkInterface, StandardProtocolFamily.INET);
     }
 
     public void test_multicastLoopOption_IPv6() throws Exception {
-        check_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv6, ipv6NetworkInterface);
+        check_joinAnySource_multicastLoopOption(GOOD_MULTICAST_IPv6, ipv6NetworkInterface, StandardProtocolFamily.INET6);
     }
 
     private void check_joinAnySource_multicastLoopOption(InetAddress group,
-            NetworkInterface networkInterface) throws Exception {
+            NetworkInterface networkInterface, ProtocolFamily protocolFamily) throws Exception {
         if (!supportsMulticast) {
             return;
         }
         final String message = "Hello, world!";
 
-        DatagramChannel dc = createReceiverChannel();
+        DatagramChannel dc = createReceiverChannel(protocolFamily);
         dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true /* enable loop */);
         dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
         configureChannelForReceiving(dc);
@@ -1290,6 +1299,17 @@
         dc.close();
     }
 
+
+    private static void createChannelAndSendMulticastMessage(
+            InetAddress group, int port, String msg, NetworkInterface sendingInterface, ProtocolFamily protocolFamily)
+            throws IOException {
+        // Any datagram socket can send to a group. It does not need to have joined the group.
+        DatagramChannel dc = DatagramChannel.open(protocolFamily);
+        BindableChannel channel = new BindableChannel(dc, sendingInterface);
+        channel.sendMulticastMessage(msg, new InetSocketAddress(group, port));
+        dc.close();
+    }
+
     /**
      * A {@link DatagramChannel} which is optionally bound to a {@link NetworkInterface}.
      */
diff --git a/luni/src/test/java/libcore/java/nio/file/LinkPermissionTest.java b/luni/src/test/java/libcore/java/nio/file/LinkPermissionTest.java
index a0e75fe..c289271 100644
--- a/luni/src/test/java/libcore/java/nio/file/LinkPermissionTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/LinkPermissionTest.java
@@ -25,7 +25,7 @@
     public void test_constructor$String() {
         // Only "hard" and "symbolic" are the supported permission target names.
         LinkPermission linkPermission = new LinkPermission("hard");
-        // Sanity check that getName() doesn't throw.
+        // Check that getName() doesn't throw.
         linkPermission.getName();
 
         linkPermission = new LinkPermission("symbolic");
diff --git a/luni/src/test/java/libcore/java/nio/file/LinuxPathTest.java b/luni/src/test/java/libcore/java/nio/file/LinuxPathTest.java
index 458804c..55e5f81 100644
--- a/luni/src/test/java/libcore/java/nio/file/LinuxPathTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/LinuxPathTest.java
@@ -59,35 +59,35 @@
     /**
      * CTS doesn't allow creating files in the test directory, however, Vogar allows creation of
      * new files in the test directory. Therefore, for the tests which don't require write
-     * permission, dummyPath would serve the purpose, however, for the others, {@link
+     * permission, fakePath would serve the purpose, however, for the others, {@link
      * FilesSetup#getTestDirPath()} should be used.
      */
-    private static final Path dummyPath = Paths.get("dummyPath");
+    private static final Path fakePath = Paths.get("fakePath");
 
     @Test
     public void test_getFileSystem() {
-        assertTrue(dummyPath.getFileSystem().provider() instanceof
+        assertTrue(fakePath.getFileSystem().provider() instanceof
                 sun.nio.fs.LinuxFileSystemProvider);
     }
 
     @Test
     public void test_isAbsolute() {
-        assertFalse(dummyPath.isAbsolute());
-        Path absolutePath = dummyPath.toAbsolutePath();
+        assertFalse(fakePath.isAbsolute());
+        Path absolutePath = fakePath.toAbsolutePath();
         assertTrue(absolutePath.isAbsolute());
     }
 
     @Test
     public void test_getRoot() {
-        assertEquals(Paths.get("/"), dummyPath.toAbsolutePath().getRoot());
-        assertNull(dummyPath.getRoot());
+        assertEquals(Paths.get("/"), fakePath.toAbsolutePath().getRoot());
+        assertNull(fakePath.getRoot());
     }
 
     @Test
     public void test_getFileName() {
-        assertEquals(dummyPath, dummyPath.getFileName());
-        assertEquals(dummyPath, dummyPath.toAbsolutePath().getFileName());
-        assertNull(dummyPath.getRoot());
+        assertEquals(fakePath, fakePath.getFileName());
+        assertEquals(fakePath, fakePath.toAbsolutePath().getFileName());
+        assertNull(fakePath.getRoot());
         assertEquals(Paths.get("data"), Paths.get("/data").getFileName());
         assertEquals(Paths.get("data"), Paths.get("/data/").getFileName());
         assertEquals(Paths.get(".."), Paths.get("/data/dir1/..").getFileName());
@@ -95,7 +95,7 @@
 
     @Test
     public void test_getParent() {
-        assertNull(dummyPath.getParent());
+        assertNull(fakePath.getParent());
         assertEquals(Paths.get("rootDir"), Paths.get("rootDir/dir").getParent());
     }
 
@@ -153,7 +153,7 @@
     @Test
     public void test_startsWith$String() {
         assertTrue(Paths.get("t1/t2").startsWith("t1"));
-        assertTrue(dummyPath.toAbsolutePath().startsWith("/"));
+        assertTrue(fakePath.toAbsolutePath().startsWith("/"));
         assertTrue(Paths.get("t1/t2/t3").startsWith("t1/t2"));
         assertFalse(Paths.get("t1/t2").startsWith("t2"));
     }
@@ -166,7 +166,7 @@
     @Test
     public void test_startsWith$Path() {
         assertTrue(Paths.get("t1/t2").startsWith(Paths.get("t1")));
-        assertTrue(dummyPath.toAbsolutePath().startsWith(Paths.get("/")));
+        assertTrue(fakePath.toAbsolutePath().startsWith(Paths.get("/")));
         assertTrue(Paths.get("t1/t2/t3").startsWith(Paths.get("t1/t2")));
         assertFalse(Paths.get("t1/t2").startsWith(Paths.get("t2")));
     }
@@ -226,7 +226,7 @@
 
     @Test(expected = NullPointerException.class)
     public void test_resolve$Path_NPE() {
-        dummyPath.resolve((Path)null);
+        fakePath.resolve((Path)null);
     }
 
     @Test
@@ -240,7 +240,7 @@
 
     @Test(expected = NullPointerException.class)
     public void test_resolve$String_NPE() {
-        dummyPath.resolve((String)null);
+        fakePath.resolve((String)null);
     }
 
     @Test
@@ -255,7 +255,7 @@
 
     @Test(expected = NullPointerException.class)
     public void test_resolveSibling$String_Path() {
-        dummyPath.resolveSibling((Path) null);
+        fakePath.resolveSibling((Path) null);
     }
 
     @Test
@@ -271,7 +271,7 @@
 
     @Test(expected = NullPointerException.class)
     public void test_resolveSibling$String_NPE() {
-        dummyPath.resolveSibling((String)null);
+        fakePath.resolveSibling((String)null);
     }
 
     @Test
@@ -296,12 +296,12 @@
 
     @Test(expected = NullPointerException.class)
     public void test_relativize_NPE() {
-        dummyPath.relativize(null);
+        fakePath.relativize(null);
     }
 
     @Test
     public void test_toURI() throws URISyntaxException {
-        assertEquals(new URI("file://" + dummyPath.toAbsolutePath().toString()), dummyPath.toUri());
+        assertEquals(new URI("file://" + fakePath.toAbsolutePath().toString()), fakePath.toUri());
         assertEquals(new URI("file:///"), Paths.get("/").toUri());
         assertEquals(new URI("file:///dir/.."), Paths.get(("/dir/..")).toUri());
         assertEquals(new URI("file:///../"), Paths.get(("/..")).toUri());
@@ -314,15 +314,15 @@
 
     @Test
     public void test_toAbsolutePath() {
-        assertFalse(dummyPath.isAbsolute());
-        assertTrue(dummyPath.toAbsolutePath().isAbsolute());
+        assertFalse(fakePath.isAbsolute());
+        assertTrue(fakePath.toAbsolutePath().isAbsolute());
     }
 
     @Test
     public void test_toRealPath() throws IOException {
         // When file doesn't exist.
         try {
-            dummyPath.toRealPath();
+            fakePath.toRealPath();
             fail();
         } catch (NoSuchFileException expected) {}
         Files.createFile(filesSetup.getTestPath());
@@ -356,8 +356,8 @@
 
     @Test
     public void test_toFile() {
-        File file = dummyPath.toFile();
-        assertEquals(dummyPath.toAbsolutePath().toString(), file.getAbsolutePath());
+        File file = fakePath.toFile();
+        assertEquals(fakePath.toAbsolutePath().toString(), file.getAbsolutePath());
     }
 
     @Test
diff --git a/luni/src/test/java/libcore/java/security/AccessControlExceptionTest.java b/luni/src/test/java/libcore/java/security/AccessControlExceptionTest.java
new file mode 100644
index 0000000..e12f8fa
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/AccessControlExceptionTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.AccessControlException;
+import java.security.AllPermission;
+import java.security.Permission;
+
+@RunWith(JUnit4.class)
+public class AccessControlExceptionTest {
+
+    @Test
+    public void testConstructor() {
+        String msg = "test message";
+        Permission permission = new AllPermission();
+
+        try {
+            throw new AccessControlException(msg);
+        } catch (AccessControlException e) {
+            assertEquals(msg, e.getMessage());
+        }
+
+        try {
+            throw new AccessControlException(msg, permission);
+        } catch (AccessControlException e) {
+            assertEquals(msg, e.getMessage());
+            assertSame(permission, e.getPermission());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/AccessControllerTest.java b/luni/src/test/java/libcore/java/security/AccessControllerTest.java
index 5746abd..678be72 100644
--- a/luni/src/test/java/libcore/java/security/AccessControllerTest.java
+++ b/luni/src/test/java/libcore/java/security/AccessControllerTest.java
@@ -16,25 +16,36 @@
 
 package libcore.java.security;
 
+import static org.junit.Assert.assertEquals;
+
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.DomainCombiner;
 import java.security.Permission;
 import java.security.Permissions;
 import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.security.ProtectionDomain;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
 import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * Android doesn't fully support access controller. This tests that actions are
  * passed through without permission enforcement.
  */
-public final class AccessControllerTest extends TestCase {
+@RunWith(JUnit4.class)
+public final class AccessControllerTest {
 
-    public void testDoPrivilegedWithCombiner() {
-        final Permission permission = new RuntimePermission("do stuff");
+    @Test
+    public void testDoPrivilegedWithCombiner() throws Exception {
         final DomainCombiner union = new DomainCombiner() {
             @Override
             public ProtectionDomain[] combine(ProtectionDomain[] a, ProtectionDomain[] b) {
@@ -46,28 +57,61 @@
         AccessControlContext accessControlContext = new AccessControlContext(
                 new AccessControlContext(new ProtectionDomain[] { protectionDomain }), union);
 
+        assertActionRun(action -> AccessController.doPrivileged(
+                (PrivilegedAction<Void>) action::get));
+
+        assertActionRun(action -> AccessController.doPrivileged(
+                (PrivilegedAction<Void>) action::get, accessControlContext));
+
+        assertActionRun(action -> {
+            try {
+                AccessController.doPrivileged((PrivilegedExceptionAction<Void>) action::get);
+            } catch (PrivilegedActionException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        assertActionRun(action -> {
+            try {
+                AccessController.doPrivileged(
+                        (PrivilegedExceptionAction<Void>) action::get, accessControlContext);
+            } catch (PrivilegedActionException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        assertActionRun(action -> AccessController.doPrivilegedWithCombiner(
+                (PrivilegedAction<Void>) action::get));
+
+        assertActionRun(action -> {
+            try {
+                AccessController.doPrivilegedWithCombiner(
+                        (PrivilegedExceptionAction<Void>) action::get);
+            } catch (PrivilegedActionException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    private static void assertActionRun(Consumer<Supplier<Void>> runner) {
+        final Permission permission = new RuntimePermission("do stuff");
+
         final AtomicInteger actionCount = new AtomicInteger();
 
-        AccessController.doPrivileged(new PrivilegedAction<Void>() {
-            @Override
-            public Void run() {
+        runner.accept(() -> {
+            assertEquals(null, AccessController.getContext().getDomainCombiner());
+            AccessController.getContext().checkPermission(permission);
+
+            // Calling doPrivileged again would have exercised the combiner
+            runner.accept(() -> {
+                actionCount.incrementAndGet();
                 assertEquals(null, AccessController.getContext().getDomainCombiner());
                 AccessController.getContext().checkPermission(permission);
-
-                // Calling doPrivileged again would have exercised the combiner
-                AccessController.doPrivileged(new PrivilegedAction<Void>() {
-                    @Override
-                    public Void run() {
-                        actionCount.incrementAndGet();
-                        assertEquals(null, AccessController.getContext().getDomainCombiner());
-                        AccessController.getContext().checkPermission(permission);
-                        return null;
-                    }
-                });
-
                 return null;
-            }
-        }, accessControlContext);
+            });
+
+            return null;
+        });
 
         assertEquals(1, actionCount.get());
     }
diff --git a/luni/src/test/java/libcore/java/security/AllPermissionTest.java b/luni/src/test/java/libcore/java/security/AllPermissionTest.java
new file mode 100644
index 0000000..0d4f4b2
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/AllPermissionTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.AllPermission;
+
+@RunWith(JUnit4.class)
+public class AllPermissionTest {
+
+    @Test
+    public void testGetAction() {
+        String action = "test action";
+        AllPermission permission = new AllPermission("Test permission", action);
+        // Action value is discarded and returns null here.
+        assertNull(permission.getActions());
+    }
+
+    @Test
+    public void testImplies() {
+        AllPermission permission = new AllPermission();
+        AllPermission permission2 = new AllPermission();
+        assertTrue(permission.implies(null));
+        assertTrue(permission.implies(permission));
+        assertTrue(permission.implies(permission2));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/AuthProviderTest.java b/luni/src/test/java/libcore/java/security/AuthProviderTest.java
new file mode 100644
index 0000000..ecc7da8
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/AuthProviderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.AuthProvider;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+
+@RunWith(JUnit4.class)
+public class AuthProviderTest {
+
+    private static class TestAuthProvider extends AuthProvider {
+        final String name;
+
+        protected TestAuthProvider(String name, double version, String info) {
+            super(name, version, info);
+            this.name = name;
+        }
+
+        @Override
+        public void login(Subject subject, CallbackHandler handler) {
+        }
+
+        @Override
+        public void logout() {
+        }
+
+        @Override
+        public void setCallbackHandler(CallbackHandler handler) {
+        }
+    }
+
+    @Test
+    public void testConstructor() {
+        TestAuthProvider provider = new TestAuthProvider("test", 1.0d, "info");
+        assertEquals("test", provider.name);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/BasicPermissionTest.java b/luni/src/test/java/libcore/java/security/BasicPermissionTest.java
new file mode 100644
index 0000000..e45c3ff
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/BasicPermissionTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.AllPermission;
+import java.security.BasicPermission;
+
+@RunWith(JUnit4.class)
+public class BasicPermissionTest {
+
+    private static class TestPermission extends BasicPermission {
+        public TestPermission(String name) {
+            super(name);
+        }
+    }
+
+    @Test
+    public void testImplies() {
+        BasicPermission permission = new TestPermission("name");
+        AllPermission permission2 = new AllPermission();
+        assertTrue(permission.implies(null));
+        assertTrue(permission.implies(permission));
+        assertTrue(permission.implies(permission2));
+    }
+
+    @Test
+    public void testCheckGuard_doesntThrow() {
+        BasicPermission permission = new TestPermission("name");
+        permission.checkGuard(null);
+        permission.checkGuard(new Object());
+        permission.checkGuard(permission);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/CodeSourceTest.java b/luni/src/test/java/libcore/java/security/CodeSourceTest.java
new file mode 100644
index 0000000..01481f8
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/CodeSourceTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.KeyStore;
+import java.security.Timestamp;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+
+import sun.security.provider.certpath.X509CertPath;
+
+@RunWith(JUnit4.class)
+public class CodeSourceTest {
+
+
+    private static final String PATH = "file://invalid_cert_path";
+
+    private CodeSource codeSource;
+
+    @Before
+    public void setUp() throws Exception {
+        CodeSigner codeSigner = createCodeSigner();
+        codeSource = new CodeSource(new URL(PATH), new CodeSigner[] { codeSigner });
+    }
+
+    private static CodeSigner createCodeSigner() throws Exception {
+        KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
+        keyStore.load(null);
+        // Get a X509Certificate from the keyStore
+        X509Certificate cert = null;
+        for (Enumeration<String> aliases = keyStore.aliases(); aliases.hasMoreElements(); ) {
+            String alias = aliases.nextElement();
+            Certificate certificate = keyStore.getCertificate(alias);
+            assertTrue(certificate instanceof X509Certificate);
+            cert = (X509Certificate) certificate;
+            break;
+        }
+
+        assertNotNull(cert);
+        X509CertPath certPath = new X509CertPath(List.of(cert));
+        return new CodeSigner(certPath, new Timestamp(new Date(), certPath));
+    }
+
+    @Test
+    public void testGetCerificates() {
+        assertNull(codeSource.getCertificates());
+    }
+
+    @Test
+    public void testGetCodeSigners() {
+        assertNull(codeSource.getCodeSigners());
+    }
+
+    @Test
+    public void testGetLocation() throws MalformedURLException {
+        assertEquals(new URL(PATH), codeSource.getLocation());
+    }
+
+    @Test
+    public void testImplies() {
+        assertTrue(codeSource.implies(null));
+        assertTrue(codeSource.implies(codeSource));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/PermissionCollectionTest.java b/luni/src/test/java/libcore/java/security/PermissionCollectionTest.java
new file mode 100644
index 0000000..1fb39e6
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/PermissionCollectionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.Enumeration;
+
+@RunWith(JUnit4.class)
+public class PermissionCollectionTest {
+
+    private static class TestPermissionCollection extends PermissionCollection {
+
+        @Override
+        public void add(Permission permission) {
+        }
+
+        @Override
+        public boolean implies(Permission permission) {
+            return true;
+        }
+
+        @Override
+        public Enumeration<Permission> elements() {
+            return null;
+        }
+    }
+
+    @Test
+    public void testSetReadOnly() {
+        PermissionCollection permissionCollection = new TestPermissionCollection();
+        // isReadOnly() always return true
+        assertTrue(permissionCollection.isReadOnly());
+        permissionCollection.setReadOnly();
+        assertTrue(permissionCollection.isReadOnly());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/PolicySpiTest.java b/luni/src/test/java/libcore/java/security/PolicySpiTest.java
new file mode 100644
index 0000000..52d5023
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/PolicySpiTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.PolicySpi;
+import java.security.ProtectionDomain;
+
+@RunWith(JUnit4.class)
+public class PolicySpiTest {
+
+    private static class TestPolicySpi extends PolicySpi {
+
+        @Override
+        protected boolean engineImplies(ProtectionDomain domain, Permission permission) {
+            return false;
+        }
+
+        @Override
+        public void engineRefresh() {
+            super.engineRefresh();
+        }
+
+        @Override
+        public PermissionCollection engineGetPermissions(CodeSource codesource) {
+            return super.engineGetPermissions(codesource);
+        }
+
+        @Override
+        public PermissionCollection engineGetPermissions(ProtectionDomain domain) {
+            return super.engineGetPermissions(domain);
+        }
+    }
+
+    private static final TestPolicySpi spi = new TestPolicySpi();
+
+    @Test
+    public void testEngineRefresh_doesntThrow() {
+        spi.engineRefresh();
+    }
+
+    @Test
+    public void testEngineGetPermissions() {
+        assertEquals(Policy.UNSUPPORTED_EMPTY_COLLECTION,
+                spi.engineGetPermissions((CodeSource) null));
+        assertEquals(Policy.UNSUPPORTED_EMPTY_COLLECTION,
+                spi.engineGetPermissions((ProtectionDomain) null));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/PolicyTest.java b/luni/src/test/java/libcore/java/security/PolicyTest.java
new file mode 100644
index 0000000..c25b3ce
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/PolicyTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.CodeSource;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.Provider;
+
+/**
+ * Test the stub implementation of {@link Policy}.
+ */
+@RunWith(JUnit4.class)
+public class PolicyTest {
+
+    private static class TestPolicy extends Policy {
+    }
+
+    private final Policy p = new TestPolicy();
+
+    @Test
+    public void testGetInstance_alwaysReturnNull() throws Exception {
+        assertNull(Policy.getInstance(null, null));
+        assertNull(Policy.getInstance(null, null, (String) null));
+        assertNull(Policy.getInstance(null, null, (Provider) null));
+
+        Policy.setPolicy(p);
+
+        // Still return null after setPolicy()
+        assertNull(Policy.getInstance(null, null));
+        assertNull(Policy.getInstance(null, null, (String) null));
+        assertNull(Policy.getInstance(null, null, (Provider) null));
+    }
+
+    @Test
+    public void testGetParameters() {
+        assertNull(p.getParameters());
+    }
+
+    @Test
+    public void testGetPermissions() {
+        assertNull(p.getPermissions((CodeSource) null));
+        assertNull(p.getPermissions((ProtectionDomain) null));
+    }
+
+    @Test
+    public void testGetProvider() {
+        assertNull(p.getProvider());
+    }
+
+    @Test
+    public void testGetType() {
+        assertNull(p.getType());
+    }
+
+    @Test
+    public void testImplies() {
+        assertTrue(p.implies(null, null));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/ProtectionDomainTest.java b/luni/src/test/java/libcore/java/security/ProtectionDomainTest.java
new file mode 100644
index 0000000..8360a52
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/ProtectionDomainTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.net.URL;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.KeyStore;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Principal;
+import java.security.ProtectionDomain;
+import java.security.Timestamp;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+
+import sun.security.provider.certpath.X509CertPath;
+
+@RunWith(JUnit4.class)
+public class ProtectionDomainTest {
+
+    private ProtectionDomain domain;
+
+    @Before
+    public void setUp() throws Exception {
+        String path = "file://invalid_path";
+        CodeSigner codeSigner = createCodeSigner();
+        CodeSource codeSource = new CodeSource(new URL(path), new CodeSigner[] { codeSigner });
+        domain = new ProtectionDomain(codeSource , new TestPermissionCollection(),
+                ProtectionDomainTest.class.getClassLoader(), new Principal[0]);
+    }
+
+    @Test
+    public void testGetClassLoader() {
+        // Return null even thought it's set in the constructor
+        assertNull(domain.getClassLoader());
+    }
+
+    @Test
+    public void testGetCodeSource() {
+        // Return null even thought it's set in the constructor
+        assertNull(domain.getCodeSource());
+    }
+
+    @Test
+    public void testGetPermissions() {
+        // Return null even thought it's set in the constructor
+        assertNull(domain.getPermissions());
+    }
+
+    @Test
+    public void testGetPrincipals() {
+        // Return null even thought it's set in the constructor
+        assertNull(domain.getPrincipals());
+    }
+
+    @Test
+    public void testImplies() {
+        assertTrue(domain.implies(null));
+    }
+
+    private static CodeSigner createCodeSigner() throws Exception {
+        KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
+        keyStore.load(null);
+        // Get a X509Certificate from the keyStore
+        X509Certificate cert = null;
+        for (Enumeration<String> aliases = keyStore.aliases(); aliases.hasMoreElements(); ) {
+            String alias = aliases.nextElement();
+            Certificate certificate = keyStore.getCertificate(alias);
+            assertTrue(certificate instanceof X509Certificate);
+            cert = (X509Certificate) certificate;
+            break;
+        }
+
+        assertNotNull(cert);
+        X509CertPath certPath = new X509CertPath(List.of(cert));
+        return new CodeSigner(certPath, new Timestamp(new Date(), certPath));
+    }
+
+    private static class TestPermissionCollection extends PermissionCollection {
+
+        @Override
+        public void add(Permission permission) {
+        }
+
+        @Override
+        public boolean implies(Permission permission) {
+            return true;
+        }
+
+        @Override
+        public Enumeration<Permission> elements() {
+            return null;
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/ProviderTest.java b/luni/src/test/java/libcore/java/security/ProviderTest.java
index 946f602..a0be85a 100644
--- a/luni/src/test/java/libcore/java/security/ProviderTest.java
+++ b/luni/src/test/java/libcore/java/security/ProviderTest.java
@@ -36,6 +36,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -155,9 +156,6 @@
                     }
                     remainingAlgorithms.removeAll(toRemove);
                 }
-                if (remainingAlgorithms != null && remainingAlgorithms.isEmpty()) {
-                    remainingExpected.remove(type);
-                }
 
                 // make sure class exists and can be initialized
                 try {
@@ -171,6 +169,14 @@
                     }
                 }
             }
+
+            // last chance: some algorithms might only be provided by their alias
+            remainingExpected.entrySet()
+                .forEach(entry ->
+                    entry.getValue()
+                        .removeIf(algorithm ->
+                            provider.getService(entry.getKey(), algorithm) != null)
+            );
         }
 
         // assert that we don't have any extra in the implementation
@@ -190,10 +196,12 @@
                 } catch (NoSuchAlgorithmException|NoSuchPaddingException e) {
                 }
             }
-            if (remainingExpected.get("Cipher").isEmpty()) {
-                remainingExpected.remove("Cipher");
-            }
         }
+
+        remainingExpected.entrySet()
+            .removeIf(entry ->
+                entry.getValue().isEmpty());
+
         // assert that we don't have any missing in the implementation
         assertEquals("Algorithms are present in StandardNames but not provided",
                 Collections.EMPTY_MAP, remainingExpected);
@@ -215,13 +223,13 @@
         assertProviderProperties(providers[2], "CertPathProvider",
             "sun.security.provider.CertPathProvider");
         assertProviderProperties(providers[3], "AndroidKeyStoreBCWorkaround",
-            "android.security.keystore.AndroidKeyStoreBCWorkaroundProvider");
+            "android.security.keystore2.AndroidKeyStoreBCWorkaroundProvider");
         assertProviderProperties(providers[4], "BC",
             "com.android.org.bouncycastle.jce.provider.BouncyCastleProvider");
         assertProviderProperties(providers[5], "HarmonyJSSE",
             "com.android.org.conscrypt.JSSEProvider");
         assertProviderProperties(providers[6], "AndroidKeyStore",
-            "android.security.keystore.AndroidKeyStoreProvider");
+            "android.security.keystore2.AndroidKeyStoreProvider");
     }
 
     private void assertProviderProperties(Provider p, String name, String className) {
@@ -382,7 +390,7 @@
                 bcClasses.add(service.getClassName());
             }
         }
-        assertTrue(bcClasses.size() > 0);  // Sanity check
+        assertTrue(bcClasses.size() > 0);
 
         // 3. Determine which IDs in BC point to that set of classes
         Set<String> shouldBeOverriddenBcIds = new HashSet<>();
@@ -402,7 +410,7 @@
                 shouldBeOverriddenBcIds.add(key);
             }
         }
-        assertTrue(shouldBeOverriddenBcIds.size() > 0);  // Sanity check
+        assertTrue(shouldBeOverriddenBcIds.size() > 0);
 
         // 4. Check each of those IDs to ensure that it's present in Conscrypt
         Set<String> nonOverriddenIds = new TreeSet<>();
@@ -1063,6 +1071,21 @@
         assertEquals("default", p.getOrDefault("thisIsNotInTheProvider", "default"));
     }
 
+    public void test_elements() {
+        Provider p = new MockProvider("MockProvider");
+        p.put("class1.algorithm1", "impl1");
+        Enumeration<Object> elements = p.elements();
+        boolean isImpl1Found = false;
+        while (elements.hasMoreElements()) {
+            if ("impl1".equals(elements.nextElement())) {
+                isImpl1Found = true;
+                break;
+            }
+        }
+
+        assertTrue("impl1 is not found.", isImpl1Found);
+    }
+
     private static class Pair<A, B> {
         private final A first;
         private final B second;
diff --git a/luni/src/test/java/libcore/java/security/SecurityPermissionTest.java b/luni/src/test/java/libcore/java/security/SecurityPermissionTest.java
new file mode 100644
index 0000000..20fa1c7
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/SecurityPermissionTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.SecurityPermission;
+
+@RunWith(JUnit4.class)
+public class SecurityPermissionTest {
+
+    @Test
+    public void testConstructor() {
+        SecurityPermission permission = new SecurityPermission("name", "action");
+
+        // The name and actions are discarded from the constructor.
+        assertEquals("", permission.getName());
+        assertEquals("", permission.getActions());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/SignatureTest.java b/luni/src/test/java/libcore/java/security/SignatureTest.java
index 9c014d2..0ba9cab 100644
--- a/luni/src/test/java/libcore/java/security/SignatureTest.java
+++ b/luni/src/test/java/libcore/java/security/SignatureTest.java
@@ -437,28 +437,6 @@
         }
     }
 
-    // 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 {
-        if (StandardNames.IS_RI) {
-            return;
-        }
-        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());
-    }
-
     /**
      * When an instance of a Signature is obtained, it's actually wrapped in an
      * implementation that makes sure the correct SPI is selected and then calls
diff --git a/luni/src/test/java/libcore/java/security/UnresolvedPermissionTest.java b/luni/src/test/java/libcore/java/security/UnresolvedPermissionTest.java
new file mode 100644
index 0000000..4e8b2de
--- /dev/null
+++ b/luni/src/test/java/libcore/java/security/UnresolvedPermissionTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.security.UnresolvedPermission;
+import java.security.cert.Certificate;
+
+@RunWith(JUnit4.class)
+public class UnresolvedPermissionTest {
+
+    private final UnresolvedPermission p = new UnresolvedPermission("type", "name", "action",
+            new Certificate[0]);
+
+    @Test
+    public void testGetName() {
+        assertEquals("", p.getName());
+    }
+
+    @Test
+    public void testGetActions() {
+        assertNull(p.getActions());
+    }
+
+    @Test
+    public void testUnresolvedType() {
+        assertNull(p.getUnresolvedType());
+    }
+
+    @Test
+    public void testGetUnresolvedCertssss() {
+        assertNull(p.getUnresolvedCerts());
+    }
+
+    @Test
+    public void testGetUnresolvedName() {
+        assertNull(p.getUnresolvedName());
+    }
+
+    @Test
+    public void testGetUnresolvedActions() {
+        assertNull(p.getUnresolvedActions());
+    }
+
+    @Test
+    public void testImplies() {
+        assertFalse(p.implies(null));
+        assertFalse(p.implies(p));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/security/cert/PKIXParametersTest.java b/luni/src/test/java/libcore/java/security/cert/PKIXParametersTest.java
index 1836aac..74868c1 100644
--- a/luni/src/test/java/libcore/java/security/cert/PKIXParametersTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/PKIXParametersTest.java
@@ -16,16 +16,38 @@
 
 package libcore.java.security.cert;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyStore;
+import java.security.cert.Certificate;
+import java.security.cert.PKIXCertPathChecker;
 import java.security.cert.PKIXParameters;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
 import libcore.java.security.TestKeyStore;
-import libcore.junit.junit3.TestCaseWithRules;
 import libcore.junit.util.EnableDeprecatedBouncyCastleAlgorithmsRule;
 import org.junit.Rule;
+import org.junit.Test;
 import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
-public class PKIXParametersTest extends TestCaseWithRules {
+@RunWith(JUnit4.class)
+public class PKIXParametersTest {
+    private  static final FakeCertPathChecker CHECK_ONE = new FakeCertPathChecker("One");
+    private  static final FakeCertPathChecker CHECK_TWO = new FakeCertPathChecker("Two");
+    private  static final FakeCertPathChecker CHECK_THREE = new FakeCertPathChecker("Three");
+    private static final List<PKIXCertPathChecker> CHECK_LIST
+        = List.of(CHECK_ONE, CHECK_TWO, CHECK_THREE);
 
     // Allow access to deprecated BC algorithms in this test, so we can ensure they
     // continue to work
@@ -33,7 +55,8 @@
     public TestRule enableDeprecatedBCAlgorithmsRule =
             EnableDeprecatedBouncyCastleAlgorithmsRule.getInstance();
 
-    public void testKeyStoreConstructor() throws Exception {
+    @Test
+    public void keyStoreConstructor() throws Exception {
         TestKeyStore server = TestKeyStore.getServer();
         KeyStore.PrivateKeyEntry pke = server.getPrivateKey("RSA", "RSA");
         char[] password = "password".toCharArray();
@@ -63,4 +86,139 @@
         } catch (InvalidAlgorithmParameterException expected) {
         }
     }
+
+    @Test
+    public void addCertPathChecker() throws Exception {
+        PKIXParameters parameters = newPkixParameters();
+        for (PKIXCertPathChecker pathChecker : CHECK_LIST) {
+            parameters.addCertPathChecker(pathChecker);
+        }
+
+        List<PKIXCertPathChecker> actualCheckers = parameters.getCertPathCheckers();
+        assertEquals(CHECK_LIST, actualCheckers);
+    }
+
+    @Test
+    public void addCertPathChecker_Null() throws Exception {
+        PKIXParameters parameters = newPkixParameters();
+
+        parameters.addCertPathChecker(null);
+        assertEquals(0, parameters.getCertPathCheckers().size());
+    }
+
+    @Test
+    public void setCertPathCheckers() throws Exception {
+        PKIXParameters parameters = newPkixParameters();
+        parameters.setCertPathCheckers(CHECK_LIST);
+
+        List<PKIXCertPathChecker> actualCheckers = parameters.getCertPathCheckers();
+        assertEquals(CHECK_LIST, actualCheckers);
+        assertNotSame(CHECK_LIST, actualCheckers);
+    }
+
+    @Test
+    public void setCertPathCheckers_NullEmpty() throws Exception {
+        PKIXParameters parameters = newPkixParameters();
+
+        parameters.setCertPathCheckers(null);
+        assertEquals(0, parameters.getCertPathCheckers().size());
+
+        parameters.setCertPathCheckers(new ArrayList<>());
+        assertEquals(0, parameters.getCertPathCheckers().size());
+    }
+
+    @Test
+    public void setCertPathCheckers_Replacement() throws Exception {
+        PKIXParameters parameters = newPkixParameters(CHECK_LIST);
+        parameters.setCertPathCheckers(null);
+        assertEquals(0, parameters.getCertPathCheckers().size());
+
+        parameters = newPkixParameters(CHECK_LIST);
+        parameters.setCertPathCheckers(new ArrayList<>());
+        assertEquals(0, parameters.getCertPathCheckers().size());
+
+        parameters = newPkixParameters(CHECK_LIST);
+        List<PKIXCertPathChecker> reversed = new ArrayList<>(CHECK_LIST);
+        Collections.reverse(reversed);
+        parameters.setCertPathCheckers(reversed);
+        assertEquals(reversed, parameters.getCertPathCheckers());
+    }
+
+    @Test
+    public void setCertPathCheckers_Error() throws Exception {
+        PKIXParameters parameters = newPkixParameters();
+        List badList = List.of("Wrong class");
+        try {
+            parameters.setCertPathCheckers(badList);
+            fail();
+        } catch (ClassCastException expected) {
+        }
+    }
+
+    @Test
+    public void toStringContainsPathCheckers() throws Exception {
+        PKIXParameters parameters = newPkixParameters(CHECK_LIST);
+
+        String regex = ".*" + CHECK_ONE + ".*" + CHECK_TWO + ".*" + CHECK_THREE + ".*";
+        Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
+        assertTrue(pattern.matcher(parameters.toString()).matches());
+    }
+
+    private PKIXParameters newPkixParameters() throws Exception {
+        PKIXParameters parameters = new PKIXParameters(TestKeyStore.getServer().keyStore);
+
+        List<PKIXCertPathChecker> pathCheckers = parameters.getCertPathCheckers();
+        assertEquals(0, pathCheckers.size());
+        return parameters;
+    }
+
+    private PKIXParameters newPkixParameters(List<PKIXCertPathChecker> pathCheckers)
+        throws Exception {
+
+        PKIXParameters parameters = newPkixParameters();
+        parameters.setCertPathCheckers(pathCheckers);
+        assertEquals(pathCheckers, parameters.getCertPathCheckers());
+        return parameters;
+    }
+
+    private static class FakeCertPathChecker extends PKIXCertPathChecker {
+        private final String tag;
+
+        FakeCertPathChecker(String tag) {
+            this.tag = tag;
+        }
+
+        @Override
+        public void init(boolean forward) {
+            // No-op
+        }
+
+        @Override
+        public boolean isForwardCheckingSupported() {
+            return false;
+        }
+
+        @Override
+        public Set<String> getSupportedExtensions() {
+            return new HashSet<>();
+        }
+
+        @Override
+        public void check(Certificate cert, Collection<String> unresolvedCritExts) {
+            // No-op
+        }
+
+        @Override
+        public String toString() {
+            return "FakeCertPathChecker: " + tag;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof FakeCertPathChecker) {
+                return tag.equals(((FakeCertPathChecker) other).tag);
+            }
+            return false;
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/security/cert/PKIXRevocationCheckerTest.java b/luni/src/test/java/libcore/java/security/cert/PKIXRevocationCheckerTest.java
index 0e420cb..eed881e 100644
--- a/luni/src/test/java/libcore/java/security/cert/PKIXRevocationCheckerTest.java
+++ b/luni/src/test/java/libcore/java/security/cert/PKIXRevocationCheckerTest.java
@@ -116,4 +116,10 @@
         assertEquals(1, checker.getOcspExtensions().size());
         assertEquals("TestExtension", checker.getOcspExtensions().get(0).getId());
     }
+
+    public void test_clone() {
+        PKIXRevocationChecker clone = checker.clone();
+        assertNotNull(clone);
+        assertNotSame(checker, clone);
+    }
 }
diff --git a/luni/src/test/java/libcore/java/sql/OldConnectionTest.java b/luni/src/test/java/libcore/java/sql/OldConnectionTest.java
index 0edee1c..3548ba3 100644
--- a/luni/src/test/java/libcore/java/sql/OldConnectionTest.java
+++ b/luni/src/test/java/libcore/java/sql/OldConnectionTest.java
@@ -1614,7 +1614,7 @@
 
     // TODO Savepoint is not supported
     public void testRollback_Savepoint() throws SQLException {
-        Savepoint sp = new DummySavePoint();
+        Savepoint sp = new FakeSavePoint();
         conn.setAutoCommit(false);
 
         try {
@@ -1735,7 +1735,7 @@
 
     // TODO Savepoint is not supported
     public void testReleaseSavepoint_Savepoint() throws SQLException {
-        Savepoint sp = new DummySavePoint();
+        Savepoint sp = new FakeSavePoint();
         conn.setAutoCommit(false);
 
         try {
@@ -2181,7 +2181,7 @@
         st.execute("select * from zoo");
     }
 
-    private static class DummySavePoint implements Savepoint{
+    private static class FakeSavePoint implements Savepoint{
         public int getSavepointId()  {
             return 0;
         }
diff --git a/luni/src/test/java/libcore/java/text/DecimalFormatTest.java b/luni/src/test/java/libcore/java/text/DecimalFormatTest.java
index e2be925..4e5e994 100644
--- a/luni/src/test/java/libcore/java/text/DecimalFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/DecimalFormatTest.java
@@ -120,6 +120,112 @@
                 en_IN, "-9,87,65,43,21,09,87,65,43,21,09,87,65,43,210");
     }
 
+    // Test for http://b/168304209
+    public void testFieldPosition() {
+        DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
+        DecimalFormat currencyDf = (DecimalFormat) NumberFormat.getCurrencyInstance(Locale.US);
+        DecimalFormat percentDf = (DecimalFormat) NumberFormat.getPercentInstance(Locale.US);
+        DecimalFormat milledf = (DecimalFormat) NumberFormat.getInstance(Locale.US);
+        milledf.applyPattern("#,##0\u2030;-#,##0\u2030");
+        DecimalFormat scientificDf = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
+        scientificDf.applyPattern("0.###E0");
+
+        // Reference behaviors of different field positions when formatting a simple integer.
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.INTEGER, "123", 0, 3);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.FRACTION, "123", 3, 3);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.CURRENCY, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.DECIMAL_SEPARATOR, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.EXPONENT, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.EXPONENT_SIGN, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.EXPONENT_SYMBOL, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.GROUPING_SEPARATOR, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.PERCENT, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.PERMILLE, "123", 0, 0);
+        assertFieldPosition4Types(df, 123, NumberFormat.Field.SIGN, "123", 0, 0);
+
+        assertFieldPosition4Types(currencyDf, 123, NumberFormat.Field.CURRENCY, "$123.00", 0, 1);
+        assertFieldPosition(df, 123.4, NumberFormat.Field.DECIMAL_SEPARATOR, "123.4", 3, 4);
+        assertFieldPosition4Types(scientificDf, 123, NumberFormat.Field.EXPONENT, "1.23E2", 5, 6);
+        assertFieldPosition(scientificDf, 0.123, NumberFormat.Field.EXPONENT_SIGN, "1.23E-1", 5, 6);
+        assertFieldPosition4Types(scientificDf, 123, NumberFormat.Field.EXPONENT_SYMBOL, "1.23E2",
+                4, 5);
+        assertFieldPosition4Types(df, 1234, NumberFormat.Field.GROUPING_SEPARATOR, "1,234", 1, 2);
+        assertFieldPosition4Types(percentDf, 12, NumberFormat.Field.PERCENT, "1,200%", 5, 6);
+        assertFieldPosition4Types(milledf, 12, NumberFormat.Field.PERMILLE, "12,000\u2030", 6, 7);
+        assertFieldPosition4Types(df, -123, NumberFormat.Field.SIGN, "-123", 0, 1);
+
+        BigInteger bigInteger = new BigInteger("999999999999999999999999"); // 24 of '9';
+        // Assert this large number is larger than the max possible long value.
+        assertEquals(1, bigInteger.compareTo(BigInteger.valueOf(Long.MAX_VALUE)));
+        String expectedStr = "999,999,999,999,999,999,999,999";
+        assertFieldPosition(df, bigInteger, NumberFormat.Field.INTEGER, expectedStr, 0,
+                expectedStr.length());
+        expectedStr = "$" + expectedStr + ".00";
+        assertFieldPosition(currencyDf, bigInteger, NumberFormat.Field.INTEGER, expectedStr, 1,
+                expectedStr.length() - 3);
+        assertFieldPosition(currencyDf, bigInteger, NumberFormat.Field.CURRENCY, expectedStr, 0, 1);
+
+        BigDecimal bigDecimal = BigDecimal.valueOf(123.45);
+        assertFieldPosition(df, bigDecimal, NumberFormat.Field.INTEGER, "123.45", 0, 3);
+        assertFieldPosition(currencyDf, bigDecimal, NumberFormat.Field.CURRENCY, "$123.45", 0, 1);
+    }
+
+    /**
+     * Run the test with {@param num} in 4 different types, i.e. long, double, BigInteger
+     * and BigDecimal to increase the test coverage, because the 4 types have 4 different code paths
+     * internally.
+     */
+    private void assertFieldPosition4Types(DecimalFormat df, long num, NumberFormat.Field field,
+            String expectedStr, int expectedBeginIndex, int expectedEndIndex) {
+        assertFieldPosition(df, num, field, expectedStr, expectedBeginIndex, expectedEndIndex);
+        assertFieldPosition(df, (double) num, field, expectedStr, expectedBeginIndex,
+                expectedEndIndex);
+        assertFieldPosition(df, BigInteger.valueOf(num), field, expectedStr, expectedBeginIndex,
+                expectedEndIndex);
+        assertFieldPosition(df, BigDecimal.valueOf(num), field, expectedStr, expectedBeginIndex,
+                expectedEndIndex);
+    }
+
+    private void assertFieldPosition(DecimalFormat df, long num, NumberFormat.Field field,
+            String expectedStr, int expectedBeginIndex, int expectedEndIndex) {
+        FieldPosition fp = new FieldPosition(field);
+        StringBuffer stringBuffer = new StringBuffer();
+        df.format(num, stringBuffer, fp);
+        assertEquals(expectedStr, stringBuffer.toString());
+        assertEquals(expectedBeginIndex, fp.getBeginIndex());
+        assertEquals(expectedEndIndex, fp.getEndIndex());
+    }
+
+    private void assertFieldPosition(DecimalFormat df, double num, NumberFormat.Field field,
+            String expectedStr, int expectedBeginIndex, int expectedEndIndex) {
+        FieldPosition fp = new FieldPosition(field);
+        StringBuffer stringBuffer = new StringBuffer();
+        df.format(num, stringBuffer, fp);
+        assertEquals(expectedStr, stringBuffer.toString());
+        assertEquals(expectedBeginIndex, fp.getBeginIndex());
+        assertEquals(expectedEndIndex, fp.getEndIndex());
+    }
+
+    private void assertFieldPosition(DecimalFormat df, BigInteger num, NumberFormat.Field field,
+            String expectedStr, int expectedBeginIndex, int expectedEndIndex) {
+        FieldPosition fp = new FieldPosition(field);
+        StringBuffer stringBuffer = new StringBuffer();
+        df.format(num, stringBuffer, fp);
+        assertEquals(expectedStr, stringBuffer.toString());
+        assertEquals(expectedBeginIndex, fp.getBeginIndex());
+        assertEquals(expectedEndIndex, fp.getEndIndex());
+    }
+
+    private void assertFieldPosition(DecimalFormat df, BigDecimal num, NumberFormat.Field field,
+            String expectedStr, int expectedBeginIndex, int expectedEndIndex) {
+        FieldPosition fp = new FieldPosition(field);
+        StringBuffer stringBuffer = new StringBuffer();
+        df.format(num, stringBuffer, fp);
+        assertEquals(expectedStr, stringBuffer.toString());
+        assertEquals(expectedBeginIndex, fp.getBeginIndex());
+        assertEquals(expectedEndIndex, fp.getEndIndex());
+    }
+
     public void testBigDecimalICUConsistency() {
         DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
         df.setMaximumFractionDigits(2);
@@ -302,34 +408,6 @@
         assertEquals(expected, numberFormat.format(2.01));
     }
 
-    /**
-     * Test no extra spacing between currency symbol and the numeric amount
-     */
-    public void testCurrencySymbolSpacing() {
-        Currency currency = Currency.getInstance(Locale.US);
-        for (Locale locale : Locale.getAvailableLocales()) {
-            DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
-            String formattedZero = new DecimalFormat("0", dfs).format(0);
-
-            assertCurrencyFormat("USD" + formattedZero, "\u00a4\u00a40", dfs, currency, locale);
-            assertCurrencyFormat(formattedZero + "USD", "0\u00a4\u00a4", dfs, currency, locale);
-            assertCurrencyFormat(currency.getSymbol(locale) + formattedZero, "\u00a40", dfs,
-                    currency, locale);
-            assertCurrencyFormat(formattedZero + currency.getSymbol(locale), "0\u00a4", dfs,
-                    currency, locale);
-        }
-    }
-
-    private static void assertCurrencyFormat(String expected, String pattern,
-            DecimalFormatSymbols dfs,
-            Currency currency, Locale locale) {
-        DecimalFormat df = new DecimalFormat(pattern, dfs);
-        df.setCurrency(currency);
-        df.setMaximumFractionDigits(0);
-        assertEquals("Not formatted as expected with pattern " + pattern + " in locale " + locale,
-                expected, df.format(0));
-    }
-
     // http://b/27855939
     public void testBug27855939() {
         DecimalFormat df = new DecimalFormat("00");
diff --git a/luni/src/test/java/libcore/java/text/OldDecimalFormatTestICU.java b/luni/src/test/java/libcore/java/text/OldDecimalFormatTestICU.java
index eb3f4ca..75344af 100644
--- a/luni/src/test/java/libcore/java/text/OldDecimalFormatTestICU.java
+++ b/luni/src/test/java/libcore/java/text/OldDecimalFormatTestICU.java
@@ -35,7 +35,7 @@
         format = (DecimalFormat) NumberFormat.getNumberInstance();
     }
 
-    // AndroidOnly: special feature of icu4c
+    // Android-note: special feature of icu4c.
     public void test_sigDigitPatterns() throws Exception {
         DecimalFormat format = (DecimalFormat) NumberFormat
         .getInstance(Locale.US);
@@ -62,7 +62,7 @@
         }
     }
 
-    // AndroidOnly: special feature of icu4c
+    // Android-note: special feature of icu4c.
     public void test_paddingPattern() throws Exception {
         format.applyPattern("*x##,##,#,##0.0#");
         assertEquals("xxxxxxxxx123.0", format.format(123));
@@ -95,7 +95,7 @@
         }
     }
 
-    // AndroidOnly: special feature of icu4c
+    // Android-note: special feature of icu4c.
     public void test_positiveExponentSign() throws Exception {
         format.applyPattern("0.###E+0");
         assertEquals("1E+2", format.format(100));
@@ -124,7 +124,7 @@
         }
     }
 
-    // AndroidOnly: special feature of icu4c
+    // Android-note: special feature of icu4c.
     public void test_secondaryGroupingSize() throws Exception {
         format.applyPattern("#,##,###,####");
         assertEquals("123,456,7890", format.format(1234567890));
diff --git a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
index a4ebad3..245cd3d 100644
--- a/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
+++ b/luni/src/test/java/libcore/java/text/SimpleDateFormatTest.java
@@ -388,21 +388,19 @@
         assertEquals(20, calendar.get(Calendar.HOUR_OF_DAY)); // 9:00 GMT+11:00
         assertEquals(0, calendar.get(Calendar.MINUTE));
     }
-
-    public void testLocales() throws Exception {
-        // Just run through them all. Handy as a poor man's benchmark, and a sanity check.
-        for (Locale l : Locale.getAvailableLocales()) {
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzzz", l);
-            sdf.format(new Date(0));
-        }
-    }
-
     // http://code.google.com/p/android/issues/detail?id=14963
     public void testParseTimezoneOnly() throws Exception {
         new SimpleDateFormat("z", Locale.FRANCE).parse("UTC");
         new SimpleDateFormat("z", Locale.US).parse("UTC");
     }
 
+    public void testParseMetazoneFallbacksToLocale() throws Exception {
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US);
+        Date date = format.parse("2021-02-08T14:14 Pacific Standard Time"); // 22:14 GMT-8:00
+
+        assertEquals(1612822440000L, date.getTime());
+    }
+
     // 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"));
diff --git a/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java
index 09a258a..ba69230 100644
--- a/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java
+++ b/luni/src/test/java/libcore/java/time/OffsetDateTimeTest.java
@@ -53,4 +53,40 @@
         assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 7, ZoneOffset.UTC),
                 ODT.plus(1, ChronoUnit.NANOS));
     }
+
+    @Test
+    public void test_minus_utc_offset() {
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 2, 4, 5, 6, ZoneOffset.UTC),
+                ODT.minus(1, ChronoUnit.HOURS));
+        assertEquals(OffsetDateTime.of(2000, 1, 1, 4, 4, 5, 6, ZoneOffset.UTC),
+                ODT.minus(23, ChronoUnit.HOURS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 3, 5, 6, ZoneOffset.UTC),
+                ODT.minus(1, ChronoUnit.MINUTES));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 3, 5, 6, ZoneOffset.UTC),
+                ODT.minus(60, ChronoUnit.SECONDS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 4, 999_000_006, ZoneOffset.UTC),
+                ODT.minus(1, ChronoUnit.MILLIS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 5, ZoneOffset.UTC),
+                ODT.minus(1, ChronoUnit.NANOS));
+    }
+
+    @Test
+    public void test_minus_non_utc_offset() {
+        ZoneOffset offset = ZoneOffset.ofHours(4);
+        OffsetDateTime odt =
+                OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 6, offset);
+
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 2, 4, 5, 6, offset),
+                odt.minus(1, ChronoUnit.HOURS));
+        assertEquals(OffsetDateTime.of(2000, 1, 1, 4, 4, 5, 6, offset),
+                odt.minus(23, ChronoUnit.HOURS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 3, 5, 6, offset),
+                odt.minus(1, ChronoUnit.MINUTES));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 3, 5, 6, offset),
+                odt.minus(60, ChronoUnit.SECONDS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 4, 999_000_006, offset),
+                odt.minus(1, ChronoUnit.MILLIS));
+        assertEquals(OffsetDateTime.of(2000, 1, 2, 3, 4, 5, 5, offset),
+                odt.minus(1, ChronoUnit.NANOS));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java
index 09422dd..afeb570 100644
--- a/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java
+++ b/luni/src/test/java/libcore/java/time/ZonedDateTimeTest.java
@@ -92,8 +92,8 @@
 
     /**
      * Assert that calling {@link ZonedDateTime#ofInstant(LocalDateTime, ZoneOffset, ZoneId)} with
-     * the first three parameters produces a sane result with the localDateTime and offset equal to
-     * the last two.
+     * the first three parameters produces a meaningful result with the localDateTime and offset
+     * equal to the last two.
      */
     private static void checkOfInstant(LocalDateTime localDateTime, ZoneOffset offset,
             ZoneId zone, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) {
@@ -162,8 +162,8 @@
 
     /**
      * Assert that calling {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)} with
-     * the first three parameters produces a sane result with the localDateTime, and offset equal
-     * to the last two.
+     * the first three parameters produces a meaningful result with the localDateTime, and offset
+     * equal to the last two.
      */
     private static void checkOfLocal(LocalDateTime localDateTime, ZoneId zone,
             ZoneOffset preferredOffset, LocalDateTime expectedDateTime, ZoneOffset expectedOffset) {
diff --git a/luni/src/test/java/libcore/java/time/chrono/ChronoPeriodTest.java b/luni/src/test/java/libcore/java/time/chrono/ChronoPeriodTest.java
new file mode 100644
index 0000000..b0e3bb6
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/ChronoPeriodTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.time.chrono;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.dx.Local;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.chrono.ChronoPeriod;
+import java.time.chrono.HijrahChronology;
+import java.time.chrono.HijrahDate;
+
+@RunWith(JUnit4.class)
+public class ChronoPeriodTest {
+
+    @Test
+    public void between_withNull_throwsNpe() {
+        try {
+            ChronoPeriod.between(null, null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+
+        try {
+            ChronoPeriod.between(null, LocalDate.now());
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+
+        try {
+            ChronoPeriod.between(LocalDate.now(), null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void between_nonNull() {
+        LocalDate now = LocalDate.of(2021, 5, 26);
+        LocalDate tomorrow = LocalDate.of(2021, 5, 27);
+
+        assertEquals(Period.ofDays(1), ChronoPeriod.between(now, tomorrow));
+        assertEquals(HijrahChronology.INSTANCE.period(0, 0, 2),
+                ChronoPeriod.between(HijrahDate.of(1442, 9, 10), HijrahDate.of(1442, 9, 12)));
+    }
+
+    @Test
+    public void between_differentInterfaceImplementations() {
+        LocalDate isoNow = LocalDate.of(2021, 5, 26);
+        HijrahDate hijrahTomorrow = HijrahDate.of(1442, 10, 15);
+
+        assertEquals(Period.ofDays(1), ChronoPeriod.between(isoNow, hijrahTomorrow));
+        assertEquals(HijrahChronology.INSTANCE.period(0, 0, -1),
+                ChronoPeriod.between(hijrahTomorrow, isoNow));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/ChronoZonedDateTimeTest.java b/luni/src/test/java/libcore/java/time/chrono/ChronoZonedDateTimeTest.java
new file mode 100644
index 0000000..a319980
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/ChronoZonedDateTimeTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.time.chrono;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.chrono.ChronoLocalDateTime;
+import java.time.chrono.ChronoZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalUnit;
+
+@RunWith(JUnit4.class)
+public class ChronoZonedDateTimeTest {
+
+    @Test
+    public void isSupported_returnsFalse_forForever() {
+        ChronoZonedDateTime chronoZonedDateTime = new TestChronoZonedDateTime();
+        assertFalse(chronoZonedDateTime.isSupported(ChronoUnit.FOREVER));
+    }
+
+    @Test
+    public void isSupported_returnsFalse_forNull() {
+        ChronoZonedDateTime chronoZonedDateTime = new TestChronoZonedDateTime();
+        assertFalse(chronoZonedDateTime.isSupported((TemporalUnit) null));
+    }
+
+    @Test
+    public void isSupported_defersToArgument() {
+        ChronoZonedDateTime chronoZonedDateTime = new TestChronoZonedDateTime();
+        TemporalUnit temporalUnit = mock(TemporalUnit.class);
+
+        chronoZonedDateTime.isSupported(temporalUnit);
+
+        verify(temporalUnit).isSupportedBy(chronoZonedDateTime);
+    }
+
+    /* Used to test default methods. */
+    private static class TestChronoZonedDateTime implements ChronoZonedDateTime {
+
+        @Override
+        public ChronoLocalDateTime toLocalDateTime() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ZoneOffset getOffset() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ZoneId getZone() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoZonedDateTime withEarlierOffsetAtOverlap() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoZonedDateTime withLaterOffsetAtOverlap() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoZonedDateTime withZoneSameLocal(ZoneId zone) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoZonedDateTime withZoneSameInstant(ZoneId zone) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isSupported(TemporalField field) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoZonedDateTime with(TemporalField field, long newValue) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ChronoZonedDateTime plus(long amountToAdd, TemporalUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long until(Temporal endExclusive, TemporalUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int compareTo(Object o) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java
index 37fdd7c..430e350 100644
--- a/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java
+++ b/luni/src/test/java/libcore/java/time/chrono/ChronologyTest.java
@@ -40,8 +40,8 @@
     @Test
     public void test_compareTo() {
         Set<Chronology> chronologies = new LinkedHashSet<>(Chronology.getAvailableChronologies());
-        chronologies.add(new DummyChronology("aaa", "z aaa"));
-        chronologies.add(new DummyChronology("zzz", "a zzz"));
+        chronologies.add(new FakeChronology("aaa", "z aaa"));
+        chronologies.add(new FakeChronology("zzz", "a zzz"));
 
         // Check for comparison of each chronology with each other (including itself).
         for (Chronology c1 : chronologies) {
@@ -68,14 +68,14 @@
         IsoChronology.INSTANCE.compareTo(null);
     }
 
-    /** Dummy chronology that supports only returning an id and a type. */
-    private static class DummyChronology extends AbstractChronology {
+    /** Fake chronology that supports only returning an id and a type. */
+    private static class FakeChronology extends AbstractChronology {
 
         private final String id;
 
         private final String type;
 
-        public DummyChronology(String id, String type) {
+        public FakeChronology(String id, String type) {
             this.id = id;
             this.type = type;
         }
diff --git a/luni/src/test/java/libcore/java/time/chrono/EraTest.java b/luni/src/test/java/libcore/java/time/chrono/EraTest.java
new file mode 100644
index 0000000..92d284e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/EraTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.time.chrono;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import static java.time.temporal.ChronoField.ERA;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.chrono.Era;
+import java.time.temporal.ChronoField;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalField;
+import java.time.temporal.TemporalUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+import java.time.temporal.ValueRange;
+import java.util.EnumSet;
+
+@RunWith(JUnit4.class)
+public class EraTest {
+
+    @Test
+    public void getLong_returnsValue_forEra() {
+        assertEquals(10, TestEra.INSTANCE.getLong(ERA));
+    }
+
+    @Test
+    public void getLong_throws_forNonERA_chronoFields() {
+        EnumSet<ChronoField> unsupportedFields = EnumSet.complementOf(EnumSet.of(ERA));
+
+        for (ChronoField unsupportedField : unsupportedFields) {
+            try {
+                TestEra.INSTANCE.getLong(unsupportedField);
+                fail("getLong(" + unsupportedField + ") should throw exception");
+            } catch (UnsupportedTemporalTypeException ignored) {
+                // expected
+            }
+        }
+    }
+
+    @Test
+    public void getLong_defersToArgument_forNonChronoFieldInstances() {
+        TestTemporalField temporalField = new TestTemporalField();
+
+        TestEra.INSTANCE.getLong(temporalField);
+
+        temporalField.assertGetFromWasCalledWith(TestEra.INSTANCE);
+    }
+
+    private enum TestEra implements Era {
+        INSTANCE;
+
+        @Override
+        public int getValue() {
+            return 10;
+        }
+    }
+
+    private static class TestTemporalField implements TemporalField {
+
+        TemporalAccessor getFromArgument = null;
+
+        @Override
+        public TemporalUnit getBaseUnit() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public TemporalUnit getRangeUnit() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ValueRange range() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isDateBased() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isTimeBased() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean isSupportedBy(TemporalAccessor temporal) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getFrom(TemporalAccessor temporal) {
+            if (getFromArgument != null) {
+                throw new IllegalStateException("getFrom was called more than once");
+            }
+            getFromArgument = temporal;
+            return 0;
+        }
+
+        @Override
+        public <R extends Temporal> R adjustInto(R temporal, long newValue) {
+            throw new UnsupportedOperationException();
+        }
+
+        public void assertGetFromWasCalledWith(TemporalAccessor temporal) {
+            assertSame(temporal, getFromArgument);
+        }
+    }
+
+
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java
index 3a33e5b..2122c01 100644
--- a/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java
+++ b/luni/src/test/java/libcore/java/time/chrono/HijrahChronologyTest.java
@@ -15,15 +15,16 @@
  */
 package libcore.java.time.chrono;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
 import org.junit.Test;
+
 import java.time.chrono.HijrahChronology;
 import java.time.chrono.HijrahDate;
 import java.time.chrono.HijrahEra;
 import java.time.temporal.ChronoField;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-
 /**
  * Additional tests for {@link HijrahDate}.
  *
@@ -64,4 +65,12 @@
     public void test_HijrahDate_withVariant_null() {
         HijrahDate.now().withVariant(null);
     }
+
+    @Test
+    public void dateEpochDay() {
+        // 18766th Epoch Day is 19 May 2021
+        assertEquals(HijrahDate.of(1442, 10, 7), HijrahChronology.INSTANCE.dateEpochDay(18766));
+        assertEquals(HijrahDate.of(1389, 10, 22), HijrahChronology.INSTANCE.dateEpochDay(0));
+    }
+
 }
diff --git a/luni/src/test/java/libcore/java/time/chrono/HijrahDateTest.java b/luni/src/test/java/libcore/java/time/chrono/HijrahDateTest.java
new file mode 100644
index 0000000..23f1ad0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/HijrahDateTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.time.chrono;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.apache.harmony.tests.support.P;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.Year;
+import java.time.chrono.HijrahChronology;
+import java.time.chrono.HijrahDate;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+@RunWith(JUnit4.class)
+public class HijrahDateTest {
+
+    @Test
+    public void minus_null_throws_NPE() {
+        try {
+            HijrahDate.now().minus(/* amount= */ null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_Duration_throws_DateTimeException() {
+        try {
+            HijrahDate.now().minus(Duration.ofDays(1));
+          fail();
+        } catch (DateTimeException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_Period_throws_DateTimeException() {
+        try {
+            HijrahDate.now().minus(Period.ofDays(1));
+            fail();
+        } catch (DateTimeException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_hijrah_period() {
+        // 26 May 2021
+        HijrahDate now = HijrahDate.of(1442, 10, 14);
+
+        assertEquals(HijrahDate.of(1442, 10, 13),
+                now.minus(HijrahChronology.INSTANCE.period(0, 0, 1)));
+        assertEquals(HijrahDate.of(1441, 10, 14),
+                now.minus(HijrahChronology.INSTANCE.period(1, 0, 0)));
+        assertEquals(HijrahDate.of(1443, 11, 15),
+                now.minus(HijrahChronology.INSTANCE.period(-1, -1, -1)));
+        assertEquals(HijrahDate.of(1442, 9, 30),
+                now.minus(HijrahChronology.INSTANCE.period(0, 0, 14)));
+        assertEquals(HijrahDate.of(1442, 11, 1),
+                now.minus(HijrahChronology.INSTANCE.period(0, 0, -16)));
+    }
+
+    @Test
+    public void until_isoDates() {
+        // 26 May 2021
+        HijrahDate hijrahNow = HijrahDate.of(1442, 10, 14);
+        LocalDate isoNow = LocalDate.of(2021, 5, 26);
+
+        assertEquals(0, hijrahNow.until(isoNow, ChronoUnit.DAYS));
+        assertEquals(0, hijrahNow.until(isoNow.atTime(10, 10), ChronoUnit.DAYS));
+    }
+
+    @Test
+    public void until_hijrahDates() {
+        // 26 May 2021
+        HijrahDate hijrahNow = HijrahDate.of(1442, 10, 14);
+        HijrahDate hijrahTomorrow = HijrahDate.of(1442, 10, 15);
+
+        assertEquals(-1, hijrahTomorrow.until(hijrahNow, ChronoUnit.DAYS));
+        assertEquals(1, hijrahNow.until(HijrahDate.of(1442, 11, 14), ChronoUnit.MONTHS));
+    }
+
+    @Test
+    public void until_isoDates_unsupportedTypes() {
+        // 26 May 2021
+        HijrahDate hijrahNow = HijrahDate.of(1442, 10, 14);
+        LocalDate isoNow = LocalDate.of(2021, 5, 26);
+
+        try {
+            hijrahNow.until(isoNow, ChronoUnit.HOURS);
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+
+        try {
+            hijrahNow.until(Year.of(2021), ChronoUnit.YEARS);
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+
+        try {
+            hijrahNow.until(Instant.ofEpochSecond(1622113481L), ChronoUnit.DAYS);
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java
index 5e920b3..148c8cd 100644
--- a/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java
+++ b/luni/src/test/java/libcore/java/time/chrono/JapaneseChronologyTest.java
@@ -18,6 +18,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
+import static java.time.chrono.JapaneseEra.REIWA;
+import static java.time.chrono.JapaneseEra.SHOWA;
+
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalTime;
@@ -74,7 +77,7 @@
         // first supported year in JapaneseChronology is Meiji 6.
         assertEquals(JapaneseEra.MEIJI, JapaneseDate.from(LocalDate.of(1873, 1, 1)).getEra());
         assertEquals(JapaneseEra.TAISHO, JapaneseDate.from(LocalDate.of(1913, 1, 1)).getEra());
-        assertEquals(JapaneseEra.SHOWA, JapaneseDate.from(LocalDate.of(1927, 1, 1)).getEra());
+        assertEquals(SHOWA, JapaneseDate.from(LocalDate.of(1927, 1, 1)).getEra());
         assertEquals(JapaneseEra.HEISEI, JapaneseDate.from(LocalDate.of(1990, 1, 1)).getEra());
     }
 
@@ -144,4 +147,15 @@
         assertEquals("Reiwa", reiwaDate.getEra().toString());
     }
 
+    @Test
+    public void dateEpochDay() {
+        JapaneseDate epoch = JapaneseChronology.INSTANCE.dateEpochDay(0);
+        JapaneseDate today = JapaneseChronology.INSTANCE.dateEpochDay(18768);
+        JapaneseDate dayBeforeEpoch = JapaneseChronology.INSTANCE.dateEpochDay(-1);
+
+        assertEquals(JapaneseDate.of(SHOWA, 45, 1, 1), epoch);
+        assertEquals(JapaneseDate.of(REIWA, 3, 5, 21), today);
+        assertEquals(JapaneseDate.of(SHOWA, 44, 12, 31), dayBeforeEpoch);
+    }
+
 }
diff --git a/luni/src/test/java/libcore/java/time/chrono/JapaneseDateTest.java b/luni/src/test/java/libcore/java/time/chrono/JapaneseDateTest.java
new file mode 100644
index 0000000..7789e06
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/JapaneseDateTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.time.chrono;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import static java.time.chrono.JapaneseEra.HEISEI;
+import static java.time.chrono.JapaneseEra.REIWA;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Period;
+import java.time.chrono.JapaneseChronology;
+import java.time.chrono.JapaneseDate;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+@RunWith(JUnit4.class)
+public class JapaneseDateTest {
+
+    @Test
+    public void minus_null_throwsNpe() {
+        try {
+            JapaneseDate.now().minus(null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_days() {
+        JapaneseDate date = JapaneseDate.of(REIWA, 3, 5, 21);
+
+        assertEquals(JapaneseDate.of(REIWA, 3, 5, 20), date.minus(1, ChronoUnit.DAYS));
+    }
+
+    @Test
+    public void minus_negative_days() {
+        JapaneseDate date = JapaneseDate.of(REIWA, 3, 5, 21);
+
+        assertEquals(JapaneseDate.of(REIWA, 3, 5, 22), date.minus(-1, ChronoUnit.DAYS));
+    }
+
+    @Test
+    public void minus_days_crossEras() {
+        JapaneseDate date = JapaneseDate.of(REIWA, 1, 5, 1);
+
+        assertEquals(JapaneseDate.of(HEISEI, 31, 4, 30), date.minus(1, ChronoUnit.DAYS));
+    }
+
+    @Test
+    public void minus_less_than_day_throwsUnsupportedTemporalTypeException() {
+        try {
+            JapaneseDate.now().minus(1, ChronoUnit.HOURS);
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+
+        try {
+            JapaneseDate.now().minus(1, ChronoUnit.MINUTES);
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+
+        try {
+            JapaneseDate.now().minus(1, ChronoUnit.SECONDS);
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_Duration_throws_UnsupportedTemporalTypeException() {
+        try {
+            JapaneseDate.now().minus(Duration.ofDays(1));
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_Period_throws_DateTimeException() {
+        try {
+            JapaneseDate.now().minus(Period.ofDays(1));
+            fail();
+        } catch (DateTimeException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_japanesePeriod() {
+        JapaneseDate date = JapaneseDate.of(REIWA, 3, 5, 21);
+
+        assertEquals(JapaneseDate.of(REIWA, 2, 4, 20),
+                date.minus(JapaneseChronology.INSTANCE.period(1, 1, 1)));
+    }
+
+    @Test
+    public void minus_japanesePeriod_withNegative() {
+        JapaneseDate date = JapaneseDate.of(REIWA, 2, 5, 21);
+
+        assertEquals(JapaneseDate.of(REIWA, 3, 8, 26),
+                date.minus(JapaneseChronology.INSTANCE.period(-1, -3, -5)));
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java
index 4d9e7bc..044e6f9 100644
--- a/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java
+++ b/luni/src/test/java/libcore/java/time/chrono/MinguoChronologyTest.java
@@ -17,6 +17,8 @@
 
 import org.junit.Test;
 import java.time.LocalDate;
+import java.time.chrono.HijrahChronology;
+import java.time.chrono.HijrahDate;
 import java.time.chrono.MinguoChronology;
 import java.time.chrono.MinguoDate;
 import java.time.chrono.MinguoEra;
@@ -119,4 +121,11 @@
         assertEquals(31 + 5, date.getLong(ChronoField.DAY_OF_YEAR));
         assertEquals(date.toEpochDay(), date.getLong(ChronoField.EPOCH_DAY));
     }
+
+    @Test
+    public void getEpochDay() {
+        // 18766th Epoch Day is 19 May 2021
+        assertEquals(MinguoDate.of(110, 5, 19), MinguoChronology.INSTANCE.dateEpochDay(18766));
+        assertEquals(MinguoDate.of(59, 1, 1), MinguoChronology.INSTANCE.dateEpochDay(0));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/time/chrono/MinguoDateTest.java b/luni/src/test/java/libcore/java/time/chrono/MinguoDateTest.java
new file mode 100644
index 0000000..5fbc1f7
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/MinguoDateTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.time.chrono;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Period;
+import java.time.chrono.MinguoChronology;
+import java.time.chrono.MinguoDate;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.UnsupportedTemporalTypeException;
+
+@RunWith(JUnit4.class)
+public class MinguoDateTest {
+
+    @Test
+    public void minus_null_throwsNpe() {
+        try {
+            MinguoDate.now().minus(null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_days() {
+        MinguoDate date = MinguoDate.of(110, 1, 1);
+
+        assertEquals(MinguoDate.of(109, 12, 31), date.minus(1, ChronoUnit.DAYS));
+    }
+
+    @Test
+    public void minus_Duration_throws_UnsupportedTemporalTypeException() {
+        try {
+            MinguoDate.now().minus(Duration.ofDays(1));
+            fail();
+        } catch (UnsupportedTemporalTypeException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_Period_throws_DateTimeException() {
+       try {
+           MinguoDate.now().minus(Period.ofDays(1));
+           fail();
+       } catch (DateTimeException ignored) {
+           // expected
+       }
+    }
+
+    @Test
+    public void minus_minguoPeriod() {
+        MinguoDate date = MinguoDate.of(110, 1, 1);
+
+        assertEquals(MinguoDate.of(108, 11, 30),
+                date.minus(MinguoChronology.INSTANCE.period(1, 1, 1)));
+    }
+
+    @Test
+    public void minus_minguoPeriod_withNegative() {
+        MinguoDate date = MinguoDate.of(110, 2, 28);
+
+        assertEquals(MinguoDate.of(109, 3, 29),
+                date.minus(MinguoChronology.INSTANCE.period(1, -1, -1)));
+
+        assertEquals(MinguoDate.of(110, 3, 29),
+                date.minus(MinguoChronology.INSTANCE.period(0, -1, -1)));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistDateTest.java b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistDateTest.java
new file mode 100644
index 0000000..34b08b2
--- /dev/null
+++ b/luni/src/test/java/libcore/java/time/chrono/ThaiBuddhistDateTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.time.chrono;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.DateTimeException;
+import java.time.Duration;
+import java.time.Period;
+import java.time.chrono.ChronoPeriod;
+import java.time.chrono.ThaiBuddhistChronology;
+import java.time.chrono.ThaiBuddhistDate;
+
+@RunWith(JUnit4.class)
+public class ThaiBuddhistDateTest {
+
+    @Test
+    public void minus_thaiBuddhistChronologyPeriod() {
+        ThaiBuddhistDate date = ThaiBuddhistDate.of(2484, 1, 1);
+
+        ChronoPeriod period = ThaiBuddhistChronology.INSTANCE.period(1, 1, 1);
+
+        assertEquals(ThaiBuddhistDate.of(2482, 11, 30), date.minus(period));
+    }
+
+    @Test
+    public void minus_thaiBuddhistChronologyPeriod_withNegative() {
+        ThaiBuddhistDate date = ThaiBuddhistDate.of(2484, 1, 1);
+
+        ChronoPeriod period = ThaiBuddhistChronology.INSTANCE.period(1, -1, -1);
+
+        assertEquals(ThaiBuddhistDate.of(2483, 2, 2), date.minus(period));
+    }
+
+    @Test
+    public void minus_javaTimePeriod_shouldThrowDateTimeException() {
+        ThaiBuddhistDate date = ThaiBuddhistDate.of(2484, 1, 1);
+
+        try {
+            date.minus(Period.ofDays(1));
+            fail();
+        } catch (DateTimeException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void minus_javaTimeDuration_shouldThrowDateTimeException() {
+        ThaiBuddhistDate date = ThaiBuddhistDate.of(2484, 1, 1);
+
+        try {
+            date.minus(Duration.ofDays(1));
+            fail();
+        } catch (DateTimeException ignored) {
+            // excepted
+        }
+    }
+
+    @Test
+    public void minusNull_shouldThrowNpe() {
+        try {
+            ThaiBuddhistDate.now().minus(null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
index 0e57178..7db90cd 100644
--- a/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
+++ b/luni/src/test/java/libcore/java/time/format/DateTimeFormatterTest.java
@@ -16,9 +16,19 @@
 package libcore.java.time.format;
 
 import org.junit.Test;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.chrono.Chronology;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
 import java.time.format.DecimalStyle;
+import java.time.format.FormatStyle;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAccessor;
 import java.util.Locale;
 
 import static org.junit.Assert.assertEquals;
@@ -60,4 +70,40 @@
             assertEquals(formatter.toString(), DecimalStyle.STANDARD, formatter.getDecimalStyle());
         }
     }
+
+    // Regression test for http://b/170717042.
+    @Test
+    public void test_format_locale_agq() {
+        Locale locale = new Locale("agq");
+        ZonedDateTime zonedDateTime = Instant.EPOCH.atZone(ZoneId.of("UTC"));
+        assertEquals("kɨbâ kɨ 1",
+                formatWithPattern(locale, "qqqq"/* standalone full quarter */, zonedDateTime));
+    }
+
+    @Test
+    public void test_format_locale_en_US() {
+        Locale locale = Locale.US;
+        ZonedDateTime zonedDateTime = Instant.EPOCH.atZone(ZoneId.of("UTC"));
+        assertEquals("1st quarter",
+                formatWithPattern(locale, "qqqq"/* standalone full quarter */, zonedDateTime));
+    }
+
+    private static String formatWithPattern(Locale l, String pattern, TemporalAccessor datetime) {
+        return DateTimeFormatter.ofPattern(pattern, l).format(datetime);
+    }
+
+    // 1 January 2022 00:00:00 GMT+00:00
+    private static final Instant TEST_INSTANT = Instant.ofEpochSecond(1640995200L);
+
+    // Regression test for http://b/174804526 when DateTimeFormatter fetches symbol 'B' from ICU.
+    @Test
+    public void test_format_locale_my_MM() {
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
+                .withLocale(new Locale("my", "MM"))
+                .withZone(ZoneOffset.UTC);
+        assertEquals("0:00", dateTimeFormatter.format(TEST_INSTANT));
+        TemporalAccessor accessor = dateTimeFormatter.parse("23:59");
+        assertEquals(23, accessor.getLong(ChronoField.HOUR_OF_DAY));
+        assertEquals(59, accessor.getLong(ChronoField.MINUTE_OF_HOUR));
+    }
 }
diff --git a/luni/src/test/java/libcore/java/time/zone/IcuZoneRulesProviderTest.java b/luni/src/test/java/libcore/java/time/zone/IcuZoneRulesProviderTest.java
deleted file mode 100644
index c3c0f50..0000000
--- a/luni/src/test/java/libcore/java/time/zone/IcuZoneRulesProviderTest.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.java.time.zone;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import android.icu.util.BasicTimeZone;
-import android.icu.util.TimeZone;
-import android.icu.util.TimeZoneRule;
-import android.icu.util.TimeZoneTransition;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.Month;
-import java.time.ZoneOffset;
-import java.time.zone.ZoneOffsetTransition;
-import java.time.zone.ZoneRules;
-import java.time.zone.ZoneRulesProvider;
-import java.util.Set;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-
-/**
- * Test the {@link java.time.zone.IcuZoneRulesProvider}.
- *
- * It is indirectly tested via static methods in {@link ZoneRulesProvider} as all the relevant
- * methods are protected. This test verifies that the rules returned by that provider behave
- * equivalently to the ICU rules from which they are created.
- */
-@RunWith(Parameterized.class)
-public class IcuZoneRulesProviderTest {
-
-    @Parameterized.Parameters(name = "{0}")
-    public static Iterable<String> getZoneIds() {
-        Set<String> availableZoneIds = ZoneRulesProvider.getAvailableZoneIds();
-        assertFalse("no zones returned", availableZoneIds.isEmpty());
-        return availableZoneIds;
-    }
-
-    private final String zoneId;
-
-    public IcuZoneRulesProviderTest(final String zoneId) {
-        this.zoneId = zoneId;
-    }
-
-    /**
-     * Verifies that ICU and java.time return the same transitions before and after a pre-selected
-     * set of instants in time.
-     */
-    @Test
-    public void testTransitionsNearInstants() {
-        // An arbitrary set of instants at which to test the offsets in both implementations.
-        Instant[] instants = new Instant[] {
-                LocalDateTime.of(1900, Month.DECEMBER, 24, 12, 0).toInstant(ZoneOffset.UTC),
-                LocalDateTime.of(1970, Month.JANUARY, 1, 2, 3).toInstant(ZoneOffset.UTC),
-                LocalDateTime.of(1980, Month.FEBRUARY, 4, 5, 6).toInstant(ZoneOffset.UTC),
-                LocalDateTime.of(1990, Month.MARCH, 7, 8, 9).toInstant(ZoneOffset.UTC),
-                LocalDateTime.of(2000, Month.APRIL, 10, 11, 12).toInstant(ZoneOffset.UTC),
-                LocalDateTime.of(2016, Month.MAY, 13, 14, 15).toInstant(ZoneOffset.UTC),
-                LocalDateTime.of(2020, Month.JUNE, 16, 17, 18).toInstant(ZoneOffset.UTC),
-                LocalDateTime.of(2100, Month.JULY, 19, 20, 21).toInstant(ZoneOffset.UTC),
-                // yes, adding "now" makes the test time-dependent, but it also ensures that future
-                // updates don't break on the then-current date.
-                Instant.now()
-        };
-        // Coincidentally this test verifies that all zones can be converted to ZoneRules and
-        // don't violate any of the assumptions of IcuZoneRulesProvider.
-        ZoneRules rules = ZoneRulesProvider.getRules(zoneId, false);
-        BasicTimeZone timeZone = (BasicTimeZone) TimeZone.getTimeZone(zoneId);
-
-        int[] icuOffsets = new int[2];
-        for (Instant instant : instants) {
-            ZoneOffset offset = rules.getOffset(instant);
-            Duration daylightSavings = rules.getDaylightSavings(instant);
-            timeZone.getOffset(instant.toEpochMilli(), false, icuOffsets);
-
-            assertEquals("total offset for " + zoneId + " at " + instant,
-                    icuOffsets[1] + icuOffsets[0], offset.getTotalSeconds() * 1000);
-            assertEquals("dst offset for " + zoneId + " at " + instant,
-                    icuOffsets[1], daylightSavings.toMillis());
-
-            ZoneOffsetTransition jtTrans;
-            TimeZoneTransition icuTrans;
-
-            jtTrans = rules.nextTransition(instant);
-            icuTrans = timeZone.getNextTransition(instant.toEpochMilli(), false);
-            while (isIcuOnlyTransition(icuTrans)) {
-                icuTrans = timeZone.getNextTransition(icuTrans.getTime(), false);
-            }
-            assertEquivalent(icuTrans, jtTrans);
-
-            jtTrans = rules.previousTransition(instant);
-            icuTrans = timeZone.getPreviousTransition(instant.toEpochMilli(), false);
-            // Find previous "real" transition.
-            while (isIcuOnlyTransition(icuTrans)) {
-                icuTrans = timeZone.getPreviousTransition(icuTrans.getTime(), false);
-            }
-            assertEquivalent(icuTrans, jtTrans);
-        }
-    }
-
-    /**
-     * Verifies that ICU and java.time rules return the same transitions between 1900 and 2100.
-     */
-    @Test
-    public void testAllTransitions() {
-        final Instant start = LocalDateTime.of(1900, Month.JANUARY, 1, 12, 0)
-                .toInstant(ZoneOffset.UTC);
-        // Many timezones have ongoing DST changes, so they would generate transitions endlessly.
-        // Pick a far-future end date to stop comparing in that case.
-        final Instant end = LocalDateTime.of(2100, Month.DECEMBER, 31, 12, 0)
-                .toInstant(ZoneOffset.UTC);
-
-        ZoneRules rules = ZoneRulesProvider.getRules(zoneId, false);
-        BasicTimeZone timeZone = (BasicTimeZone) TimeZone.getTimeZone(zoneId);
-
-        Instant instant = start;
-        while (instant.isBefore(end)) {
-            ZoneOffsetTransition jtTrans;
-            TimeZoneTransition icuTrans;
-
-            jtTrans = rules.nextTransition(instant);
-            icuTrans = timeZone.getNextTransition(instant.toEpochMilli(), false);
-            while (isIcuOnlyTransition(icuTrans)) {
-                icuTrans = timeZone.getNextTransition(icuTrans.getTime(), false);
-            }
-            assertEquivalent(icuTrans, jtTrans);
-            if (jtTrans == null) {
-                break;
-            }
-            instant = jtTrans.getInstant();
-        }
-    }
-
-    /**
-     * Returns {@code true} iff this transition will only be returned by ICU code.
-     * ICU reports "no-op" transitions where the raw offset and the dst savings
-     * change by the same absolute value in opposite directions, java.time doesn't
-     * return them, so find the next "real" transition.
-     */
-    private static boolean isIcuOnlyTransition(TimeZoneTransition transition) {
-        if (transition == null) {
-            return false;
-        }
-        return transition.getFrom().getRawOffset() + transition.getFrom().getDSTSavings()
-                == transition.getTo().getRawOffset() + transition.getTo().getDSTSavings();
-    }
-
-    /**
-     * Asserts that the ICU {@link TimeZoneTransition} is equivalent to the java.time {@link
-     * ZoneOffsetTransition}.
-     */
-    private static void assertEquivalent(
-            TimeZoneTransition icuTransition, ZoneOffsetTransition jtTransition) {
-        if (icuTransition == null) {
-            assertNull(jtTransition);
-            return;
-        }
-        assertEquals("time of transition",
-                Instant.ofEpochMilli(icuTransition.getTime()), jtTransition.getInstant());
-        TimeZoneRule from = icuTransition.getFrom();
-        TimeZoneRule to = icuTransition.getTo();
-        assertEquals("offset before",
-                (from.getDSTSavings() + from.getRawOffset()) / 1000,
-                jtTransition.getOffsetBefore().getTotalSeconds());
-        assertEquals("offset after",
-                (to.getDSTSavings() + to.getRawOffset()) / 1000,
-                jtTransition.getOffsetAfter().getTotalSeconds());
-    }
-}
diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java
index cdeab95..abe5ab8 100644
--- a/luni/src/test/java/libcore/java/util/CalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/CalendarTest.java
@@ -418,16 +418,4 @@
 
         }
     }
-
-    /**
-     * Ensures that Gregorian is the default Calendar for all Locales in Android. This is the
-     * historic behavior on Android; this test exists to avoid unintentional regressions.
-     * http://b/80294184
-     */
-    public void testAllDefaultCalendar_Gregorian() {
-        for (Locale locale : Locale.getAvailableLocales()) {
-            assertTrue("Default calendar should be Gregorian: " + locale,
-                    Calendar.getInstance(locale) instanceof GregorianCalendar);
-        }
-    }
 }
diff --git a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java b/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
deleted file mode 100644
index 397108c..0000000
--- a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package libcore.java.util;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-public class ConcurrentHashMapTest extends junit.framework.TestCase {
-
-    public void test_getOrDefault() {
-        MapDefaultMethodTester.test_getOrDefault(new ConcurrentHashMap<>(),
-                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/,
-                true /*getAcceptsAnyObject*/);
-    }
-
-    public void test_forEach() {
-        MapDefaultMethodTester.test_forEach(new ConcurrentHashMap<>());
-    }
-
-    public void test_putIfAbsent() {
-        MapDefaultMethodTester
-                .test_putIfAbsent(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/,
-                        false /*doesNotAcceptNullValue*/);
-    }
-
-    public void test_remove() {
-        MapDefaultMethodTester
-                .test_remove(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/,
-                        false /*doesNotAcceptNullValue*/);
-    }
-
-    public void test_replace$K$V$V() {
-        MapDefaultMethodTester
-                .test_replace$K$V$V(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/,
-                        false /*doesNotAcceptNullValue*/);
-    }
-
-    public void test_replace$K$V() {
-        MapDefaultMethodTester.test_replace$K$V(new ConcurrentHashMap<>(),
-                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
-    }
-
-    public void test_computeIfAbsent() {
-        MapDefaultMethodTester.test_computeIfAbsent(new ConcurrentHashMap<>(),
-                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
-    }
-
-    public void test_computeIfPresent() {
-        MapDefaultMethodTester.test_computeIfPresent(new ConcurrentHashMap<>(),
-                false /*doesNotAcceptNullKey*/);
-    }
-
-    public void test_compute() {
-        MapDefaultMethodTester
-                .test_compute(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/);
-    }
-
-    public void test_merge() {
-        MapDefaultMethodTester.test_merge(new ConcurrentHashMap<>(),
-                false /*doesNotAcceptNullKey*/);
-    }
-}
diff --git a/luni/src/test/java/libcore/java/util/CurrencyTest.java b/luni/src/test/java/libcore/java/util/CurrencyTest.java
index 6ed6bc6..87f4315 100644
--- a/luni/src/test/java/libcore/java/util/CurrencyTest.java
+++ b/luni/src/test/java/libcore/java/util/CurrencyTest.java
@@ -165,4 +165,50 @@
         assertEquals(0, Currency.getInstance("XFU").getNumericCode());
     }
 
+    /**
+    * It tests the current behavior, but the current behavior may not be expected.
+    */
+    public void test_getInstanceWithLocaleVariant() {
+        Locale[] invalidLocales = new Locale[] {
+            // Locale without country code
+            Locale.ROOT,
+            new Locale("en"),
+            // Invalid country code
+            new Locale("en", "XA"),
+            new Locale("en", "AA"),
+            // Locale with 3 special variants. It's invalid due to historic reason.
+            new Locale("de", "DE", "PREEURO"),
+            new Locale("pt", "PT", "PREEURO"),
+            new Locale("de", "DE", "EURO"),
+            new Locale("pt", "PT", "EURO"),
+            new Locale("zh", "HK", "HK"),
+            new Locale("en", "US", "HK"),
+        };
+        for (Locale invalidLocale : invalidLocales) {
+            try {
+                Currency.getInstance(invalidLocale);
+                fail("Currency.getInstance doesn't fail with locale:"
+                    + invalidLocale.toLanguageTag());
+            } catch (IllegalArgumentException e){
+                // expected
+            }
+        }
+    }
+
+    public void test_localeExtension() {
+        // Language=en, Country=US, Currency=Euro
+        Locale locale = Locale.forLanguageTag("en-US-u-cu-eur");
+        // This is expected because Currency.getInstance only considers country code, not extension.
+        assertEquals("USD", getCurrency(locale).getCurrencyCode());
+    }
+
+    private static Currency getCurrency(Locale l) {
+        try {
+            return Currency.getInstance(l);
+        } catch (IllegalArgumentException e) {
+            // The locale could have no country or does not have currency for other reasons.
+            return null;
+        }
+    }
+
 }
diff --git a/luni/src/test/java/libcore/java/util/EvilMapTest.java b/luni/src/test/java/libcore/java/util/EvilMapTest.java
index 2c5f913..3532462 100644
--- a/luni/src/test/java/libcore/java/util/EvilMapTest.java
+++ b/luni/src/test/java/libcore/java/util/EvilMapTest.java
@@ -40,7 +40,7 @@
     // ...but potentially return many entries.
     @Override public Set<Entry<K, V>> entrySet() { return entries; }
 
-    // Dummy implementation, not relevant for this test but
+    // Fake implementation, not relevant for this test but
     // necessary to implement AbstractMap.
     @Override public V put(K key, V val) { return val; }
   }
diff --git a/luni/src/test/java/libcore/java/util/InvalidPropertiesFormatExceptionTest.java b/luni/src/test/java/libcore/java/util/InvalidPropertiesFormatExceptionTest.java
index 35693d6..046be49 100644
--- a/luni/src/test/java/libcore/java/util/InvalidPropertiesFormatExceptionTest.java
+++ b/luni/src/test/java/libcore/java/util/InvalidPropertiesFormatExceptionTest.java
@@ -94,7 +94,7 @@
             Object obj = SerializationTester.deserializeHex(hex);
             fail("Deserialized to " + obj);
         } catch (NotSerializableException expected) {
-            // Sanity check that this is the right exception that we expected.
+            // Check that this is the right exception that we expected.
             assertEquals("Not serializable.", expected.getMessage());
         }
     }
@@ -107,7 +107,7 @@
             }
             fail();
         } catch (NotSerializableException expected) {
-            // Sanity check that this is the right exception that we expected.
+            // Check that this is the right exception that we expected.
             assertEquals("Not serializable.", expected.getMessage());
         }
     }
diff --git a/luni/src/test/java/libcore/java/util/LocaleTest.java b/luni/src/test/java/libcore/java/util/LocaleTest.java
index 6cb9e17..84a8740 100644
--- a/luni/src/test/java/libcore/java/util/LocaleTest.java
+++ b/luni/src/test/java/libcore/java/util/LocaleTest.java
@@ -1037,28 +1037,28 @@
         List<LanguageRange> priorityList = languageRangesOf("de-DE", "de-*-DE");
         List<Locale> locales = localesOf("de-DE", "de-Latn-DE", "ja-JP");
 
-        Locale dummy = Locale.FRANCE;
+        Locale fake = Locale.FRANCE;
         // should not throw
-        Locale.filter(priorityList, locales).add(dummy);
-        Locale.filter(priorityList, locales, AUTOSELECT_FILTERING).add(dummy);
-        Locale.filter(priorityList, locales, EXTENDED_FILTERING).add(dummy);
-        Locale.filter(priorityList, locales, IGNORE_EXTENDED_RANGES).add(dummy);
-        Locale.filter(priorityList, locales, MAP_EXTENDED_RANGES).add(dummy);
-        Locale.filter(languageRangesOf("de-DE"), locales, REJECT_EXTENDED_RANGES).add(dummy);
+        Locale.filter(priorityList, locales).add(fake);
+        Locale.filter(priorityList, locales, AUTOSELECT_FILTERING).add(fake);
+        Locale.filter(priorityList, locales, EXTENDED_FILTERING).add(fake);
+        Locale.filter(priorityList, locales, IGNORE_EXTENDED_RANGES).add(fake);
+        Locale.filter(priorityList, locales, MAP_EXTENDED_RANGES).add(fake);
+        Locale.filter(languageRangesOf("de-DE"), locales, REJECT_EXTENDED_RANGES).add(fake);
     }
 
     public void test_filter_resultIsModifiable_tags() {
         List<LanguageRange> priorityList = languageRangesOf("de-DE", "de-*-DE");
         List<String> tags = tagsOf("de-DE", "de-Latn-DE", "ja-JP");
 
-        String dummy = "fr-FR";
+        String fake = "fr-FR";
         // should not throw
-        Locale.filterTags(priorityList, tags).add(dummy);
-        Locale.filterTags(priorityList, tags, AUTOSELECT_FILTERING).add(dummy);
-        Locale.filterTags(priorityList, tags, EXTENDED_FILTERING).add(dummy);
-        Locale.filterTags(priorityList, tags, IGNORE_EXTENDED_RANGES).add(dummy);
-        Locale.filterTags(priorityList, tags, MAP_EXTENDED_RANGES).add(dummy);
-        Locale.filterTags(languageRangesOf("de-DE"), tags, REJECT_EXTENDED_RANGES).add(dummy);
+        Locale.filterTags(priorityList, tags).add(fake);
+        Locale.filterTags(priorityList, tags, AUTOSELECT_FILTERING).add(fake);
+        Locale.filterTags(priorityList, tags, EXTENDED_FILTERING).add(fake);
+        Locale.filterTags(priorityList, tags, IGNORE_EXTENDED_RANGES).add(fake);
+        Locale.filterTags(priorityList, tags, MAP_EXTENDED_RANGES).add(fake);
+        Locale.filterTags(languageRangesOf("de-DE"), tags, REJECT_EXTENDED_RANGES).add(fake);
     }
 
     public void test_forLanguageTag() {
@@ -1507,8 +1507,10 @@
         assertEquals("en-US-POSIX", posix.toLanguageTag());
     }
 
-    public void test_forLanguageTag_grandFatheredLocale() {
-        // Regular grandfathered locale.
+    // Legacy locales in this test refer to "grandfathered" locales as defined in
+    // https://tools.ietf.org/html/bcp47#section-2.2.8
+    public void test_forLanguageTag_legacyLocale() {
+        // Regular legacy locale.
         Locale gaulish = Locale.forLanguageTag("cel-gaulish");
         assertEquals("xtg", gaulish.getLanguage());
         assertEquals("cel-gaulish", gaulish.getExtension(Locale.PRIVATE_USE_EXTENSION));
@@ -1516,7 +1518,7 @@
         assertEquals("", gaulish.getScript());
         assertEquals("", gaulish.getVariant());
 
-        // Irregular grandfathered locale.
+        // Irregular legacy locale.
         Locale enochian = Locale.forLanguageTag("i-enochian");
         assertEquals("", enochian.getLanguage());
         assertEquals("i-enochian", enochian.getExtension(Locale.PRIVATE_USE_EXTENSION));
diff --git a/luni/src/test/java/libcore/java/util/Locales.java b/luni/src/test/java/libcore/java/util/Locales.java
index 102188b..8fe231b 100644
--- a/luni/src/test/java/libcore/java/util/Locales.java
+++ b/luni/src/test/java/libcore/java/util/Locales.java
@@ -56,7 +56,7 @@
         Locales oldLocales = getDefault();
         Locales newLocales = new Locales(uncategorizedLocale, displayLocale, formatLocale);
         newLocales.setAsDefault();
-        assertEquals(newLocales, getDefault()); // sanity check
+        assertEquals(newLocales, getDefault());
         return oldLocales;
     }
 
diff --git a/luni/src/test/java/libcore/java/util/SimpleTimeZoneTest.java b/luni/src/test/java/libcore/java/util/SimpleTimeZoneTest.java
index fdfcdcf..2bedf7c 100644
--- a/luni/src/test/java/libcore/java/util/SimpleTimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/SimpleTimeZoneTest.java
@@ -41,7 +41,7 @@
     private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
 
     /**
-     * Sanity check to ensure that the standard TimeZone for Europe/Paris has the correct DST
+     * Check to ensure that the standard TimeZone for Europe/Paris has the correct DST
      * transition times.
      */
     public void testStandardParis2014() {
@@ -100,7 +100,7 @@
     }
 
     /**
-     * Sanity check to ensure that the standard TimeZone for America/New_York has the correct DST
+     * Check to ensure that the standard TimeZone for America/New_York has the correct DST
      * transition times.
      */
     public void testStandardNewYork2014() {
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index da4a04f..a686534 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -298,7 +298,7 @@
                 }
             }
 
-            // Sanity check that whenever a display name is just a GMT string that it's the
+            // Check that whenever a display name is just a GMT string that it's the
             // right GMT string.
             String gmtDst = formatGmtString(tz, true);
             String gmtStd = formatGmtString(tz, false);
diff --git a/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java b/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java
index 1c9b399..3ae0acc 100644
--- a/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java
+++ b/luni/src/test/java/libcore/java/util/beans/PropertyChangeSupportTest.java
@@ -16,6 +16,7 @@
 
 package libcore.java.util.beans;
 
+import java.beans.IndexedPropertyChangeEvent;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeListenerProxy;
@@ -246,6 +247,68 @@
         }.test();
     }
 
+    public void testFireIndexedPropertyChange_intUpdate() {
+        Object bean = "bean";
+        PropertyChangeSupport support = new PropertyChangeSupport(bean);
+
+        EventLog listenerToIntProperty = new EventLog();
+        support.addPropertyChangeListener("intProperty", listenerToIntProperty);
+
+        support.fireIndexedPropertyChange("intProperty", 10, 1, 2);
+
+        List<PropertyChangeEvent> events = listenerToIntProperty.log;
+        assertEquals(1, events.size());
+        assertEquals(1, events.get(0).getOldValue());
+        assertEquals(2, events.get(0).getNewValue());
+        assertEquals("intProperty", events.get(0).getPropertyName());
+        assertTrue(events.get(0) instanceof IndexedPropertyChangeEvent);
+        assertEquals(10, ((IndexedPropertyChangeEvent) events.get(0)).getIndex());
+    }
+
+    public void testFireIndexedPropertyChange_intUpdate_noEventWhenOldIsEqualToNew() {
+        Object bean = "bean";
+        PropertyChangeSupport support = new PropertyChangeSupport(bean);
+
+        EventLog listenerToIntProperty = new EventLog();
+        support.addPropertyChangeListener("intProperty", listenerToIntProperty);
+
+        support.fireIndexedPropertyChange("intProperty", 0, 1, 1);
+
+        List<PropertyChangeEvent> events = listenerToIntProperty.log;
+        assertTrue(events.isEmpty());
+    }
+
+    public void testFireIndexedPropertyChange_booleanUpdate() {
+        Object bean = "bean";
+        PropertyChangeSupport support = new PropertyChangeSupport(bean);
+
+        EventLog listenerToBooleanProperty = new EventLog();
+        support.addPropertyChangeListener("booleanProperty", listenerToBooleanProperty);
+
+        support.fireIndexedPropertyChange("booleanProperty", 11, true, false);
+
+        List<PropertyChangeEvent> events = listenerToBooleanProperty.log;
+        assertEquals(1, events.size());
+        assertEquals(true, events.get(0).getOldValue());
+        assertEquals(false, events.get(0).getNewValue());
+        assertEquals("booleanProperty", events.get(0).getPropertyName());
+        assertTrue(events.get(0) instanceof IndexedPropertyChangeEvent);
+        assertEquals(11, ((IndexedPropertyChangeEvent) events.get(0)).getIndex());
+    }
+
+    public void testFireIndexedPropertyChange_booleanUpdate_noEventWhenOldIsEqualToNew() {
+        Object bean = "bean";
+        PropertyChangeSupport support = new PropertyChangeSupport(bean);
+
+        EventLog listenerToBooleanProperty = new EventLog();
+        support.addPropertyChangeListener("booleanProperty", listenerToBooleanProperty);
+
+        support.fireIndexedPropertyChange("booleanProperty", 0, true, true);
+
+        List<PropertyChangeEvent> events = listenerToBooleanProperty.log;
+        assertTrue(events.isEmpty());
+    }
+
     private String describe(PropertyChangeListener[] listeners) {
         List<String> result = new ArrayList<String>();
         for (PropertyChangeListener listener : listeners) {
diff --git a/luni/src/test/java/libcore/java/util/concurrent/AbstractExecutorServiceTest.java b/luni/src/test/java/libcore/java/util/concurrent/AbstractExecutorServiceTest.java
new file mode 100644
index 0000000..c4ed1e6
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/AbstractExecutorServiceTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AbstractExecutorServiceTest {
+
+    static class TestExecutorService extends AbstractExecutorService {
+
+        public volatile boolean isRunning = true;
+
+        public boolean awaitTermination(long timeout, TimeUnit unit) {
+            return isShutdown();
+        }
+
+        public boolean isShutdown() {
+            return !isRunning;
+        }
+
+        public boolean isTerminated() {
+            return isShutdown();
+        }
+
+        public void shutdown() {
+            isRunning = false;
+        }
+
+        public List<Runnable> shutdownNow() {
+            shutdown();
+            return Collections.emptyList();
+        }
+
+        public void execute(Runnable command) {
+            command.run();
+        }
+
+        public <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
+            return super.newTaskFor(runnable, value);
+        }
+    }
+
+    @Test
+    public void testSubmitRunnableWithValue() throws Exception {
+        Integer value = Integer.valueOf(42);
+        ExecutorService service = new TestExecutorService();
+        AtomicBoolean didRun = new AtomicBoolean(false);
+        Future<Integer> future = service.submit(() -> didRun.set(true), value);
+        Integer result = future.get();
+        assertSame(value, result);
+        assertTrue(didRun.get());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/AbstractQueuedLongSynchronizerTest.java b/luni/src/test/java/libcore/java/util/concurrent/AbstractQueuedLongSynchronizerTest.java
new file mode 100644
index 0000000..2735eab
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/AbstractQueuedLongSynchronizerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AbstractQueuedLongSynchronizerTest {
+
+    class DefaultMutex extends AbstractQueuedLongSynchronizer {
+
+        @Override
+        public boolean isHeldExclusively() {
+            return super.isHeldExclusively();
+        }
+
+        @Override
+        public boolean tryAcquire(long acquires) {
+            return super.tryAcquire(acquires);
+        }
+
+        @Override
+        public long tryAcquireShared(long acquires) {
+            return super.tryAcquireShared(acquires);
+        }
+
+        @Override
+        public boolean tryRelease(long releases) {
+            return super.tryRelease(releases);
+        }
+
+        @Override
+        public boolean tryReleaseShared(long releases) {
+            return super.tryReleaseShared(releases);
+        }
+
+        public AbstractQueuedLongSynchronizer.ConditionObject newCondition() {
+            return new AbstractQueuedLongSynchronizer.ConditionObject();
+        }
+
+    }
+
+    @Test
+    public void testDefaultIsHeldExclusivelyFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.isHeldExclusively();
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryAcquireFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryAcquire(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryAcquireSharedFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryAcquireShared(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryReleaseFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryRelease(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryReleaseSharedFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryReleaseShared(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testConditionObjectCreation() {
+        DefaultMutex mutex = new DefaultMutex();
+        AbstractQueuedLongSynchronizer.ConditionObject condition = mutex.newCondition();
+        assertTrue(mutex.owns(condition));
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/AbstractQueuedSynchronizerTest.java b/luni/src/test/java/libcore/java/util/concurrent/AbstractQueuedSynchronizerTest.java
new file mode 100644
index 0000000..0c0b98a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/AbstractQueuedSynchronizerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AbstractQueuedSynchronizerTest {
+
+    class DefaultMutex extends AbstractQueuedSynchronizer {
+
+        @Override
+        public boolean isHeldExclusively() {
+            return super.isHeldExclusively();
+        }
+
+        @Override
+        public boolean tryAcquire(int acquires) {
+            return super.tryAcquire(acquires);
+        }
+
+        @Override
+        public int tryAcquireShared(int acquires) {
+            return super.tryAcquireShared(acquires);
+        }
+
+        @Override
+        public boolean tryRelease(int releases) {
+            return super.tryRelease(releases);
+        }
+
+        @Override
+        public boolean tryReleaseShared(int releases) {
+            return super.tryReleaseShared(releases);
+        }
+
+        public AbstractQueuedSynchronizer.ConditionObject newCondition() {
+            return new AbstractQueuedSynchronizer.ConditionObject();
+        }
+
+    }
+
+    @Test
+    public void testDefaultIsHeldExclusivelyFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.isHeldExclusively();
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryAcquireFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryAcquire(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryAcquireSharedFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryAcquireShared(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryReleaseFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryRelease(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testDefaultTryReleaseSharedFails() {
+        DefaultMutex mutex = new DefaultMutex();
+        try {
+            mutex.tryReleaseShared(1);
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testConditionObjectCreation() {
+        DefaultMutex mutex = new DefaultMutex();
+        AbstractQueuedSynchronizer.ConditionObject condition = mutex.newCondition();
+        assertTrue(mutex.owns(condition));
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/BrokenBarrierExceptionTest.java b/luni/src/test/java/libcore/java/util/concurrent/BrokenBarrierExceptionTest.java
new file mode 100644
index 0000000..3632c09
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/BrokenBarrierExceptionTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.concurrent.BrokenBarrierException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BrokenBarrierExceptionTest {
+
+    /**
+     * constructor creates exception with detail message
+     */
+    @Test
+    public void testConstructWithMessage() {
+        BrokenBarrierException exception = new BrokenBarrierException("test");
+        assertEquals("test", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/CompletionExceptionTest.java b/luni/src/test/java/libcore/java/util/concurrent/CompletionExceptionTest.java
new file mode 100644
index 0000000..956026c
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/CompletionExceptionTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.concurrent.CompletionException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+public class CompletionExceptionTest {
+
+    // Adding derived class to be able to test the protected constructors
+    private class TestCompletionException extends CompletionException {
+        public TestCompletionException() {
+            super();
+        }
+        public TestCompletionException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * constructor creates exception without any details
+     */
+    @Test
+    public void testConstructNoMessage() {
+        CompletionException exception = new TestCompletionException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * constructor creates exception with detail message
+     */
+    @Test
+    public void testConstructWithMessage() {
+        CompletionException exception = new TestCompletionException("test");
+        assertEquals("test", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * constructor creates exception with detail message and cause
+     */
+    @Test
+    public void testConstructWithMessageAndCause() {
+        Throwable cause = new Exception();
+        CompletionException exception = new CompletionException("test", cause);
+        assertEquals("test", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentHashMapTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentHashMapTest.java
new file mode 100644
index 0000000..233e6d3
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentHashMapTest.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.ToDoubleBiFunction;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntBiFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongBiFunction;
+import java.util.function.ToLongFunction;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import libcore.java.util.MapDefaultMethodTester;
+
+@RunWith(JUnit4.class)
+public class ConcurrentHashMapTest {
+
+    // Constants that dictate parallelism for 'reduce' calls. The value represents the number of
+    // elements needed for the operation to be executed in parallel.
+    static final long IN_PARALLEL = 1L;
+    static final long SEQUENTIALLY = Long.MAX_VALUE;
+
+    static final int MAP_SIZE = 100;
+
+    static class SumKeys implements BiFunction<Map.Entry<Long,Long>,
+                 Map.Entry<Long,Long>, Map.Entry<Long,Long>> {
+        public Map.Entry<Long,Long> apply(Map.Entry<Long,Long> x, Map.Entry<Long,Long> y) {
+            return new AbstractMap.SimpleEntry<Long,Long>
+             (Long.valueOf(x.getKey().longValue() + y.getKey().longValue()),
+              Long.valueOf(1L));
+        }
+    }
+
+    static class IncrementKey implements Function<Map.Entry<Long, Long>, Map.Entry<Long, Long>> {
+        public Map.Entry<Long, Long> apply(Map.Entry<Long, Long> in) {
+            return new AbstractMap.SimpleEntry<Long, Long>
+                (Long.valueOf(in.getKey().longValue() + 1),
+                 Long.valueOf(1L));
+        }
+    }
+
+    static class KeyAsDouble implements ToDoubleFunction<Map.Entry<Long, Long>> {
+        public double applyAsDouble(Map.Entry<Long, Long> in) {
+            return in.getKey().doubleValue();
+        }
+    }
+
+    static class KeyAsInt implements ToIntFunction<Map.Entry<Long, Long>> {
+        public int applyAsInt(Map.Entry<Long, Long> in) {
+            return in.getKey().intValue();
+        }
+    }
+
+    static class KeyAsLong implements ToLongFunction<Map.Entry<Long, Long>> {
+        public long applyAsLong(Map.Entry<Long, Long> in) {
+            return in.getKey().longValue();
+        }
+    }
+
+    static class IncrementKeyToDouble implements ToDoubleBiFunction<Long, Long> {
+        public double applyAsDouble(Long key, Long value) {
+            return (key.doubleValue() + 1);
+        }
+    }
+
+    static class IncrementKeyToInt implements ToIntBiFunction<Long, Long> {
+        public int applyAsInt(Long key, Long value) {
+            return (key.intValue() + 1);
+        }
+    }
+
+    static class IncrementKeyToLong implements ToLongBiFunction<Long, Long> {
+        public long applyAsLong(Long key, Long value) {
+            return (key.longValue() + 1);
+        }
+    }
+
+    static ConcurrentHashMap<Long, Long> createMap() {
+        ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<Long, Long>(MAP_SIZE);
+        for (int i = 0; i < MAP_SIZE; ++i) {
+            map.put(Long.valueOf(i), Long.valueOf(-i));
+        }
+        return map;
+    }
+
+    @Test
+    public void testReduceEntriesToDoubleSequentially() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        double result = map.reduceEntriesToDouble(SEQUENTIALLY,
+                new KeyAsDouble(), 0.0, Double::sum);
+        assertEquals((double)MAP_SIZE * (MAP_SIZE - 1) / 2, result, 0.5);
+    }
+
+    @Test
+    public void testReduceEntriesToDoubleInParallel() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        double result = map.reduceEntriesToDouble(IN_PARALLEL,
+                new KeyAsDouble(), 0.0, Double::sum);
+        assertEquals((double)MAP_SIZE * (MAP_SIZE - 1) / 2, result, 0.5);
+    }
+
+    @Test
+    public void testReduceEntriesToIntSequentially() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        int result = map.reduceEntriesToInt(SEQUENTIALLY, new KeyAsInt(), 0, Integer::sum);
+        assertEquals((int)MAP_SIZE * (MAP_SIZE - 1) / 2, result);
+    }
+
+    @Test
+    public void testReduceEntriesToIntInParallel() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        int result = map.reduceEntriesToInt(IN_PARALLEL, new KeyAsInt(), 0, Integer::sum);
+        assertEquals(MAP_SIZE * (MAP_SIZE - 1) / 2, result);
+    }
+
+    @Test
+    public void testReduceEntriesToLongSequentially() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        long result = map.reduceEntriesToLong(SEQUENTIALLY, new KeyAsLong(), 0L, Long::sum);
+        assertEquals((long)MAP_SIZE * (MAP_SIZE - 1) / 2, result);
+    }
+
+    @Test
+    public void testReduceEntriesToLongInParallel() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        long result = map.reduceEntriesToLong(IN_PARALLEL, new KeyAsLong(), 0L, Long::sum);
+        assertEquals((long)MAP_SIZE * (MAP_SIZE - 1) / 2, result);
+    }
+
+    @Test
+    public void testTransformReduceEntriesSequentially() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        Map.Entry<Long, Long> result;
+        result = map.reduceEntries(SEQUENTIALLY, new IncrementKey(), new SumKeys());
+        assertEquals((long)MAP_SIZE * (MAP_SIZE + 1) / 2, result.getKey().longValue());
+    }
+
+    @Test
+    public void testTransformReduceEntriesInParallel() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        Map.Entry<Long, Long> result;
+        result = map.reduceEntries(IN_PARALLEL, new IncrementKey(), new SumKeys());
+        assertEquals((long)MAP_SIZE * (MAP_SIZE + 1) / 2, result.getKey().longValue());
+    }
+
+    @Test
+    public void testTransformReduceEntriesToDoubleSequentially() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        double result = map.reduceToDouble(SEQUENTIALLY,
+                new IncrementKeyToDouble(), 0.0, Double::sum);
+        assertEquals((double)MAP_SIZE * (MAP_SIZE + 1) / 2, result, 0.5);
+    }
+
+    @Test
+    public void testTransformReduceEntriesToDoubleInParallel() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        double result = map.reduceToDouble(IN_PARALLEL,
+                new IncrementKeyToDouble(), 0.0, Double::sum);
+        assertEquals((double)MAP_SIZE * (MAP_SIZE + 1) / 2, result, 0.5);
+    }
+
+    @Test
+    public void testTransformReduceEntriesToIntSequentially() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        int result = map.reduceToInt(SEQUENTIALLY, new IncrementKeyToInt(), 0, Integer::sum);
+        assertEquals(MAP_SIZE * (MAP_SIZE + 1) / 2, result);
+    }
+
+    @Test
+    public void testTransformReduceEntriesToIntInParallel() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        int result = map.reduceToInt(IN_PARALLEL, new IncrementKeyToInt(), 0, Integer::sum);
+        assertEquals(MAP_SIZE * (MAP_SIZE + 1) / 2, result);
+    }
+
+    @Test
+    public void testTransformReduceEntriesToLongSequentially() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        long result = map.reduceToLong(SEQUENTIALLY, new IncrementKeyToLong(), 0L, Long::sum);
+        assertEquals((long)MAP_SIZE * (MAP_SIZE + 1) / 2, result);
+    }
+
+    @Test
+    public void testTransformReduceEntriesToLongInParallel() {
+        ConcurrentHashMap<Long, Long> map = createMap();
+        long result = map.reduceToLong(IN_PARALLEL, new IncrementKeyToLong(), 0L, Long::sum);
+        assertEquals((long)MAP_SIZE * (MAP_SIZE + 1) / 2, result);
+    }
+
+    @Test
+    public void testNewKeySetWithCapacity() {
+        final int capacity = 10;
+        Set<Long> set = ConcurrentHashMap.<Long>newKeySet(capacity);
+        assertTrue(set.isEmpty());
+        for (long i = 0; i < capacity; ++i) {
+            assertTrue(set.add(i));
+        }
+        assertFalse(set.isEmpty());
+        assertEquals(capacity, set.size());
+    }
+
+    @Test
+    public void testNewKeySetWithZeroCapacity() {
+        final int capacity = 0;
+        final int elements = 10;
+        Set<Long> set = ConcurrentHashMap.<Long>newKeySet(capacity);
+        assertTrue(set.isEmpty());
+        for (long i = 0; i < elements; ++i) {
+            assertTrue(set.add(i));
+        }
+        assertFalse(set.isEmpty());
+        assertEquals(elements, set.size());
+    }
+
+    @Test
+    public void testNewKeySetWithInvalidCapacity() {
+        try {
+            final int capacity = -10;
+            Set<Long> set = ConcurrentHashMap.<Long>newKeySet(capacity);
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testGetOrDefault() {
+        MapDefaultMethodTester.test_getOrDefault(new ConcurrentHashMap<>(),
+                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/,
+                true /*getAcceptsAnyObject*/);
+    }
+
+    @Test
+    public void testForEach() {
+        MapDefaultMethodTester.test_forEach(new ConcurrentHashMap<>());
+    }
+
+    @Test
+    public void testPutIfAbsent() {
+        MapDefaultMethodTester
+                .test_putIfAbsent(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/,
+                        false /*doesNotAcceptNullValue*/);
+    }
+
+    @Test
+    public void testRemove() {
+        MapDefaultMethodTester
+                .test_remove(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/,
+                        false /*doesNotAcceptNullValue*/);
+    }
+
+    @Test
+    public void testReplace$K$V$V() {
+        MapDefaultMethodTester
+                .test_replace$K$V$V(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/,
+                        false /*doesNotAcceptNullValue*/);
+    }
+
+    @Test
+    public void testReplace$K$V() {
+        MapDefaultMethodTester.test_replace$K$V(new ConcurrentHashMap<>(),
+                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
+    }
+
+    @Test
+    public void testComputeIfAbsent() {
+        MapDefaultMethodTester.test_computeIfAbsent(new ConcurrentHashMap<>(),
+                false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
+    }
+
+    @Test
+    public void testComputeIfPresent() {
+        MapDefaultMethodTester.test_computeIfPresent(new ConcurrentHashMap<>(),
+                false /*doesNotAcceptNullKey*/);
+    }
+
+    @Test
+    public void testCompute() {
+        MapDefaultMethodTester
+                .test_compute(new ConcurrentHashMap<>(), false /*doesNotAcceptNullKey*/);
+    }
+
+    @Test
+    public void testMerge() {
+        MapDefaultMethodTester.test_merge(new ConcurrentHashMap<>(),
+                false /*doesNotAcceptNullKey*/);
+    }
+
+    private ConcurrentHashMap.KeySetView<String, Boolean> createKeySet() {
+        final int count = 5;
+        ConcurrentHashMap.KeySetView<String, Boolean> set =
+            ConcurrentHashMap.<String>newKeySet(count);
+        assertTrue(set.isEmpty());
+        set.add("A");
+        set.add("B");
+        set.add("C");
+        set.add("D");
+        set.add("E");
+        assertFalse(set.isEmpty());
+        assertEquals(count, set.size());
+        return set;
+    }
+
+    @Test
+    public void testKeySetViewClear() {
+        ConcurrentHashMap.KeySetView set = createKeySet();
+        assertFalse(set.isEmpty());
+        set.clear();
+        assertEquals(0, set.size());
+        assertTrue(set.isEmpty());
+    }
+
+    @Test
+    public void testKeySetViewContainsAll() {
+        ConcurrentHashMap.KeySetView set = createKeySet();
+        assertTrue(set.containsAll(Arrays.asList()));
+        assertTrue(set.containsAll(Arrays.asList("A")));
+        assertTrue(set.containsAll(Arrays.asList("A", "E")));
+        assertTrue(set.containsAll(Arrays.asList("A", "B", "C", "D", "E")));
+        assertFalse(set.containsAll(Arrays.asList("A", "B", "F")));
+        assertFalse(set.containsAll(Arrays.asList("F")));
+    }
+
+    @Test
+    public void testKeySetViewForEach() {
+        final int count = 8;
+        ConcurrentHashMap.KeySetView<Integer, Boolean> set =
+            ConcurrentHashMap.<Integer>newKeySet(count);
+        for(int i = 0; i < count; ++i) {
+            set.add(i+1);
+        }
+        LongAdder adder = new LongAdder();
+        set.forEach((Integer x) -> adder.add(x.longValue()));
+        // The size is small enough for the sum not to overflow
+        assertEquals(set.size() * (set.size() + 1) / 2, adder.sum());
+    }
+
+    @Test
+    public void testKeySetViewRemoveAll() {
+        ConcurrentHashMap.KeySetView set = createKeySet();
+        assertTrue(set.removeAll(Arrays.asList("A", "C")));
+        assertEquals(set.size(), 3);
+        assertTrue(set.containsAll(Arrays.asList("B", "D", "E")));
+        assertFalse(set.removeAll(Arrays.asList("A", "C")));
+        assertEquals(set.size(), 3);
+        assertTrue(set.containsAll(Arrays.asList("B", "D", "E")));
+    }
+
+    @Test
+    public void testKeySetViewRetainAll() {
+        ConcurrentHashMap.KeySetView set = createKeySet();
+        assertTrue(set.retainAll(Arrays.asList("A", "C")));
+        assertEquals(set.size(), 2);
+        assertTrue(set.containsAll(Arrays.asList("A", "C")));
+        assertFalse(set.retainAll(Arrays.asList("A", "C")));
+        assertEquals(set.size(), 2);
+        assertTrue(set.containsAll(Arrays.asList("A", "C")));
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
index 54b354e..c175d0b 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
@@ -16,62 +16,212 @@
 
 package libcore.java.util.concurrent;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import libcore.java.util.MapDefaultMethodTester;
 
-public class ConcurrentSkipListMapTest extends junit.framework.TestCase {
+@RunWith(JUnit4.class)
+public class ConcurrentSkipListMapTest {
 
-    public void test_getOrDefault() {
+    @Test
+    public void testGetOrDefault() {
         MapDefaultMethodTester.test_getOrDefault(new ConcurrentSkipListMap<>(),
                 false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/,
                 false /*getAcceptsAnyObject*/);
     }
 
-    public void test_forEach() {
+    @Test
+    public void testForEach() {
         MapDefaultMethodTester.test_forEach(new ConcurrentSkipListMap<>());
     }
 
-    public void test_putIfAbsent() {
+    @Test
+    public void testPutIfAbsent() {
         MapDefaultMethodTester
                 .test_putIfAbsent(new ConcurrentSkipListMap<>(), false /*doesNotAcceptNullKey*/,
                         false /*doesNotAcceptNullValue*/);
     }
 
-    public void test_remove() {
+    @Test
+    public void testRemove() {
         MapDefaultMethodTester
                 .test_remove(new ConcurrentSkipListMap<>(), false /*doesNotAcceptNullKey*/,
                         false /*doesNotAcceptNullValue*/);
     }
 
-    public void test_replace$K$V$V() {
+    @Test
+    public void testReplace$K$V$V() {
         MapDefaultMethodTester
                 .test_replace$K$V$V(new ConcurrentSkipListMap<>(), false /*doesNotAcceptNullKey*/,
                         false /*doesNotAcceptNullValue*/);
     }
 
-    public void test_replace$K$V() {
+    @Test
+    public void testReplace$K$V() {
         MapDefaultMethodTester.test_replace$K$V(new ConcurrentSkipListMap<>(),
                 false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
     }
 
-    public void test_computeIfAbsent() {
+    @Test
+    public void testComputeIfAbsent() {
         MapDefaultMethodTester.test_computeIfAbsent(new ConcurrentSkipListMap<>(),
                 false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
     }
 
-    public void test_computeIfPresent() {
+    @Test
+    public void testComputeIfPresent() {
         MapDefaultMethodTester.test_computeIfPresent(new ConcurrentSkipListMap<>(),
                 false /*doesNotAcceptNullKey*/);
     }
 
-    public void test_compute() {
+    @Test
+    public void testCompute() {
         MapDefaultMethodTester.test_compute(new ConcurrentSkipListMap<>(),
                 false /*doesNotAcceptNullKey*/);
     }
 
-    public void test_merge() {
+    @Test
+    public void testMerge() {
         MapDefaultMethodTester.test_merge(new ConcurrentSkipListMap<>(),
                 false /*doesNotAcceptNullKey*/);
     }
+
+    private ConcurrentSkipListMap createPopulatedMap() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        map.put("A", "a");
+        map.put("B", "b");
+        map.put("C", "c");
+        map.put("D", "d");
+        map.put("E", "e");
+        map.put("F", "f");
+        assertFalse(map.isEmpty());
+        return map;
+    }
+
+    @Test
+    public void testCloneFromSorted() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        ConcurrentSkipListMap mapClone = map.clone();
+        assertNotSame(map, mapClone);
+        Set set = map.entrySet();
+        Set setOfClone = mapClone.entrySet();
+        Iterator it = set.iterator();
+        Iterator itOfClone = setOfClone.iterator();
+        while ( it.hasNext() && itOfClone.hasNext() ) {
+            Map.Entry entry = (Map.Entry) it.next();
+            Map.Entry entryOfClone = (Map.Entry) itOfClone.next();
+            assertSame(entry.getKey(), entryOfClone.getKey());
+            assertSame(entry.getValue(), entryOfClone.getValue());
+        }
+        assertFalse(it.hasNext());
+        assertFalse(itOfClone.hasNext());
+    }
+
+    @Test
+    public void testFirstEntry() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        assertEquals("A", map.firstEntry().getKey());
+        assertEquals("a", map.firstEntry().getValue());
+    }
+
+    @Test
+    public void testLastEntry() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        assertEquals("F", map.lastEntry().getKey());
+        assertEquals("f", map.lastEntry().getValue());
+    }
+
+    @Test
+    public void testSubMap() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        ConcurrentNavigableMap subMap = map.subMap("B", "D");
+        assertEquals(2, subMap.size());
+        assertFalse(subMap.containsKey("A"));
+        assertTrue(subMap.containsKey("B"));
+        assertTrue(subMap.containsKey("C"));
+        assertFalse(subMap.containsKey("D"));
+        assertFalse(subMap.containsKey("E"));
+        assertFalse(subMap.containsKey("F"));
+    }
+
+    @Test
+    public void testSubMapEmpty() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        ConcurrentNavigableMap subMap = map.subMap("A", "A");
+        assertTrue(subMap.isEmpty());
+        assertFalse(subMap.containsKey("A"));
+        assertFalse(subMap.containsKey("B"));
+        assertFalse(subMap.containsKey("C"));
+        assertFalse(subMap.containsKey("D"));
+        assertFalse(subMap.containsKey("E"));
+        assertFalse(subMap.containsKey("F"));
+    }
+
+    @Test
+    public void testHeadMap() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        ConcurrentNavigableMap subMap = map.headMap("D");
+        assertEquals(3, subMap.size());
+        assertTrue(subMap.containsKey("A"));
+        assertTrue(subMap.containsKey("B"));
+        assertTrue(subMap.containsKey("C"));
+        assertFalse(subMap.containsKey("D"));
+        assertFalse(subMap.containsKey("E"));
+        assertFalse(subMap.containsKey("F"));
+    }
+
+    @Test
+    public void testHeadMapEmpty() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        ConcurrentNavigableMap subMap = map.headMap("A");
+        assertTrue(subMap.isEmpty());
+        assertFalse(subMap.containsKey("A"));
+        assertFalse(subMap.containsKey("B"));
+        assertFalse(subMap.containsKey("C"));
+        assertFalse(subMap.containsKey("D"));
+        assertFalse(subMap.containsKey("E"));
+        assertFalse(subMap.containsKey("F"));
+    }
+
+    @Test
+    public void testTailMap() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        ConcurrentNavigableMap subMap = map.tailMap("C");
+        assertEquals(4, subMap.size());
+        assertFalse(subMap.containsKey("A"));
+        assertFalse(subMap.containsKey("B"));
+        assertTrue(subMap.containsKey("C"));
+        assertTrue(subMap.containsKey("D"));
+        assertTrue(subMap.containsKey("E"));
+        assertTrue(subMap.containsKey("F"));
+    }
+
+    @Test
+    public void testTailMapSingleElement() {
+        ConcurrentSkipListMap map = createPopulatedMap();
+        ConcurrentNavigableMap subMap = map.tailMap("F");
+        assertEquals(1, subMap.size());
+        assertFalse(subMap.containsKey("A"));
+        assertFalse(subMap.containsKey("B"));
+        assertFalse(subMap.containsKey("C"));
+        assertFalse(subMap.containsKey("D"));
+        assertFalse(subMap.containsKey("E"));
+        assertTrue(subMap.containsKey("F"));
+    }
+
 }
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListSetTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListSetTest.java
new file mode 100644
index 0000000..bd858b0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListSetTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ConcurrentSkipListSetTest {
+
+    private ConcurrentSkipListSet createPopulatedSet() {
+        ConcurrentSkipListSet set = new ConcurrentSkipListSet();
+        set.add("A");
+        set.add("B");
+        set.add("C");
+        set.add("D");
+        set.add("E");
+        set.add("F");
+        assertFalse(set.isEmpty());
+        return set;
+    }
+
+    @Test
+    public void testConstructorFromSortedSet() {
+        TreeSet treeSet = new TreeSet();
+        treeSet.add("A");
+        treeSet.add("B");
+        treeSet.add("C");
+        treeSet.add("D");
+        treeSet.add("E");
+        treeSet.add("F");
+        ConcurrentSkipListSet skipListSet = new ConcurrentSkipListSet(treeSet);
+        assertEquals(treeSet.size(), skipListSet.size());
+        while (!treeSet.isEmpty()) {
+            assertFalse(skipListSet.isEmpty());
+            assertEquals(treeSet.pollFirst(), skipListSet.pollFirst());
+        }
+    }
+
+    @Test
+    public void testClone() {
+        ConcurrentSkipListSet set = createPopulatedSet();
+        ConcurrentSkipListSet setClone = set.clone();
+        assertNotSame(set, setClone);
+        Iterator it = set.iterator();
+        Iterator itOfClone = setClone.iterator();
+        while (it.hasNext() && itOfClone.hasNext()) {
+            String entry = (String) it.next();
+            String entryOfClone = (String) itOfClone.next();
+            assertSame(entry, entryOfClone);
+        }
+        assertFalse(it.hasNext());
+        assertFalse(itOfClone.hasNext());
+    }
+
+    @Test
+    public void testDescendingIterator() {
+        ConcurrentSkipListSet set = createPopulatedSet();
+        int size = set.size();
+        int i;
+        Iterator it = set.descendingIterator();
+        String lastVal = null;
+        for (i = 0; it.hasNext(); i++) {
+            String val = (String) it.next();
+            assertTrue(set.contains(val));
+            if(lastVal != null) {
+                assertTrue(0 <= lastVal.compareTo(val));
+            }
+            lastVal = val;
+        }
+        assertEquals(i, size);
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
index fc783c0..80005df 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/CopyOnWriteArrayListTest.java
@@ -101,6 +101,15 @@
         assertEquals(Arrays.asList("a", "d"), list);
     }
 
+    public void testRetainAll() {
+        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
+        list.addAll(Arrays.asList("a", "b", "c", "d", "e"));
+        assertTrue(list.retainAll(Arrays.asList("b", "d")));
+        assertEquals(2, list.size());
+        assertFalse(list.retainAll(Arrays.asList("b", "d")));
+        assertEquals(2, list.size());
+    }
+
     public void testSubListClear() {
         CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
         list.addAll(Arrays.asList("a", "b", "c", "d", "e"));
diff --git a/luni/src/test/java/libcore/java/util/concurrent/CountedCompleterTest.java b/luni/src/test/java/libcore/java/util/concurrent/CountedCompleterTest.java
new file mode 100644
index 0000000..3f14d14
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/CountedCompleterTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CountedCompleterTest {
+
+    /**
+     * Exercises the completion of all tasks once one of them has a result.
+     *
+     * Instead of needing all tasks to be performed to get a result, this will only expect one of
+     * them to provide it. That task, given by choiceIndex, will just complete the root completer.
+     * All other tasks will remain "unfinished".
+     *
+     * The result is passed through setRawResult().
+     */
+    private static int chooseOne(Integer[] array, int choiceIndex) {
+        class Task extends CountedCompleter<Integer> {
+            final int lo;
+            final int hi;
+            AtomicInteger ai = new AtomicInteger(0);
+
+            Task(Task parent, int lo, int hi) {
+                super(parent);
+                this.lo = lo;
+                this.hi = hi;
+            }
+
+            @Override
+            public void compute() {
+                if (hi - lo >= 2) {
+                    int mid = (lo + hi) >>> 1;
+                    // must set pending count before fork
+                    setPendingCount(2);
+                    new Task(this, mid, hi).fork(); // right child
+                    new Task(this, lo, mid).fork(); // left child
+                } else if (hi > lo) {
+                    if (choiceIndex == lo) {
+                        final CountedCompleter root = getRoot();
+                        final Integer val = Integer.valueOf(array[lo]);
+                        if (root != null) {
+                            root.complete(val);
+                        } else {
+                            complete(val); // the current task is the root
+                        }
+                    }
+                }
+            }
+
+            public Integer getRawResult() {
+                return new Integer(ai.intValue());
+            }
+
+            protected void setRawResult(Integer val) {
+                ai.addAndGet(val.intValue());
+            }
+        }
+        return new Task(null, 0, array.length).invoke().intValue();
+    }
+
+    /**
+     * complete marks a task as complete regardless of the pending count.
+     *
+     * The test will only require one task to complete.
+     */
+    @Test
+    public void testRecursiveChoice() {
+        int n = 7;
+        Integer[] a = new Integer[n];
+        for (int i = 0; i < n; i++) {
+            a[i] = i + 1;
+        }
+        for (int chosenOne = 0; chosenOne < n; ++chosenOne) {
+            final int result = chooseOne(a, chosenOne);
+            assertEquals(chosenOne + 1, result);
+        }
+    }
+
+    /**
+     * Forces a task to complete all its children by running them its own pool.
+     *
+     * After a parent task adds its children tasks to it's own pool queue, it uses helpComplete to
+     * ensure that those tasks are run before it returns. As all tasks are queued to the same pool
+     * and the parallelism is set to 1, this will make the pool execute the children tasks from
+     * within the execution of the parent.
+     */
+    private static void completeAllChildren(Integer[] array, Consumer<Integer> action) {
+
+        /**
+         * Leaf task that just runs the action and then completes.
+         */
+        class Task extends CountedCompleter<Integer> {
+            final int idx;
+
+            Task(CountedCompleter<Integer> parent, int idx) {
+                super(parent);
+                this.idx = idx;
+            }
+
+            @Override
+            public void compute() {
+                action.accept(array[idx]);
+                tryComplete();
+            }
+        }
+
+        /**
+         * The parent task that creates and queues its children, then executes them on its own pool
+         * before completing.
+         */
+        class MainTask extends CountedCompleter<Integer> {
+            final ForkJoinPool pool;
+            final int lo;
+            final int hi;
+
+            MainTask(ForkJoinPool pool, int lo, int hi) {
+                super(null);
+                this.pool = pool;
+                this.lo = lo;
+                this.hi = hi;
+            }
+
+            @Override
+            public void compute() {
+                final int count = hi - lo;
+                setPendingCount(count);
+
+                for (int idx = lo; idx < hi; ++idx) {
+                    // Do not fork the task, rather add it to the parent's pool so that it is
+                    // guaranteed not to be running before this compute() returns, unless
+                    // helpComplete() is called.
+                    pool.submit(new Task(this, idx));
+                }
+
+                // Make the pool run all the children tasks before moving one
+                helpComplete(count);
+
+                // If helpComplete() worked properly, by this point the pending count should be back
+                // to 0, so the tryComplete will terminate this task. Otherwise, the task will not
+                // get completed.
+                if (getPendingCount() == 0) {
+                    tryComplete();
+                }
+            }
+        }
+
+        // Use a pool with parallelism set to 1 so the children tasks cannot run before the main
+        // task, unless helpComplete is used.
+        ForkJoinPool pool = new ForkJoinPool(1);
+        MainTask task = new MainTask(pool, 0, array.length);
+        pool.submit(task);
+        task.join();
+    }
+
+    /**
+     * helpComplete attempts to process at most a given number of unprocessed children tasks.
+     */
+    @Test
+    public void testHelpComplete() {
+        int n = 7;
+        Integer[] a = new Integer[n];
+        for (int i = 0; i < n; i++) {
+            a[i] = i + 1;
+        }
+        AtomicInteger ai = new AtomicInteger(0);
+        // Use an atomic add as the action for each task. This will add all the elements of the
+        // array into the ai variable. Since the elements are between 1 and 7, the number does not
+        // overflow.
+        completeAllChildren(a, ai::addAndGet);
+        assertEquals(n * (n + 1) / 2, ai.get());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ExecutionExceptionTest.java b/luni/src/test/java/libcore/java/util/concurrent/ExecutionExceptionTest.java
new file mode 100644
index 0000000..d848403
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ExecutionExceptionTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+public class ExecutionExceptionTest {
+
+    // Adding derived class to be able to test the protected constructors
+    private class TestExecutionException extends ExecutionException {
+        public TestExecutionException() {
+            super();
+        }
+        public TestExecutionException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * constructor creates exception without any details
+     */
+    @Test
+    public void testConstructNoMessage() {
+        ExecutionException exception = new TestExecutionException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * constructor creates exception with detail message
+     */
+    @Test
+    public void testConstructWithMessage() {
+        ExecutionException exception = new TestExecutionException("test");
+        assertEquals("test", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * constructor creates exception with detail message and cause
+     */
+    @Test
+    public void testConstructWithMessageAndCause() {
+        Throwable cause = new Exception();
+        ExecutionException exception = new ExecutionException("test", cause);
+        assertEquals("test", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ExecutorServiceAutoCloseable.java b/luni/src/test/java/libcore/java/util/concurrent/ExecutorServiceAutoCloseable.java
new file mode 100644
index 0000000..2df0b29
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ExecutorServiceAutoCloseable.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+final class ExecutorServiceAutoCloseable implements AutoCloseable {
+    private final ExecutorService service;
+
+    public ExecutorServiceAutoCloseable(ExecutorService service) {
+        this.service = service;
+    }
+
+    @Override
+    public void close() {
+        try {
+            service.shutdown();
+
+            if(!service.awaitTermination(5000, TimeUnit.MILLISECONDS)) {
+                service.shutdownNow();
+                service.awaitTermination(1000, TimeUnit.MILLISECONDS);
+                fail();
+            }
+        } catch(InterruptedException e) {
+            fail("Unexpected InterruptedException: " + e.getMessage());
+        }
+    };
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ExecutorsTest.java b/luni/src/test/java/libcore/java/util/concurrent/ExecutorsTest.java
new file mode 100644
index 0000000..dc44a81
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ExecutorsTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ExecutorsTest {
+
+    class TestRunnable implements Runnable {
+        public void run() { }
+    }
+
+    @Test
+    public void testNewWorkStealingPoolDefault() {
+        final ExecutorService e = Executors.newWorkStealingPool();
+        try (ExecutorServiceAutoCloseable cleaner = new ExecutorServiceAutoCloseable(e)) {
+            e.execute(new TestRunnable());
+            e.execute(new TestRunnable());
+            e.execute(new TestRunnable());
+        }
+    }
+
+    @Test
+    public void testNewWorkStealingPoolWithParallelism() {
+        final ExecutorService e = Executors.newWorkStealingPool(2);
+        try (ExecutorServiceAutoCloseable cleaner = new ExecutorServiceAutoCloseable(e)) {
+            e.execute(new TestRunnable());
+            e.execute(new TestRunnable());
+            e.execute(new TestRunnable());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/FlowTest.java b/luni/src/test/java/libcore/java/util/concurrent/FlowTest.java
new file mode 100644
index 0000000..b6e564b
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/FlowTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.Flow;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FlowTest {
+
+    @Test
+    /**
+     * defaultBufferSize returns default value for Publisher or Subscriber buffering.
+     */
+    public void testDefaultBufferSize() {
+        // Currently the implementation always returns 256, as documented in the API. If this
+        // changes, the test would need to be adjusted as well. This, at least, can serve as a
+        // reminder to update the documentation.
+        assertEquals(256, Flow.defaultBufferSize());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ForkJoinPoolTest.java b/luni/src/test/java/libcore/java/util/concurrent/ForkJoinPoolTest.java
new file mode 100644
index 0000000..578970e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ForkJoinPoolTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ForkJoinPoolTest {
+
+    @Test
+    public void testSubmit() {
+        final ForkJoinPool pool = new ForkJoinPool();
+        try (ExecutorServiceAutoCloseable cleaner = new ExecutorServiceAutoCloseable(pool)) {
+            final AtomicInteger result = new AtomicInteger(0);
+            ForkJoinTask task = pool.submit(() -> result.addAndGet(42), result);
+            assertSame(result, task.get());
+            assertEquals(42, result.get());
+        } catch(Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testGetRunningThreadCount() {
+        final ForkJoinPool pool = new ForkJoinPool();
+        try (ExecutorServiceAutoCloseable cleaner = new ExecutorServiceAutoCloseable(pool)) {
+            assertEquals(0, pool.getRunningThreadCount());
+
+            final AtomicInteger value = new AtomicInteger(0);
+            final AtomicBoolean stop = new AtomicBoolean(false);
+            ForkJoinTask task = pool.submit(new Runnable() {
+                    public void run() {
+                        while(!stop.get()) {
+                            value.incrementAndGet();
+                        }
+                        stop.set(false);
+                    }
+                });
+            assertEquals(1, pool.getRunningThreadCount());
+            stop.set(true);
+            task.join();
+            pool.awaitTermination(2000, TimeUnit.MILLISECONDS);
+            assertEquals(0, pool.getRunningThreadCount());
+        } catch(Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ForkJoinTaskTest.java b/luni/src/test/java/libcore/java/util/concurrent/ForkJoinTaskTest.java
new file mode 100644
index 0000000..264aa6a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ForkJoinTaskTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ForkJoinTaskTest {
+
+    /**
+     * adapt uses a Runnable to perform a task, returning as the result the provided object
+     */
+    @Test
+    public void testAdaptToRunnableWithResult() {
+
+        final AtomicInteger result = new AtomicInteger(0);
+        final ForkJoinTask task = ForkJoinTask.adapt(() -> result.addAndGet(42), result);
+        final ForkJoinPool pool = new ForkJoinPool(1);
+        try (ExecutorServiceAutoCloseable cleaner = new ExecutorServiceAutoCloseable(pool)) {
+            pool.execute(task);
+            assertSame(result, task.join());
+            assertEquals(42, result.get());
+        } catch(Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    /**
+     * adapt uses a Callable to perform a task, returning as the result the value returned from the
+     * call() function
+     */
+    @Test
+    public void testAdaptToCallable() {
+
+        Callable callable = () -> { return Integer.valueOf(42); };
+        final ForkJoinTask task = ForkJoinTask.adapt(callable);
+        final ForkJoinPool pool = new ForkJoinPool(1);
+        try (ExecutorServiceAutoCloseable cleaner = new ExecutorServiceAutoCloseable(pool)) {
+            pool.execute(task);
+            Integer result = (Integer)task.join();
+            assertEquals(42, result.intValue());
+        } catch(Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    /**
+     * adapt uses a Callable to perform a task, converting any checked exception into
+     * RuntimeException
+     */
+    @Test
+    public void testAdaptToCallableThrowsException() {
+
+        Callable callable = () -> { throw new Exception("Test passed"); };
+        final ForkJoinTask task = ForkJoinTask.adapt(callable);
+        final ForkJoinPool pool = new ForkJoinPool(1);
+        try (ExecutorServiceAutoCloseable cleaner = new ExecutorServiceAutoCloseable(pool)) {
+            pool.execute(task);
+            Integer result = (Integer)task.join();
+            fail("Expected RuntimeException");
+        } catch(RuntimeException e) {
+            assertTrue(e.getMessage().contains("Test passed"));
+        } catch(Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/PhaserTest.java b/luni/src/test/java/libcore/java/util/concurrent/PhaserTest.java
new file mode 100644
index 0000000..c489a2f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/PhaserTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertSame;
+
+import java.util.concurrent.Phaser;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PhaserTest {
+
+    /**
+     * The root ancestor of a phaser should be returned from getRoot
+     */
+    @Test
+    public void testGetRoot() {
+        Phaser root = new Phaser();
+        Phaser parent = new Phaser(root);
+        Phaser child = new Phaser(parent);
+        assertSame(root, root.getRoot());
+        assertSame(root, parent.getRoot());
+        assertSame(root, child.getRoot());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/RecursiveTaskTest.java b/luni/src/test/java/libcore/java/util/concurrent/RecursiveTaskTest.java
new file mode 100644
index 0000000..861c498
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/RecursiveTaskTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.time.Duration;
+import java.util.concurrent.RecursiveTask;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RecursiveTaskTest {
+
+    static final long MILLIS_TO_NANO = (1000 * 1000);
+
+    final class SumTask extends RecursiveTask<Integer> {
+        final int value;
+
+        SumTask(int value) {
+            this.value = value;
+        }
+
+        @Override
+        protected Integer compute() {
+            if (value <= 1)
+                return value;
+            SumTask subTask = new SumTask(value - 1);
+            subTask.fork();
+            return subTask.join() + value;
+        }
+
+        public void waitForCompletion(Integer forceResult) {
+            if(!TestUtils.waitWhileTrueOrTimeout(Duration.ofSeconds(10),
+                        () -> { return !isDone(); })) {
+                fail("timed out waiting for task completion");
+            }
+            if (forceResult != null) {
+                super.setRawResult(forceResult);
+            }
+        }
+    }
+
+    @Test
+    public void testSetRawResult() {
+        SumTask task = new SumTask(10);
+        final Integer expected = Integer.valueOf(-1);
+        task.fork();
+        task.waitForCompletion(expected);
+        Integer result = task.join();
+        assertSame(expected, result);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ReentrantReadWriteLockTest.java b/luni/src/test/java/libcore/java/util/concurrent/ReentrantReadWriteLockTest.java
new file mode 100644
index 0000000..4497157
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/ReentrantReadWriteLockTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.time.Duration;
+import java.util.Collection;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ReentrantReadWriteLockTest {
+
+    class ReadLockRunnable implements Runnable {
+        final ReentrantReadWriteLock lock;
+
+        ReadLockRunnable(ReentrantReadWriteLock lock) {
+            this.lock = lock;
+        }
+
+        @Override
+        public void run() {
+            lock.readLock().lock();
+            lock.readLock().unlock();
+        }
+    }
+
+    class WriteLockRunnable implements Runnable {
+        final ReentrantReadWriteLock lock;
+
+        WriteLockRunnable(ReentrantReadWriteLock lock) {
+            this.lock = lock;
+        }
+
+        @Override
+        public void run() {
+            lock.writeLock().lock();
+            lock.writeLock().unlock();
+        }
+    }
+
+    static class DefaultReentrantReadWriteLock extends ReentrantReadWriteLock {
+
+        DefaultReentrantReadWriteLock() {
+            super();
+        }
+
+        @Override
+        public Collection<Thread> getQueuedReaderThreads() {
+            return super.getQueuedReaderThreads();
+        }
+
+        @Override
+        public Collection<Thread> getQueuedWriterThreads() {
+            return super.getQueuedWriterThreads();
+        }
+    }
+
+    private void waitForQueuedThread(DefaultReentrantReadWriteLock lock, Thread thread) {
+        if(!TestUtils.waitWhileTrueOrTimeout(Duration.ofSeconds(2),
+                    () -> { return !lock.hasQueuedThread(thread); })) {
+            fail("timed out waiting for queued thread");
+        }
+    }
+
+    @Test
+    public void testReadLockCondition() {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+        try {
+            final Condition condition = lock.readLock().newCondition();
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException success) {
+        } catch (Throwable t) {
+            fail("Unexpected exception: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testGetQueuedReaderThreads() {
+        final DefaultReentrantReadWriteLock lock = new DefaultReentrantReadWriteLock();
+        Thread thread1 = new Thread(new ReadLockRunnable(lock));
+        Thread thread2 = new Thread(new ReadLockRunnable(lock));
+        lock.writeLock().lock();
+        assertTrue(lock.getQueuedReaderThreads().isEmpty());
+        thread1.start();
+        waitForQueuedThread(lock, thread1);
+        assertTrue(lock.getQueuedReaderThreads().contains(thread1));
+        thread2.start();
+        waitForQueuedThread(lock, thread2);
+        assertTrue(lock.getQueuedReaderThreads().contains(thread2));
+        lock.writeLock().unlock();
+        TestUtils.joinThreadOrFail(2000, thread1);
+        TestUtils.joinThreadOrFail(2000, thread2);
+        assertTrue(lock.getQueuedReaderThreads().isEmpty());
+    }
+
+    @Test
+    public void testGetQueuedWriterThreads() {
+        final DefaultReentrantReadWriteLock lock = new DefaultReentrantReadWriteLock();
+        Thread thread1 = new Thread(new WriteLockRunnable(lock));
+        Thread thread2 = new Thread(new WriteLockRunnable(lock));
+        lock.writeLock().lock();
+        assertTrue(lock.getQueuedWriterThreads().isEmpty());
+        thread1.start();
+        waitForQueuedThread(lock, thread1);
+        assertTrue(lock.getQueuedWriterThreads().contains(thread1));
+        thread2.start();
+        waitForQueuedThread(lock, thread2);
+        assertTrue(lock.getQueuedWriterThreads().contains(thread2));
+        lock.writeLock().unlock();
+        TestUtils.joinThreadOrFail(2000, thread1);
+        TestUtils.joinThreadOrFail(2000, thread2);
+        assertTrue(lock.getQueuedWriterThreads().isEmpty());
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/RejectedExecutionExceptionTest.java b/luni/src/test/java/libcore/java/util/concurrent/RejectedExecutionExceptionTest.java
new file mode 100644
index 0000000..14c1408
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/RejectedExecutionExceptionTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.concurrent.RejectedExecutionException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+public class RejectedExecutionExceptionTest {
+
+    // Adding derived class to be able to test the protected constructors
+    private class TestExecutionException extends RejectedExecutionException {
+        public TestExecutionException() {
+            super();
+        }
+        public TestExecutionException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * constructor creates exception without any details
+     */
+    @Test
+    public void testConstructDefault() {
+        RejectedExecutionException exception = new TestExecutionException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * constructor creates exception with detail message and cause
+     */
+    @Test
+    public void testConstructWithMessageAndCause() {
+        Throwable cause = new Exception();
+        RejectedExecutionException exception = new RejectedExecutionException("test", cause);
+        assertEquals("test", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * constructor creates exception with cause
+     */
+    @Test
+    public void testConstructWithCause() {
+        Throwable cause = new Exception("root");
+        RejectedExecutionException exception = new RejectedExecutionException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/TestUtils.java b/luni/src/test/java/libcore/java/util/concurrent/TestUtils.java
new file mode 100644
index 0000000..eaa4330
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/concurrent/TestUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package libcore.java.util.concurrent;
+
+import static org.junit.Assert.fail;
+
+import java.time.Duration;
+import java.util.function.BooleanSupplier;
+
+class TestUtils {
+
+    public static final long MILLIS_TO_NANO = (1000 * 1000);
+
+    public static void joinThreadOrFail(long timeoutMillis, Thread thread) {
+        try {
+            thread.join(timeoutMillis);
+        } catch (InterruptedException fail) {
+            fail("InterruptedException when joining thread");
+        } finally {
+            if (thread.getState() != Thread.State.TERMINATED) {
+                thread.interrupt();
+                fail("timed out waiting to join thread");
+            }
+        }
+    }
+
+    public static boolean waitWhileTrueOrTimeout(Duration timeout, BooleanSupplier conditionFn) {
+        Duration startTime = Duration.ofNanos(System.nanoTime());
+        while (conditionFn.getAsBoolean()) {
+            Duration now = Duration.ofNanos(System.nanoTime());
+            if (now.minus(startTime).compareTo(timeout) >= 0) {
+                return false;
+            }
+            Thread.yield();
+        }
+        return true;
+    }
+
+}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ThreadPoolExecutorTest.java b/luni/src/test/java/libcore/java/util/concurrent/ThreadPoolExecutorTest.java
index 4273598..48b2781 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/ThreadPoolExecutorTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/ThreadPoolExecutorTest.java
@@ -32,8 +32,8 @@
 
         // It should be illegal to set a core pool size that's larger than the max
         // pool size but apps have been allowed to get away with it so far. The pattern
-        // below occurs in a commonly used library. Note that the executor is in a sane
-        // state at the end of both method calls.
+        // below occurs in a commonly used library. Note that the executor is in a
+        // consistent state at the end of both method calls.
         tp.setCorePoolSize(5);
         tp.setMaximumPoolSize(5);
     }
diff --git a/luni/src/test/java/libcore/java/util/jar/Pack200Test.java b/luni/src/test/java/libcore/java/util/jar/Pack200Test.java
new file mode 100644
index 0000000..11078f6
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/jar/Pack200Test.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util.jar;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.jar.Pack200;
+
+@RunWith(JUnit4.class)
+public class Pack200Test {
+
+    @Test
+    public void newPacker_throwsNpe() {
+        try {
+            // Default implementation is removed and alternative is not available
+            Pack200.newPacker();
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    @Test
+    public void newUnpacker_throwsNpe() {
+        try {
+            // Default implementation is removed and alternative is not available
+            Pack200.newUnpacker();
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+
+}
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 c2d56ae..630cea4 100644
--- a/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java
+++ b/luni/src/test/java/libcore/java/util/regex/OldMatcherTest.java
@@ -617,7 +617,7 @@
                             }
                             m.appendTail(sb);
                         } catch (Exception expected) {
-                            // This code is inherently unsafe and crazy;
+                            // This code is inherently unsafe;
                             // we're just trying to provoke native crashes!
                         }
                     }
diff --git a/luni/src/test/java/libcore/java/util/stream/CollectorsTest.java b/luni/src/test/java/libcore/java/util/stream/CollectorsTest.java
new file mode 100644
index 0000000..ecec61b
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/stream/CollectorsTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util.stream;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import static java.util.stream.Collectors.counting;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toList;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@RunWith(JUnit4.class)
+public class CollectorsTest {
+
+    @Test
+    public void counting_countsNulls() {
+        long count = Stream.of(null, null, null).collect(counting());
+
+        assertEquals(3, count);
+    }
+
+    @Test
+    public void counting_emptyStream() {
+        assertEquals(0L, (long) Stream.empty().collect(counting()));
+    }
+
+    @Test
+    public void counting_nonEmptyStream() {
+        long count = Stream.of(null, 1, 2, "").collect(counting());
+
+        assertEquals(4, count);
+    }
+
+    @Test
+    public void counting_largeStream() {
+        int size = 10_000_000;
+
+        long actual = Stream.generate(() -> 1)
+                .limit(size)
+                .collect(counting());
+
+        assertEquals(size, actual);
+    }
+
+    @Test
+    public void collectorOf() {
+        Collector<Integer, int[], int[]> sqSumCollector =
+                Collector.of(
+                        () -> new int[] {0},
+                        (sum, next) -> sum[0] = sum[0] + next * next,
+                        (a, b) -> new int[] {a[0] + b[0]},
+                        Collector.Characteristics.UNORDERED);
+
+        int[] anArray = new int[] {10};
+        assertSame("Finisher is identity fn", anArray, sqSumCollector.finisher().apply(anArray));
+
+        assertArrayEquals(new int[]{0}, sqSumCollector.supplier().get());
+        assertArrayEquals(new int[] {20}, sqSumCollector.combiner().apply(anArray, anArray));
+
+        sqSumCollector.accumulator().accept(anArray, 10);
+        assertArrayEquals(new int[] {110}, anArray);
+        assertTrue(sqSumCollector.characteristics().contains(Collector.Characteristics.UNORDERED));
+
+        assertArrayEquals(new int[] {30}, Stream.of(1, 2, 3, 4).collect(sqSumCollector));
+    }
+
+    @Test
+    public void mapping_asArgumentToCollect() {
+        Collector<Integer, ?, List<Integer>> plusOneCollector =
+                Collectors.mapping((Integer x) -> x + 1, toList());
+
+        List<Integer> actual = Stream.of(1, 2, 10).collect(plusOneCollector);
+
+        assertEquals(List.of(2, 3, 11), actual);
+    }
+
+    @Test
+    public void mapping_asArgumentInGroupingBy() {
+        Collector<Integer, ?, List<Integer>> plusOneCollector =
+                Collectors.mapping((Integer x) -> x + 1, toList());
+
+        Map<Boolean, List<Integer>> actual = Stream.of(1, 2, 3, 4)
+                .collect(groupingBy(x -> x % 2 == 0, plusOneCollector));
+        Map<Boolean, List<Integer>> expected = Map.of(true, List.of(3, 5), false, List.of(2, 4));
+
+        assertEquals(expected, actual);
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/stream/LongStreamTest.java b/luni/src/test/java/libcore/java/util/stream/LongStreamTest.java
new file mode 100644
index 0000000..3c36aa7
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/stream/LongStreamTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util.stream;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.stream.LongStream;
+
+@RunWith(JUnit4.class)
+public class LongStreamTest {
+
+    private static final int[] TEST_SIZES = {0, 1, 2, 10, 20, 100, 1000};
+
+    @Test
+    public void ofArraysWithDifferentSizes() {
+        for (int size : TEST_SIZES) {
+            long[] sourceArray = generate(size);
+            LongStream stream = LongStream.of(sourceArray);
+
+            long[] destArray = stream.toArray();
+
+            assertFalse("By default stream should be sequential", stream.isParallel());
+            assertNotSame("New array should be generated", sourceArray, destArray);
+            assertArrayEquals(sourceArray, destArray);
+        }
+    }
+
+    @Test
+    public void ofNullArray_shouldThrowNPE() {
+        try {
+            LongStream.of((long[]) null);
+            fail();
+        } catch (NullPointerException ignored) {
+            // expected
+        }
+    }
+
+    private long[] generate(int size) {
+        long[] array = new long[size];
+
+        for (int index = 0; index < size; ++index) {
+            array[index] = index + 1;
+        }
+
+        return array;
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index 2175289..ae07e02 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -26,6 +26,7 @@
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 import java.util.Enumeration;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -85,4 +86,40 @@
             fail();
         } catch(FileNotFoundException expected) {}
     }
+
+    /**
+     * cp1251.zip archive has single empty file with cp1251 encoding name.
+     * Its name is 'имя файла'('file name' in Russian), but in cp1251.
+     * It was created using "convmv -f utf-8 -t cp1251 &lt;file&gt; --notest".
+     */
+    public void test_zipFileWith_cp1251_fileNames() throws Exception {
+        String resourceName = "/libcore/java/util/zip/cp1251.zip";
+
+        File tempFile = createTemporaryZipFile();
+        try (
+            InputStream is = ZipFileTest.class.getResourceAsStream(resourceName);
+            FileOutputStream fos = new FileOutputStream(tempFile)) {
+
+            int read;
+            byte[] arr = new byte[1024];
+
+            while ((read = is.read(arr)) > 0) {
+                fos.write(arr, 0, read);
+            }
+            fos.flush();
+
+            Charset cp1251 = Charset.forName("cp1251");
+            try (ZipFile zipFile = new ZipFile(tempFile, cp1251)) {
+                ZipEntry zipEntry = zipFile.entries().nextElement();
+
+                assertEquals("имя файла", zipEntry.getName());
+            }
+
+            try (ZipFile zipFile = new ZipFile(tempFile.getAbsolutePath(), cp1251)) {
+                ZipEntry zipEntry = zipFile.entries().nextElement();
+
+                assertEquals("имя файла", zipEntry.getName());
+            }
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/javax/crypto/MacTest.java b/luni/src/test/java/libcore/javax/crypto/MacTest.java
index bd2ad39..fb62018 100644
--- a/luni/src/test/java/libcore/javax/crypto/MacTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/MacTest.java
@@ -113,7 +113,7 @@
     };
 
     /**
-     * Test that BC has the same results as the SunJCA provider for
+     * Test that default PBEWITHHMACSHA implementation has the same results as the SunJCA provider.
      */
     public void test_PBEWITHHMACSHA_Variants() throws Exception {
         byte[] plaintext = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
@@ -126,14 +126,14 @@
         for (int shaVariantIndex = 0; shaVariantIndex < shaVariants.length; shaVariantIndex++) {
             int shaVariant = shaVariants[shaVariantIndex];
             SecretKeyFactory secretKeyFactory =
-                    SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA" + shaVariant, "BC");
+                    SecretKeyFactory.getInstance("PBKDF2WITHHMACSHA" + shaVariant);
             PBEKeySpec pbeKeySpec = new PBEKeySpec(password,
                     salt,
                     iterationCount,
                     // Key depending on block size!
                     (shaVariant < 384) ? 64 : 128);
             SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
-            Mac mac = Mac.getInstance("PBEWITHHMACSHA" + shaVariant, "BC");
+            Mac mac = Mac.getInstance("PBEWITHHMACSHA" + shaVariant);
             mac.init(secretKey);
             byte[] bcResult = mac.doFinal(plaintext);
             assertEquals(
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLPermissionTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLPermissionTest.java
new file mode 100644
index 0000000..d0217b4
--- /dev/null
+++ b/luni/src/test/java/libcore/javax/net/ssl/SSLPermissionTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.javax.net.ssl;
+
+import junit.framework.TestCase;
+
+import javax.net.ssl.SSLPermission;
+
+public final class SSLPermissionTest extends TestCase {
+
+    public void testSslPermissions_constructor_LString() {
+        SSLPermission permission = new SSLPermission("name");
+
+        assertEquals("", permission.getName());
+        assertEquals("", permission.getActions());
+    }
+
+    public void testSslPermission_constructor_LString_LString() {
+        SSLPermission permission = new SSLPermission("name", "actions");
+
+        assertEquals("", permission.getName());
+        assertEquals("", permission.getActions());
+    }
+}
diff --git a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
index 226952f..757e14b 100644
--- a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
+++ b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
@@ -65,41 +65,17 @@
      * Confirm DRLCertFactory uses a non-hex format for T61String encoding: http://b/2102191
      */
     public void testGetName() throws Exception {
-        CertificateFactory certFactBC = CertificateFactory.getInstance("X.509", "BC");
-        CertificateFactory certFactOpenSSL = CertificateFactory.getInstance("X.509",
-                "AndroidOpenSSL");
-
-        X509Certificate certBC = (X509Certificate)
-                certFactBC.generateCertificate(new ByteArrayInputStream(T61STRING_CERT));
-        X509Certificate certOpenSSL = (X509Certificate)
-                certFactOpenSSL.generateCertificate(new ByteArrayInputStream(T61STRING_CERT));
-
-        assertEquals(certBC, certOpenSSL);
-
-        assertEquals(certBC.getSubjectX500Principal(), certBC.getSubjectX500Principal());
-        assertEquals(certOpenSSL.getIssuerX500Principal(), certOpenSSL.getIssuerX500Principal());
-
-        assertEquals(certBC.getSubjectX500Principal(), certOpenSSL.getSubjectX500Principal());
-        assertEquals(certBC.getIssuerX500Principal(), certOpenSSL.getIssuerX500Principal());
-
-        String[] formats = {
-            X500Principal.CANONICAL,
-            X500Principal.RFC1779,
-            X500Principal.RFC2253
-        };
-        for (String format : formats) {
-            assertEquals(certBC.getSubjectX500Principal().getName(format),
-                         certOpenSSL.getSubjectX500Principal().getName(format));
-            assertEquals(certBC.getIssuerX500Principal().getName(format),
-                         certOpenSSL.getIssuerX500Principal().getName(format));
-        }
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate)
+                certFact.generateCertificate(new ByteArrayInputStream(T61STRING_CERT));
+        
         String expected = ""
                 + "cn=entrust.net certification authority (2048),"
                 + "ou=(c) 1999 entrust.net limited,"
                 + "ou=www.entrust.net/cps_2048 incorp. by ref. (limits liab.),"
                 + "o=entrust.net";
         assertEquals(expected,
-                     certBC.getSubjectX500Principal().getName(X500Principal.CANONICAL));
+            cert.getSubjectX500Principal().getName(X500Principal.CANONICAL));
 
     }
 
diff --git a/luni/src/test/java/libcore/libcore/content/type/MimeMapTest.java b/luni/src/test/java/libcore/libcore/content/type/MimeMapTest.java
index 764bad2..405aa84 100644
--- a/luni/src/test/java/libcore/libcore/content/type/MimeMapTest.java
+++ b/luni/src/test/java/libcore/libcore/content/type/MimeMapTest.java
@@ -70,7 +70,7 @@
 
     @Test public void caseNormalization_key() {
         mimeMap = MimeMap.builder()
-                .put("application/msWord", Arrays.asList("Doc"))
+                .addMimeMapping("application/msWord", Arrays.asList("Doc"))
                 .build();
         assertEquals("application/msword", mimeMap.guessMimeTypeFromExtension("dOc"));
         assertEquals("doc", mimeMap.guessExtensionFromMimeType("appliCATion/mSWOrd"));
@@ -93,7 +93,7 @@
 
         // Known keys for a custom map
         mimeMap = MimeMap.builder()
-                .put("application/msWord", Arrays.asList("Doc"))
+                .addMimeMapping("application/msWord", Arrays.asList("Doc"))
                 .build();
         assertEquals("doc", mimeMap.guessExtensionFromMimeType("Application/mSWord"));
         assertEquals("application/msword", mimeMap.guessMimeTypeFromExtension("DoC"));
@@ -105,7 +105,7 @@
 
     @Test public void unmapped() {
         mimeMap = MimeMap.builder()
-                .put("mime/test", Arrays.asList("test", "tst"))
+                .addMimeMapping("mime/test", Arrays.asList("test", "tst"))
                 .build();
         assertNull(mimeMap.guessExtensionFromMimeType("mime/unknown"));
         assertFalse(mimeMap.hasMimeType("mime/unknown"));
@@ -122,7 +122,7 @@
         MimeMap originalDefault = MimeMap.getDefault();
         try {
             // Constructs a new instance every time it is called
-            MimeMap.setDefaultSupplier(() -> MimeMap.builder().put("mime/sup", "sup").build());
+            MimeMap.setDefaultSupplier(() -> MimeMap.builder().addMimeMapping("mime/sup", "sup").build());
             // Same instance is returned both times
             assertSame(MimeMap.getDefault(), MimeMap.getDefault());
             // Check that the supplier is in effect
@@ -165,7 +165,7 @@
                 mimeMap);
 
         mimeMap = mimeMap.buildUpon()
-                .put("text/plain", "txt")
+                .addMimeMapping("text/plain", "txt")
                 .build();
         assertMap(
                 makeMap("text/plain", "txt"),
@@ -173,7 +173,7 @@
                 mimeMap);
 
         mimeMap = mimeMap.buildUpon()
-                .put("audio/mpeg", Arrays.asList("mp2", "mp3"))
+                .addMimeMapping("audio/mpeg", Arrays.asList("mp2", "mp3"))
                 .build();
         assertMap(
                 makeMap("audio/mpeg", "mp2",
@@ -184,7 +184,7 @@
                 mimeMap);
 
         mimeMap = mimeMap.buildUpon()
-                .put("text/plain", "text")
+                .addMimeMapping("text/plain", "text")
                 .build();
         assertMap(
                 makeMap("audio/mpeg", "mp2",
@@ -198,12 +198,12 @@
 
     @Test public void put() {
         MimeMap a = MimeMap.builder()
-                .put("text/plain", Arrays.asList("txt", "text"))
-                .put("application/msword", "doc")
+                .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
+                .addMimeMapping("application/msword", "doc")
                 .build();
         MimeMap b = MimeMap.builder()
-                .put("text/plain", Arrays.asList("txt", "text"))
-                .put("application/msword", "doc")
+                .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
+                .addMimeMapping("application/msword", "doc")
                 .build();
         assertEqualsButNotSame(a, b);
         assertEqualsButNotSame(a, a.buildUpon().build());
@@ -219,7 +219,7 @@
 
     @Test public void put_noExtensions() {
         checkPut_noExtensions(emptyMap);
-        checkPut_noExtensions(MimeMap.builder().put("text/plain", "txt").build());
+        checkPut_noExtensions(MimeMap.builder().addMimeMapping("text/plain", "txt").build());
         checkPut_noExtensions(mimeMap);
     }
 
@@ -228,7 +228,7 @@
      */
     private static void checkPut_noExtensions(MimeMap baseMap) {
         MimeMap mimeMap = baseMap.buildUpon()
-                .put("mime/type", Collections.emptyList())
+                .addMimeMapping("mime/type", Collections.emptyList())
                 .build();
         assertEquals(baseMap, mimeMap);
     }
@@ -249,11 +249,11 @@
     }
 
     @Test public void put_String_String_nullOrEmpty() {
-        assertThrowsNpe(() -> MimeMap.builder().put(null, "ext"));
-        assertThrowsIae(() -> MimeMap.builder().put("", "ext"));
+        assertThrowsNpe(() -> MimeMap.builder().addMimeMapping(null, "ext"));
+        assertThrowsIae(() -> MimeMap.builder().addMimeMapping("", "ext"));
 
-        assertThrowsNpe(() -> MimeMap.builder().put("mime/type", (String) null));
-        assertThrowsIae(() -> MimeMap.builder().put("mime/type", ""));
+        assertThrowsNpe(() -> MimeMap.builder().addMimeMapping("mime/type", (String) null));
+        assertThrowsIae(() -> MimeMap.builder().addMimeMapping("mime/type", ""));
     }
 
     /**
@@ -262,8 +262,8 @@
     @Test public void putIfAbsent() {
         // Starting from an empty mapping, add a bunch more, some with and some without '?'.
         mimeMap = MimeMap.builder()
-                .put("?text/plain", "?txt")
-                .put("audio/mpeg", Arrays.asList("mpga", "mpega", "?mp2", "mp3"))
+                .addMimeMapping("?text/plain", "?txt")
+                .addMimeMapping("audio/mpeg", Arrays.asList("mpga", "mpega", "?mp2", "mp3"))
                 .build();
         assertEquals("txt", mimeMap.guessExtensionFromMimeType("text/plain"));
         assertEquals("text/plain", mimeMap.guessMimeTypeFromExtension("txt"));
@@ -273,14 +273,14 @@
 
         // Override a ext -> MIME mapping without overriding the MIME -> ext mapping.
         mimeMap = mimeMap.buildUpon()
-                .put("?audio/mpeg", "m4a")
+                .addMimeMapping("?audio/mpeg", "m4a")
                 .build();
         assertEquals("mpga", mimeMap.guessExtensionFromMimeType("audio/mpeg"));
         assertEquals("audio/mpeg", mimeMap.guessMimeTypeFromExtension("m4a"));
 
         // Override a MIME -> ext mapping without overriding the ext -> MIME mapping.
         mimeMap = mimeMap.buildUpon()
-                .put("audio/mpeg", "?txt")
+                .addMimeMapping("audio/mpeg", "?txt")
                 .build();
         assertEquals("txt", mimeMap.guessExtensionFromMimeType("audio/mpeg"));
         assertEquals("text/plain", mimeMap.guessMimeTypeFromExtension("txt"));
@@ -307,10 +307,10 @@
     @Test public void extensions() {
         assertEquals(Collections.emptySet(), emptyMap.extensions());
         mimeMap = MimeMap.builder()
-                .put("text/plain", Arrays.asList("txt", "text"))
-                .put("audi/mpeg", "m4a")
-                .put("application/msword", "doc")
-                .put("text/plain", "tx")
+                .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
+                .addMimeMapping("audi/mpeg", "m4a")
+                .addMimeMapping("application/msword", "doc")
+                .addMimeMapping("text/plain", "tx")
                 .build();
         Set<String> extensions = new HashSet<>(Arrays.asList(
                 "txt", "text", "m4a", "doc", "tx"));
@@ -326,10 +326,10 @@
     @Test public void mimeTypes() {
         assertEquals(Collections.emptySet(), emptyMap.mimeTypes());
         mimeMap = MimeMap.builder()
-                .put("text/plain", Arrays.asList("txt", "text"))
-                .put("audio/mpeg", "m4a")
-                .put("application/msword", "doc")
-                .put("text/plain", "tx")
+                .addMimeMapping("text/plain", Arrays.asList("txt", "text"))
+                .addMimeMapping("audio/mpeg", "m4a")
+                .addMimeMapping("application/msword", "doc")
+                .addMimeMapping("text/plain", "tx")
                 .build();
         Set<String> mimeTypes = new HashSet<>(Arrays.asList(
                 "text/plain",
@@ -362,17 +362,17 @@
         assertPutThrowsIae("invalidmime", "ext");
 
         // During lookups, wrong arguments return null rather than throwing.
-        mimeMap = MimeMap.builder().put("mime/type", "ext").build();
+        mimeMap = MimeMap.builder().addMimeMapping("mime/type", "ext").build();
         assertNull(mimeMap.guessExtensionFromMimeType("ext")); // ext is no mime type
         assertNull(mimeMap.guessMimeTypeFromExtension("mime/type")); // mime/type is no extension
     }
 
     private static void assertPutThrowsNpe(String mime, String... exts) {
-        assertThrowsNpe(() -> MimeMap.builder().put(mime, Arrays.asList(exts)));
+        assertThrowsNpe(() -> MimeMap.builder().addMimeMapping(mime, Arrays.asList(exts)));
     }
 
     private static void assertPutThrowsIae(final String mime, final String... exts) {
-        assertThrowsIae(() -> MimeMap.builder().put(mime, Arrays.asList(exts)));
+        assertThrowsIae(() -> MimeMap.builder().addMimeMapping(mime, Arrays.asList(exts)));
     }
 
     private static void assertThrowsNpe(Runnable runnable) {
@@ -393,8 +393,8 @@
 
     @Test public void hashCodeValue() {
         assertEquals(0, emptyMap.hashCode());
-        MimeMap a = MimeMap.builder().put("mime/test", "test").build();
-        MimeMap b = a.buildUpon().put("foo/bar", "baz").build();
+        MimeMap a = MimeMap.builder().addMimeMapping("mime/test", "test").build();
+        MimeMap b = a.buildUpon().addMimeMapping("foo/bar", "baz").build();
         assertTrue(0 != a.hashCode());
         assertTrue((a.hashCode() != b.hashCode()));
     }
@@ -434,13 +434,13 @@
             String ext = entry.getKey();
             String mime = entry.getValue();
             assertEquals(ext + ": " + mimeMap, mime, mimeMap.guessMimeTypeFromExtension(ext));
-            expectedBuilder.put("?" + mime, ext);
+            expectedBuilder.addMimeMapping("?" + mime, ext);
         }
         for (Map.Entry<String, String> entry : expectedMimeToExt.entrySet()) {
             String mime = entry.getKey();
             String ext = entry.getValue();
             assertEquals(mime + ": "  + mimeMap, ext, mimeMap.guessExtensionFromMimeType(mime));
-            expectedBuilder.put(mime, "?" + ext);
+            expectedBuilder.addMimeMapping(mime, "?" + ext);
         }
         // Check that there are no unexpected additional mappings.
         assertEqualsButNotSame(expectedBuilder.build(), mimeMap);
diff --git a/luni/src/test/java/libcore/libcore/icu/AlphabeticIndexTest.java b/luni/src/test/java/libcore/libcore/icu/AlphabeticIndexTest.java
deleted file mode 100644
index c659f9d..0000000
--- a/luni/src/test/java/libcore/libcore/icu/AlphabeticIndexTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.libcore.icu;
-
-import org.junit.Test;
-import android.icu.text.AlphabeticIndex;
-import android.icu.text.UnicodeSet;
-import android.icu.util.LocaleData;
-import android.icu.util.ULocale;
-import java.util.Locale;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertNotNull;
-
-/**
- * Test for {@link android.icu.text.AlphabeticIndex}
- */
-public class AlphabeticIndexTest {
-
-    @Test
-    public void test_english() {
-        verifyIndex(Locale.ENGLISH);
-    }
-
-    // http://b/64953401
-    @Test
-    public void testBucketCount_withNoIndexCharacters() {
-        Locale localeWithNoIndexCharacters = null;
-        // Search in the available languages only instead of all possible locales
-        // in order to speed-up the test.
-        for (String language : Locale.getISOLanguages()) {
-            Locale locale = Locale.forLanguageTag(language);
-            UnicodeSet exemplarSet = LocaleData.getExemplarSet(
-                ULocale.forLocale(locale), 0, LocaleData.ES_INDEX);
-            if (exemplarSet == null || exemplarSet.isEmpty()) {
-                localeWithNoIndexCharacters = locale;
-                break;
-            }
-        }
-
-        assertNotNull("Can't find any language with no index characters",
-            localeWithNoIndexCharacters);
-
-        // If this assert fails it means that it can't find any language with no index character.
-        // If that's the case, please find expand the search space to find a locale that's missing
-        // that key where the logic in AlphabeticIndex.addIndexExemplars will generate buckets from
-        // alternate data.
-        verifyIndex(localeWithNoIndexCharacters);
-    }
-
-    private void verifyIndex(Locale locale) {
-        ULocale uLocale = ULocale.forLocale(locale);
-        AlphabeticIndex index = new AlphabeticIndex(uLocale);
-        LocaleData localeData = LocaleData.getInstance(uLocale);
-
-        // 0 = "default options", there is no constant for this.
-        UnicodeSet exemplarSet = localeData.getExemplarSet(0, LocaleData.ES_STANDARD);
-        for (String s : exemplarSet) {
-            index.addRecord(s, s);
-        }
-        assertTrue("Locale(" + locale + ") does not have enough buckets: " + index.getBucketLabels(),
-            index.getBucketCount() > 1);
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java
index 55af0e2..da41559 100644
--- a/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java
@@ -139,12 +139,12 @@
 
     assertEquals("19–22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, 0));
     assertEquals("19–22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("lun, 19 de ene. – jue, 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
     assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
     assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, 0));
     assertEquals("19 de ene. – 22 de abr. 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("lun, 19 de ene. – mié, 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
     assertEquals("enero–abril de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
     assertEquals("19 de ene. de 2009 – 9 de feb. de 2012", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
@@ -155,17 +155,17 @@
     // The same tests but for es_ES.
 
     assertEquals("19–22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
-    assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun., 19 ene. – jue., 22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("19–22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun, 19 ene – jue, 22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
     assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY));
 
     assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
-    assertEquals("19 ene. – 22 abr. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("lun., 19 ene. – mié., 22 abr. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
+    assertEquals("19 ene – 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("lun, 19 ene – mié, 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
     assertEquals("enero–abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
 
-    assertEquals("19 ene. 2009 – 9 feb. 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
-    assertEquals("ene. 2009 – feb. 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
+    assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
+    assertEquals("ene 2009 – feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
     assertEquals("19 de enero de 2009–9 de febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, 0));
     assertEquals("lunes, 19 de enero de 2009–jueves, 9 de febrero de 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_WEEKDAY));
   }
diff --git a/luni/src/test/java/libcore/libcore/icu/ICUCalendarTest.java b/luni/src/test/java/libcore/libcore/icu/ICUCalendarTest.java
deleted file mode 100644
index e01de4d..0000000
--- a/luni/src/test/java/libcore/libcore/icu/ICUCalendarTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 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.libcore.icu;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.icu.impl.ICUData;
-import android.icu.impl.ICUResourceBundle;
-import android.icu.util.Calendar;
-import android.icu.util.GregorianCalendar;
-import android.icu.util.ULocale;
-
-import org.junit.Test;
-
-/**
- * Test for Android patches in ICU's calendar.
- */
-public class ICUCalendarTest {
-
-    /**
-     * Ensures that Gregorian is the default Calendar for all Locales in Android. This is the
-     * historic behavior on Android; this test exists to avoid unintentional regressions.
-     * http://b/80294184
-     */
-    @Test
-    public void testAllDefaultCalendar_Gregorian() {
-        for (ULocale locale : ULocale.getAvailableLocales()) {
-            assertTrue("Default calendar should be Gregorian: " + locale,
-                    Calendar.getInstance(locale) instanceof GregorianCalendar);
-        }
-
-        for (ULocale locale : ULocale.getAvailableLocales()) {
-            // Classes, e.g. RelativeDateTimeFormatter, use a different property,
-            // i.e. calendar/default, for calendar type. However, there is no direct way to obtain
-            // the calendar type via the APIs. Verify the property value directly here.
-            ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
-                    ICUData.ICU_BASE_NAME, locale);
-            String calType = bundle.getStringWithFallback("calendar/default");
-            assertEquals("calendar/default should be Gregorian: " + locale, "gregorian", calType);
-        }
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/icu/ICULocaleDataTest.java b/luni/src/test/java/libcore/libcore/icu/ICULocaleDataTest.java
deleted file mode 100644
index 7f0fc6b..0000000
--- a/luni/src/test/java/libcore/libcore/icu/ICULocaleDataTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package libcore.libcore.icu;
-
-import android.icu.util.LocaleData;
-import android.icu.util.ULocale;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-
-/**
- * Test for {@link android.icu.util.LocaleData}. Don't confuse with {@link libcore.icu.LocaleData}.
- */
-public class ICULocaleDataTest {
-
-    @Test
-    public void testGetDelimiter() {
-        LocaleData usLocaleData = LocaleData.getInstance(ULocale.US);
-        assertEquals("“", usLocaleData.getDelimiter(LocaleData.QUOTATION_START));
-        assertEquals("”", usLocaleData.getDelimiter(LocaleData.QUOTATION_END));
-        assertEquals("‘", usLocaleData.getDelimiter(LocaleData.ALT_QUOTATION_START));
-        assertEquals("’", usLocaleData.getDelimiter(LocaleData.ALT_QUOTATION_END));
-        LocaleData italianLocaleData = LocaleData.getInstance(ULocale.ITALIAN);
-        assertEquals("«", italianLocaleData.getDelimiter(LocaleData.QUOTATION_START));
-        assertEquals("»", italianLocaleData.getDelimiter(LocaleData.QUOTATION_END));
-        assertEquals("“", italianLocaleData.getDelimiter(LocaleData.ALT_QUOTATION_START));
-        assertEquals("”", italianLocaleData.getDelimiter(LocaleData.ALT_QUOTATION_END));
-
-        for (ULocale locale : ULocale.getAvailableLocales()) {
-            LocaleData localeData = LocaleData.getInstance(locale);
-            for (int type = LocaleData.QUOTATION_START; type <= LocaleData.ALT_QUOTATION_END;
-                    type++) {
-                String delimiter = localeData.getDelimiter(type);
-                assertNotNull("Delimiter is null in this locale " + locale, delimiter);
-                assertFalse("Delimiter is empty in this locale " + locale, delimiter.isEmpty());
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/libcore/icu/ICUTest.java b/luni/src/test/java/libcore/libcore/icu/ICUTest.java
index 30e3081..a5577a5 100644
--- a/luni/src/test/java/libcore/libcore/icu/ICUTest.java
+++ b/luni/src/test/java/libcore/libcore/icu/ICUTest.java
@@ -44,6 +44,23 @@
     assertNotNull(ICU.getAvailableLocales()[0]);
   }
 
+  public void test_getCurrencyCode() {
+    assertEquals("USD", ICU.getCurrencyCode("US"));
+    assertEquals("CAD", ICU.getCurrencyCode("CA"));
+    assertEquals("HKD", ICU.getCurrencyCode("HK")); // a region
+
+    // Test invalid country codes
+    assertNull(ICU.getCurrencyCode("A"));
+    assertNull(ICU.getCurrencyCode(null));
+    assertNull(ICU.getCurrencyCode(""));
+    assertNull(ICU.getCurrencyCode("AA"));  // 2-charcter invalid country code
+    assertNull(ICU.getCurrencyCode("USA")); // 3-character country code
+    assertNull(ICU.getCurrencyCode("ZZZ"));
+    assertNull(ICU.getCurrencyCode("EURO"));
+    assertNull(ICU.getCurrencyCode("PREEURO"));
+    assertNull(ICU.getCurrencyCode("en_EURO"));
+  }
+
   public void test_getBestDateTimePattern() throws Exception {
     assertEquals("d MMMM", ICU.getBestDateTimePattern("MMMMd", new Locale("ca", "ES")));
     assertEquals("d 'de' MMMM", ICU.getBestDateTimePattern("MMMMd", new Locale("es", "ES")));
@@ -220,6 +237,32 @@
     assertTrue(c.compare("AF", "af") < 0);
   }
 
+  public void testTransformIcuDateTimePattern_forJavaTime() {
+    // Example patterns coming from locale my-MM
+    assertTransformIcuDateTimePattern("B H:mm", "H:mm");
+    assertTransformIcuDateTimePattern("B HH:mm:ss", "HH:mm:ss");
+    assertTransformIcuDateTimePattern("dd-MM-yy B HH:mm:ss", "dd-MM-yy HH:mm:ss");
+    assertTransformIcuDateTimePattern("y၊ MMM d B HH:mm:ss", "y၊ MMM d HH:mm:ss");
+
+    // Other examples
+    assertTransformIcuDateTimePattern("H:mm B", "H:mm");
+    assertTransformIcuDateTimePattern("H:mm b", "H:mm");
+    assertTransformIcuDateTimePattern("b H:mm", "H:mm");
+    assertTransformIcuDateTimePattern("B H:mm:ss, E", "H:mm:ss, E");
+
+    // Examples with no effect
+    assertTransformIcuDateTimePattern("hh:mm b", "hh:mm b"); // No change for 12-hour format
+    assertTransformIcuDateTimePattern("hh:mm B", "hh:mm B"); // No change for 12-hour format
+    assertTransformIcuDateTimePattern("B h:mm:ss, E", "B h:mm:ss, E");
+    // No change when no hour is specified
+    assertTransformIcuDateTimePattern("dd-MM-yy B", "dd-MM-yy B");
+  }
+
+  private static void assertTransformIcuDateTimePattern(String input, String expectedOutput) {
+    String pattern = ICU.transformIcuDateTimePattern_forJavaTime(input);
+    assertEquals("input:" + input, expectedOutput, pattern);
+  }
+
   public void testSetDefault() {
       String current = ICU.getDefaultLocale();
 
diff --git a/luni/src/test/java/libcore/libcore/icu/LocaleDataTest.java b/luni/src/test/java/libcore/libcore/icu/LocaleDataTest.java
index eace233..b524483 100644
--- a/luni/src/test/java/libcore/libcore/icu/LocaleDataTest.java
+++ b/luni/src/test/java/libcore/libcore/icu/LocaleDataTest.java
@@ -23,6 +23,7 @@
 
 import android.icu.text.DateTimePatternGenerator;
 
+import java.text.DateFormat;
 import java.text.DateFormatSymbols;
 import java.text.DecimalFormatSymbols;
 import java.text.SimpleDateFormat;
@@ -42,25 +43,14 @@
 
 @RunWith(JUnit4.class)
 public class LocaleDataTest {
-  
+
   @Rule
   public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance();
 
   @Test
-  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.format("%20s %s %s %s\n", l, d.yesterday, d.today, d.tomorrow);
-      // System.err.format("%20s %10s %10s\n", l, d.timeFormat_hm, d.timeFormat_Hm);
-    }
-  }
-
-  @Test
   public void test_en_US() throws Exception {
     LocaleData l = LocaleData.get(Locale.US);
     assertEquals("AM", l.amPm[0]);
-    assertEquals("a", l.narrowAm);
 
     assertEquals("BC", l.eras[0]);
 
@@ -80,18 +70,6 @@
     assertEquals("Sun", l.shortStandAloneWeekdayNames[1]);
     assertEquals("S", l.tinyStandAloneWeekdayNames[1]);
 
-    assertEquals("Yesterday", l.yesterday);
-    assertEquals("Today", l.today);
-    assertEquals("Tomorrow", l.tomorrow);
-  }
-
-  @Test
-  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);
   }
 
   @Test
@@ -108,16 +86,6 @@
   }
 
   @Test
-  public void test_ko_KR() throws Exception {
-    LocaleData l = LocaleData.get(new Locale("ko", "KR"));
-
-    // Ensure the fix for http://b/14493853 doesn't mangle Hangul.
-    assertEquals("어제", l.yesterday);
-    assertEquals("오늘", l.today);
-    assertEquals("내일", l.tomorrow);
-  }
-
-  @Test
   public void test_ru_RU() throws Exception {
     LocaleData l = LocaleData.get(new Locale("ru", "RU"));
 
@@ -157,14 +125,23 @@
 
   // http://b/7924970
   @Test
-  public void testTimeFormat12And24() throws Exception {
-    LocaleData en_US = LocaleData.get(Locale.US);
-    assertEquals("h:mm a", en_US.timeFormat_hm);
-    assertEquals("HH:mm", en_US.timeFormat_Hm);
+  public void testTimeFormat12And24() {
+    Boolean originalSetting = DateFormat.is24Hour;
+    try {
+      LocaleData en_US = LocaleData.get(Locale.US);
+      DateFormat.is24Hour = false;
+      assertEquals("h:mm a", en_US.getTimeFormat(DateFormat.SHORT));
+      DateFormat.is24Hour = true;
+      assertEquals("HH:mm", en_US.getTimeFormat(DateFormat.SHORT));
 
-    LocaleData ja_JP = LocaleData.get(Locale.JAPAN);
-    assertEquals("aK:mm", ja_JP.timeFormat_hm);
-    assertEquals("H:mm", ja_JP.timeFormat_Hm);
+      LocaleData ja_JP = LocaleData.get(Locale.JAPAN);
+      DateFormat.is24Hour = false;
+      assertEquals("aK:mm", ja_JP.getTimeFormat(DateFormat.SHORT));
+      DateFormat.is24Hour = true;
+      assertEquals("H:mm", ja_JP.getTimeFormat(DateFormat.SHORT));
+    } finally {
+      DateFormat.is24Hour = originalSetting;
+    }
   }
 
   // http://b/26397197
@@ -264,11 +241,6 @@
     assertEquals(localeData.infinity, icuDecimalFormatSymbols.getInfinity());
     assertEquals(decimalFormatSymbols.getInfinity(), icuDecimalFormatSymbols.getInfinity());
 
-    assertEquals(localeData.timeFormat_Hm, dtpg.getBestPattern("Hm"));
-    assertEquals(localeData.timeFormat_hm, dtpg.getBestPattern("hm"));
-    assertEquals(localeData.timeFormat_Hms, dtpg.getBestPattern("Hms"));
-    assertEquals(localeData.timeFormat_hms, dtpg.getBestPattern("hms"));
-
     // Explicitly test Calendar and DateFormatSymbols here because they are known to
     // cache some part of LocaleData.
     assertEquals(calendar.getFirstDayOfWeek(), icuCalendar.getFirstDayOfWeek());
diff --git a/luni/src/test/java/libcore/libcore/icu/RelativeDateTimeFormatterTest.java b/luni/src/test/java/libcore/libcore/icu/RelativeDateTimeFormatterTest.java
deleted file mode 100644
index 651b9f9..0000000
--- a/luni/src/test/java/libcore/libcore/icu/RelativeDateTimeFormatterTest.java
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.libcore.icu;
-
-import java.util.Calendar;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import libcore.icu.RelativeDateTimeFormatter;
-
-import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_ALL;
-import static libcore.icu.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
-import static libcore.icu.DateUtilsBridge.FORMAT_NO_YEAR;
-import static libcore.icu.DateUtilsBridge.FORMAT_NUMERIC_DATE;
-import static libcore.icu.DateUtilsBridge.FORMAT_SHOW_YEAR;
-import static libcore.icu.RelativeDateTimeFormatter.DAY_IN_MILLIS;
-import static libcore.icu.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
-import static libcore.icu.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
-import static libcore.icu.RelativeDateTimeFormatter.SECOND_IN_MILLIS;
-import static libcore.icu.RelativeDateTimeFormatter.WEEK_IN_MILLIS;
-import static libcore.icu.RelativeDateTimeFormatter.YEAR_IN_MILLIS;
-import static libcore.icu.RelativeDateTimeFormatter.getRelativeDateTimeString;
-import static libcore.icu.RelativeDateTimeFormatter.getRelativeTimeSpanString;
-
-public class RelativeDateTimeFormatterTest extends junit.framework.TestCase {
-
-  // Tests adopted from CTS tests for DateUtils.getRelativeTimeSpanString.
-  public void test_getRelativeTimeSpanStringCTS() throws Exception {
-    Locale en_US = new Locale("en", "US");
-    TimeZone tz = TimeZone.getTimeZone("GMT");
-    Calendar cal = Calendar.getInstance(tz, en_US);
-    // Feb 5, 2015 at 10:50 GMT
-    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
-    final long baseTime = cal.getTimeInMillis();
-
-    assertEquals("0 minutes ago",
-                 getRelativeTimeSpanString(en_US, tz, baseTime - SECOND_IN_MILLIS, baseTime,
-                                           MINUTE_IN_MILLIS, 0));
-    assertEquals("In 0 minutes",
-                 getRelativeTimeSpanString(en_US, tz, baseTime + SECOND_IN_MILLIS, baseTime,
-                                           MINUTE_IN_MILLIS, 0));
-
-    assertEquals("1 minute ago",
-                 getRelativeTimeSpanString(en_US, tz, 0, MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 0));
-    assertEquals("In 1 minute",
-                 getRelativeTimeSpanString(en_US, tz, MINUTE_IN_MILLIS, 0, MINUTE_IN_MILLIS, 0));
-
-    assertEquals("42 minutes ago",
-      getRelativeTimeSpanString(en_US, tz, baseTime - 42 * MINUTE_IN_MILLIS, baseTime,
-                                MINUTE_IN_MILLIS, 0));
-    assertEquals("In 42 minutes",
-      getRelativeTimeSpanString(en_US, tz, baseTime + 42 * MINUTE_IN_MILLIS, baseTime,
-                                MINUTE_IN_MILLIS, 0));
-
-    final long TWO_HOURS_IN_MS = 2 * HOUR_IN_MILLIS;
-    assertEquals("2 hours ago",
-                 getRelativeTimeSpanString(en_US, tz, baseTime - TWO_HOURS_IN_MS, baseTime,
-                                           MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
-    assertEquals("In 2 hours",
-                 getRelativeTimeSpanString(en_US, tz, baseTime + TWO_HOURS_IN_MS, baseTime,
-                                           MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
-
-    assertEquals("In 42 min.",
-                 getRelativeTimeSpanString(en_US, tz, baseTime + (42 * MINUTE_IN_MILLIS), baseTime,
-                                           MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-
-    assertEquals("Tomorrow",
-                 getRelativeTimeSpanString(en_US, tz, DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
-    assertEquals("In 2 days",
-                 getRelativeTimeSpanString(en_US, tz, 2 * DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
-    assertEquals("Yesterday",
-                 getRelativeTimeSpanString(en_US, tz, 0, DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
-    assertEquals("2 days ago",
-                 getRelativeTimeSpanString(en_US, tz, 0, 2 * DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
-
-    final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
-    assertEquals("5 days ago",
-                 getRelativeTimeSpanString(en_US, tz, baseTime - DAY_DURATION, baseTime,
-                                           DAY_IN_MILLIS, 0));
-  }
-
-  private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, int flags,
-                                                     String expectedInPast,
-                                                     String expectedInFuture) throws Exception {
-    Locale en_US = new Locale("en", "US");
-    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-    Calendar cal = Calendar.getInstance(tz, en_US);
-    // Feb 5, 2015 at 10:50 PST
-    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
-    final long base = cal.getTimeInMillis();
-
-    assertEquals(expectedInPast,
-                 getRelativeTimeSpanString(en_US, tz, base - delta, base, minResolution, flags));
-    assertEquals(expectedInFuture,
-                 getRelativeTimeSpanString(en_US, tz, base + delta, base, minResolution, flags));
-  }
-
-  private void test_getRelativeTimeSpanString_helper(long delta, long minResolution,
-                                                     String expectedInPast,
-                                                     String expectedInFuture) throws Exception {
-    test_getRelativeTimeSpanString_helper(delta, minResolution, 0, expectedInPast, expectedInFuture);
-  }
-
-  public void test_getRelativeTimeSpanString() throws Exception {
-
-    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, "0 seconds ago", "0 seconds ago");
-    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
-    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago", "In 1 minute");
-    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, "5 days ago", "In 5 days");
-
-    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "0 seconds ago",
-                                          "0 seconds ago");
-    test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 second ago",
-                                          "In 1 second");
-    test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "2 seconds ago",
-                                          "In 2 seconds");
-    test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "25 seconds ago",
-                                          "In 25 seconds");
-    test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 minute ago",
-                                          "In 1 minute");
-    test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, "1 hour ago",
-                                          "In 1 hour");
-
-    test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
-                                          "0 minutes ago");
-    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
-                                          "In 1 minute");
-    test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "2 minutes ago",
-                                          "In 2 minutes");
-    test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "25 minutes ago",
-                                          "In 25 minutes");
-    test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 hour ago",
-                                          "In 1 hour");
-    test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "12 hours ago",
-                                          "In 12 hours");
-
-    test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
-                                          "0 hours ago");
-    test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
-                                          "In 1 hour");
-    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "2 hours ago",
-                                          "In 2 hours");
-    test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "5 hours ago",
-                                          "In 5 hours");
-    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "20 hours ago",
-                                          "In 20 hours");
-
-    test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
-    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
-                                          "Tomorrow");
-    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
-                                          "Tomorrow");
-    test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
-                                          "In 2 days");
-    test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, "January 11",
-                                          "March 2");
-
-    test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
-                                          "0 weeks ago");
-    test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
-                                          "In 1 week");
-    test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "2 weeks ago",
-                                          "In 2 weeks");
-    test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "25 weeks ago",
-                                          "In 25 weeks");
-
-    // duration >= minResolution
-    test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, "30 seconds ago",
-                                          "In 30 seconds");
-    test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
-                                          "30 minutes ago", "In 30 minutes");
-    test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, "Yesterday",
-                                          "Tomorrow");
-    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, "5 days ago",
-                                          "In 5 days");
-    test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, "July 10, 2014",
-                                          "September 3");
-    test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS,
-                                          "February 6, 2010", "February 4, 2020");
-
-    test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "1 minute ago",
-                                          "In 1 minute");
-    test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS,
-                                          "1 minute ago", "In 1 minute");
-    test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
-                                          "In 1 hour");
-    test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, "1 hour ago",
-                                          "In 1 hour");
-    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
-    test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
-                                          "Today");
-    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
-                                          "Tomorrow");
-    test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
-                                          "In 2 days");
-    test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
-                                          "In 2 days");
-    test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
-                                          "In 1 week");
-    test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, "1 week ago",
-                                          "In 1 week");
-
-    // duration < minResolution
-    test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, "0 minutes ago",
-                                          "In 0 minutes");
-    test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
-                                          "In 0 hours");
-    test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, "0 hours ago",
-                                          "In 0 hours");
-    test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, "Yesterday",
-                                          "Tomorrow");
-    test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
-                                          "In 0 weeks");
-    test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, "0 weeks ago",
-                                          "In 0 weeks");
-  }
-
-  public void test_getRelativeTimeSpanStringAbbrev() throws Exception {
-    int flags = FORMAT_ABBREV_RELATIVE;
-
-    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, flags, "0 sec. ago",
-                                          "0 sec. ago");
-    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, flags, "1 min. ago",
-                                          "In 1 min.");
-    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, flags, "5 days ago", "In 5 days");
-
-    test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
-                                          "0 sec. ago", "0 sec. ago");
-    test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
-                                          "1 sec. ago", "In 1 sec.");
-    test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
-                                          "2 sec. ago", "In 2 sec.");
-    test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
-                                          "25 sec. ago", "In 25 sec.");
-    test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
-                                          "1 min. ago", "In 1 min.");
-    test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
-                                          "1 hr. ago", "In 1 hr.");
-
-    test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "0 min. ago", "0 min. ago");
-    test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "1 min. ago", "In 1 min.");
-    test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "2 min. ago", "In 2 min.");
-    test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "25 min. ago", "In 25 min.");
-    test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "1 hr. ago", "In 1 hr.");
-    test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "12 hr. ago", "In 12 hr.");
-
-    test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
-                                          "0 hr. ago", "0 hr. ago");
-    test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
-                                          "1 hr. ago", "In 1 hr.");
-    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
-                                          "2 hr. ago", "In 2 hr.");
-    test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
-                                          "5 hr. ago", "In 5 hr.");
-    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
-                                          "20 hr. ago", "In 20 hr.");
-
-    test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
-                                          "Today");
-    test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "Yesterday", "Tomorrow");
-    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "Yesterday", "Tomorrow");
-    test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "2 days ago", "In 2 days");
-    test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "January 11", "March 2");
-
-    test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
-                                          "0 wk. ago", "0 wk. ago");
-    test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
-                                          "1 wk. ago", "In 1 wk.");
-    test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
-                                          "2 wk. ago", "In 2 wk.");
-    test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
-                                          "25 wk. ago", "In 25 wk.");
-
-    // duration >= minResolution
-    test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, flags, "30 sec. ago",
-                                          "In 30 sec.");
-    test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "30 min. ago", "In 30 min.");
-    test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "Yesterday", "Tomorrow");
-    test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "5 days ago", "In 5 days");
-    test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "July 10, 2014", "September 3");
-    test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "February 6, 2010", "February 4, 2020");
-
-    test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "1 min. ago", "In 1 min.");
-    test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, flags,
-                                          "1 min. ago", "In 1 min.");
-    test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
-                                          "1 hr. ago", "In 1 hr.");
-    test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
-                                          "1 hr. ago", "In 1 hr.");
-    test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
-                                          "Today");
-    test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "Yesterday", "Today");
-    test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "Yesterday", "Tomorrow");
-    test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "2 days ago", "In 2 days");
-    test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
-                                          "2 days ago", "In 2 days");
-    test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, flags,
-                                          "1 wk. ago", "In 1 wk.");
-    test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
-                                          "1 wk. ago", "In 1 wk.");
-
-    // duration < minResolution
-    test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
-                                          "0 min. ago", "In 0 min.");
-    test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
-                                          "0 hr. ago", "In 0 hr.");
-    test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
-                                          "0 hr. ago", "In 0 hr.");
-    test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, flags,
-                                          "Yesterday", "Tomorrow");
-    test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, flags,
-                                          "0 wk. ago", "In 0 wk.");
-    test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
-                                          "0 wk. ago", "In 0 wk.");
-
-  }
-
-  public void test_getRelativeTimeSpanStringGerman() throws Exception {
-    // Bug: 19744876
-    // We need to specify the timezone and the time explicitly. Otherwise it
-    // may not always give a correct answer of "tomorrow" by using
-    // (now + DAY_IN_MILLIS).
-    Locale de_DE = new Locale("de", "DE");
-    TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
-    Calendar cal = Calendar.getInstance(tz, de_DE);
-    // Feb 5, 2015 at 10:50 CET
-    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
-    final long now = cal.getTimeInMillis();
-
-    // 42 minutes ago
-    assertEquals("Vor 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
-        now - 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
-    // In 42 minutes
-    assertEquals("In 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
-        now + 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
-    // Yesterday
-    assertEquals("Gestern", getRelativeTimeSpanString(de_DE, tz,
-        now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-    // The day before yesterday
-    assertEquals("Vorgestern", getRelativeTimeSpanString(de_DE, tz,
-        now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-    // Tomorrow
-    assertEquals("Morgen", getRelativeTimeSpanString(de_DE, tz,
-        now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-    // The day after tomorrow
-    assertEquals("Übermorgen", getRelativeTimeSpanString(de_DE, tz,
-        now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-  }
-
-  public void test_getRelativeTimeSpanStringFrench() throws Exception {
-    Locale fr_FR = new Locale("fr", "FR");
-    TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
-    Calendar cal = Calendar.getInstance(tz, fr_FR);
-    // Feb 5, 2015 at 10:50 CET
-    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
-    final long now = cal.getTimeInMillis();
-
-    // 42 minutes ago
-    assertEquals("Il y a 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
-        now - (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
-    // In 42 minutes
-    assertEquals("Dans 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
-        now + (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
-    // Yesterday
-    assertEquals("Hier", getRelativeTimeSpanString(fr_FR, tz,
-        now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-    // The day before yesterday
-    assertEquals("Avant-hier", getRelativeTimeSpanString(fr_FR, tz,
-        now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-    // Tomorrow
-    assertEquals("Demain", getRelativeTimeSpanString(fr_FR, tz,
-        now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-    // The day after tomorrow
-    assertEquals("Après-demain", getRelativeTimeSpanString(fr_FR, tz,
-        now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
-  }
-
-  // Tests adopted from CTS tests for DateUtils.getRelativeDateTimeString.
-  public void test_getRelativeDateTimeStringCTS() throws Exception {
-    Locale en_US = Locale.getDefault();
-    TimeZone tz = TimeZone.getDefault();
-    final long baseTime = System.currentTimeMillis();
-
-    final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
-    assertNotNull(getRelativeDateTimeString(en_US, tz, baseTime - DAY_DURATION, baseTime,
-                                            MINUTE_IN_MILLIS, DAY_IN_MILLIS,
-                                            FORMAT_NUMERIC_DATE));
-  }
-
-  public void test_getRelativeDateTimeString() throws Exception {
-    Locale en_US = new Locale("en", "US");
-    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-    Calendar cal = Calendar.getInstance(tz, en_US);
-    // Feb 5, 2015 at 10:50 PST
-    cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
-    final long base = cal.getTimeInMillis();
-
-    assertEquals("5 seconds ago, 10:49 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
-                                           MINUTE_IN_MILLIS, 0));
-    assertEquals("5 min. ago, 10:45 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
-                                           HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-    assertEquals("0 hr. ago, 10:45 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base,
-                                           HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-    assertEquals("5 hours ago, 5:50 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base,
-                                           HOUR_IN_MILLIS, DAY_IN_MILLIS, 0));
-    assertEquals("Yesterday, 7:50 PM",
-                 getRelativeDateTimeString(en_US, tz, base - 15 * HOUR_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-    assertEquals("5 days ago, 10:50 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 5 * DAY_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-    assertEquals("Jan 29, 10:50 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 7 * DAY_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-    assertEquals("11/27/2014, 10:50 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-    assertEquals("11/27/2014, 10:50 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
-                                           YEAR_IN_MILLIS, 0));
-
-    // User-supplied flags should be ignored when formatting the date clause.
-    final int FORMAT_SHOW_WEEKDAY = 0x00002;
-    assertEquals("11/27/2014, 10:50 AM",
-                 getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS,
-                                           FORMAT_ABBREV_ALL | FORMAT_SHOW_WEEKDAY));
-  }
-
-  public void test_getRelativeDateTimeStringDST() throws Exception {
-    Locale en_US = new Locale("en", "US");
-    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-    Calendar cal = Calendar.getInstance(tz, en_US);
-
-    // DST starts on Mar 9, 2014 at 2:00 AM.
-    // So 5 hours before 3:15 AM should be formatted as 'Yesterday, 9:15 PM'.
-    cal.set(2014, Calendar.MARCH, 9, 3, 15, 0);
-    long base = cal.getTimeInMillis();
-    assertEquals("Yesterday, 9:15 PM",
-                 getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-
-    // 1 hour after 2:00 AM should be formatted as 'In 1 hour, 4:00 AM'.
-    cal.set(2014, Calendar.MARCH, 9, 2, 0, 0);
-    base = cal.getTimeInMillis();
-    assertEquals("In 1 hour, 4:00 AM",
-                 getRelativeDateTimeString(en_US, tz, base + 1 * HOUR_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-
-    // DST ends on Nov 2, 2014 at 2:00 AM. Clocks are turned backward 1 hour to
-    // 1:00 AM. 8 hours before 5:20 AM should be 'Yesterday, 10:20 PM'.
-    cal.set(2014, Calendar.NOVEMBER, 2, 5, 20, 0);
-    base = cal.getTimeInMillis();
-    assertEquals("Yesterday, 10:20 PM",
-                 getRelativeDateTimeString(en_US, tz, base - 8 * HOUR_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-
-    cal.set(2014, Calendar.NOVEMBER, 2, 0, 45, 0);
-    base = cal.getTimeInMillis();
-    // 45 minutes after 0:45 AM should be 'In 45 minutes, 1:30 AM'.
-    assertEquals("In 45 minutes, 1:30 AM",
-                 getRelativeDateTimeString(en_US, tz, base + 45 * MINUTE_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-    // 45 minutes later, it should be 'In 45 minutes, 1:15 AM'.
-    assertEquals("In 45 minutes, 1:15 AM",
-                 getRelativeDateTimeString(en_US, tz, base + 90 * MINUTE_IN_MILLIS,
-                                           base + 45 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
-    // Another 45 minutes later, it should be 'In 45 minutes, 2:00 AM'.
-    assertEquals("In 45 minutes, 2:00 AM",
-                 getRelativeDateTimeString(en_US, tz, base + 135 * MINUTE_IN_MILLIS,
-                                           base + 90 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
-  }
-
-
-  public void test_getRelativeDateTimeStringItalian() throws Exception {
-    Locale it_IT = new Locale("it", "IT");
-    TimeZone tz = TimeZone.getTimeZone("Europe/Rome");
-    Calendar cal = Calendar.getInstance(tz, it_IT);
-    // 05 febbraio 2015 20:15
-    cal.set(2015, Calendar.FEBRUARY, 5, 20, 15, 0);
-    final long base = cal.getTimeInMillis();
-
-    assertEquals("5 secondi fa, 20:14",
-                 getRelativeDateTimeString(it_IT, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
-                                           MINUTE_IN_MILLIS, 0));
-    assertEquals("5 min fa, 20:10",
-                 getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
-                                           HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-    assertEquals("0 h fa, 20:10",
-                 getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base,
-                                           HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-    assertEquals("Ieri, 22:15",
-                 getRelativeDateTimeString(it_IT, tz, base - 22 * HOUR_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-    assertEquals("5 giorni fa, 20:15",
-                 getRelativeDateTimeString(it_IT, tz, base - 5 * DAY_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-    assertEquals("27/11/2014, 20:15",
-                 getRelativeDateTimeString(it_IT, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
-                                           WEEK_IN_MILLIS, 0));
-  }
-
-  // http://b/5252772: detect the actual date difference
-  public void test5252772() throws Exception {
-    Locale en_US = new Locale("en", "US");
-    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-
-    // Now is Sep 2, 2011, 10:23 AM PDT.
-    Calendar nowCalendar = Calendar.getInstance(tz, en_US);
-    nowCalendar.set(2011, Calendar.SEPTEMBER, 2, 10, 23, 0);
-    final long now = nowCalendar.getTimeInMillis();
-
-    // Sep 1, 2011, 10:24 AM
-    Calendar yesterdayCalendar1 = Calendar.getInstance(tz, en_US);
-    yesterdayCalendar1.set(2011, Calendar.SEPTEMBER, 1, 10, 24, 0);
-    long yesterday1 = yesterdayCalendar1.getTimeInMillis();
-    assertEquals("Yesterday, 10:24 AM",
-                 getRelativeDateTimeString(en_US, tz, yesterday1, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-
-    // Sep 1, 2011, 10:22 AM
-    Calendar yesterdayCalendar2 = Calendar.getInstance(tz, en_US);
-    yesterdayCalendar2.set(2011, Calendar.SEPTEMBER, 1, 10, 22, 0);
-    long yesterday2 = yesterdayCalendar2.getTimeInMillis();
-    assertEquals("Yesterday, 10:22 AM",
-                 getRelativeDateTimeString(en_US, tz, yesterday2, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-
-    // Aug 31, 2011, 10:24 AM
-    Calendar twoDaysAgoCalendar1 = Calendar.getInstance(tz, en_US);
-    twoDaysAgoCalendar1.set(2011, Calendar.AUGUST, 31, 10, 24, 0);
-    long twoDaysAgo1 = twoDaysAgoCalendar1.getTimeInMillis();
-    assertEquals("2 days ago, 10:24 AM",
-                 getRelativeDateTimeString(en_US, tz, twoDaysAgo1, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-
-    // Aug 31, 2011, 10:22 AM
-    Calendar twoDaysAgoCalendar2 = Calendar.getInstance(tz, en_US);
-    twoDaysAgoCalendar2.set(2011, Calendar.AUGUST, 31, 10, 22, 0);
-    long twoDaysAgo2 = twoDaysAgoCalendar2.getTimeInMillis();
-    assertEquals("2 days ago, 10:22 AM",
-                 getRelativeDateTimeString(en_US, tz, twoDaysAgo2, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-
-    // Sep 3, 2011, 10:22 AM
-    Calendar tomorrowCalendar1 = Calendar.getInstance(tz, en_US);
-    tomorrowCalendar1.set(2011, Calendar.SEPTEMBER, 3, 10, 22, 0);
-    long tomorrow1 = tomorrowCalendar1.getTimeInMillis();
-    assertEquals("Tomorrow, 10:22 AM",
-                 getRelativeDateTimeString(en_US, tz, tomorrow1, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-
-    // Sep 3, 2011, 10:24 AM
-    Calendar tomorrowCalendar2 = Calendar.getInstance(tz, en_US);
-    tomorrowCalendar2.set(2011, Calendar.SEPTEMBER, 3, 10, 24, 0);
-    long tomorrow2 = tomorrowCalendar2.getTimeInMillis();
-    assertEquals("Tomorrow, 10:24 AM",
-                 getRelativeDateTimeString(en_US, tz, tomorrow2, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-
-    // Sep 4, 2011, 10:22 AM
-    Calendar twoDaysLaterCalendar1 = Calendar.getInstance(tz, en_US);
-    twoDaysLaterCalendar1.set(2011, Calendar.SEPTEMBER, 4, 10, 22, 0);
-    long twoDaysLater1 = twoDaysLaterCalendar1.getTimeInMillis();
-    assertEquals("In 2 days, 10:22 AM",
-                 getRelativeDateTimeString(en_US, tz, twoDaysLater1, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-
-    // Sep 4, 2011, 10:24 AM
-    Calendar twoDaysLaterCalendar2 = Calendar.getInstance(tz, en_US);
-    twoDaysLaterCalendar2.set(2011, Calendar.SEPTEMBER, 4, 10, 24, 0);
-    long twoDaysLater2 = twoDaysLaterCalendar2.getTimeInMillis();
-    assertEquals("In 2 days, 10:24 AM",
-                 getRelativeDateTimeString(en_US, tz, twoDaysLater2, now, MINUTE_IN_MILLIS,
-                                           WEEK_IN_MILLIS, 0));
-  }
-
-  // b/19822016: show / hide the year based on the dates in the arguments.
-  public void test_bug19822016() throws Exception {
-    Locale en_US = new Locale("en", "US");
-    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
-    Calendar cal = Calendar.getInstance(tz, en_US);
-    // Feb 5, 2012 at 10:50 PST
-    cal.set(2012, Calendar.FEBRUARY, 5, 10, 50, 0);
-    long base = cal.getTimeInMillis();
-
-    assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
-        base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
-    assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
-        base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
-    assertEquals("11/27/2011, 10:50 AM", getRelativeDateTimeString(en_US, tz,
-        base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
-
-    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
-        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
-    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
-        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
-    assertEquals("January 6, 2012", getRelativeTimeSpanString(en_US, tz,
-        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
-    assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
-        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
-    assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
-        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
-    assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
-        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
-
-    // Feb 5, 2018 at 10:50 PST
-    cal.set(2018, Calendar.FEBRUARY, 5, 10, 50, 0);
-    base = cal.getTimeInMillis();
-    assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
-        base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
-    assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
-        base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
-    assertEquals("11/27/2017, 10:50 AM", getRelativeDateTimeString(en_US, tz,
-        base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
-
-    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
-        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
-    assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
-        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
-    assertEquals("January 6, 2018", getRelativeTimeSpanString(en_US, tz,
-        base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
-    assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
-        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
-    assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
-        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
-    assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
-        base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
-  }
-
-  // Check for missing ICU data. http://b/25821045
-  public void test_bug25821045() {
-    final TimeZone tz = TimeZone.getDefault();
-    final long now = System.currentTimeMillis();
-    final long time = now + 1000;
-    final int minResolution = 1000 * 60;
-    final int transitionResolution = minResolution;
-    final int flags = FORMAT_ABBREV_RELATIVE;
-    // Exercise all available locales, forcing the ICU implementation to pre-cache the data. This
-    // highlights data issues. It can take a while.
-    for (Locale locale : Locale.getAvailableLocales()) {
-      // In (e.g.) ICU56 an exception is thrown on the first use for a locale if required data for
-      // the "other" plural is missing. It doesn't matter what is actually formatted.
-      try {
-        RelativeDateTimeFormatter.getRelativeDateTimeString(
-            locale, tz, time, now, minResolution, transitionResolution, flags);
-      } catch (IllegalStateException e) {
-        fail("Failed to format for " + locale);
-      }
-    }
-  }
-
-  // Check for ICU data lookup fallback failure. http://b/25883157
-  public void test_bug25883157() {
-    final Locale locale = new Locale("en", "GB");
-    final TimeZone tz = TimeZone.getTimeZone("GMT");
-
-    final Calendar cal = Calendar.getInstance(tz, locale);
-    cal.set(2015, Calendar.JUNE, 19, 12, 0, 0);
-
-    final long base = cal.getTimeInMillis();
-    final long time = base + 2 * WEEK_IN_MILLIS;
-
-    assertEquals("In 2 wk", getRelativeTimeSpanString(
-        locale, tz, time, base, WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
-  }
-
-  // http://b/63745717
-  public void test_combineDateAndTime_apostrophe() {
-    final Locale locale = new Locale("fr");
-    android.icu.text.RelativeDateTimeFormatter icuFormatter =
-            android.icu.text.RelativeDateTimeFormatter.getInstance(locale);
-    assertEquals("D à T", icuFormatter.combineDateAndTime("D", "T"));
-    // Ensure single quote ' and curly braces {} are not interpreted in input values.
-    assertEquals("D'x' à T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}"));
-  }
-}
diff --git a/luni/src/test/java/libcore/libcore/icu/TimeZoneIntegrationTest.java b/luni/src/test/java/libcore/libcore/icu/TimeZoneIntegrationTest.java
deleted file mode 100644
index f24d8b1..0000000
--- a/luni/src/test/java/libcore/libcore/icu/TimeZoneIntegrationTest.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2018 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.libcore.icu;
-
-import org.junit.Test;
-
-import android.icu.text.TimeZoneNames;
-import android.icu.util.VersionInfo;
-import android.system.Os;
-
-import com.android.icu.util.Icu4cMetadata;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.BrokenBarrierException;
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import libcore.timezone.TimeZoneDataFiles;
-import libcore.timezone.TimeZoneFinder;
-import libcore.timezone.TzDataSetVersion;
-import libcore.timezone.ZoneInfoDb;
-import libcore.util.CoreLibraryDebug;
-import libcore.util.DebugInfo;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/**
- * Tests that compare ICU and libcore time zone behavior and similar cross-cutting concerns.
- */
-public class TimeZoneIntegrationTest {
-
-    // http://b/28949992
-    @Test
-    public void testJavaSetDefaultAppliesToIcuTimezone() {
-        java.util.TimeZone origTz = java.util.TimeZone.getDefault();
-        try {
-            android.icu.util.TimeZone origIcuTz = android.icu.util.TimeZone.getDefault();
-            assertEquals(origTz.getID(), origIcuTz.getID());
-
-            java.util.TimeZone tz = java.util.TimeZone.getTimeZone("GMT-05:00");
-            java.util.TimeZone.setDefault(tz);
-            android.icu.util.TimeZone icuTz = android.icu.util.TimeZone.getDefault();
-            assertEquals(tz.getID(), icuTz.getID());
-        } finally {
-            java.util.TimeZone.setDefault(origTz);
-        }
-    }
-
-    // http://b/30937209
-    @Test
-    public void testSetDefaultDeadlock() throws InterruptedException, BrokenBarrierException {
-        // Since this tests a deadlock, the test has two fundamental problems:
-        // - it is probabilistic: it's not guaranteed to fail if the problem exists
-        // - if it fails, it will effectively hang the current runtime, as no other thread will
-        //   be able to call TimeZone.getDefault()/setDefault() successfully any more.
-
-        // 10 was too low to be reliable, 100 failed more than half the time (on a bullhead).
-        final int iterations = 100;
-        java.util.TimeZone otherTimeZone = java.util.TimeZone.getTimeZone("Europe/London");
-        AtomicInteger setterCount = new AtomicInteger();
-        CyclicBarrier startBarrier = new CyclicBarrier(2);
-        Thread setter = new Thread(() -> {
-            waitFor(startBarrier);
-            for (int i = 0; i < iterations; i++) {
-                java.util.TimeZone.setDefault(otherTimeZone);
-                java.util.TimeZone.setDefault(null);
-                setterCount.set(i+1);
-            }
-        });
-        setter.setName("testSetDefaultDeadlock setter");
-
-        AtomicInteger getterCount = new AtomicInteger();
-        Thread getter = new Thread(() -> {
-            waitFor(startBarrier);
-            for (int i = 0; i < iterations; i++) {
-                android.icu.util.TimeZone.getDefault();
-                getterCount.set(i+1);
-            }
-        });
-        getter.setName("testSetDefaultDeadlock getter");
-
-        setter.start();
-        getter.start();
-
-        // 2 seconds is plenty: If successful, we usually complete much faster.
-        setter.join(1000);
-        getter.join(1000);
-        if (setter.isAlive() || getter.isAlive()) {
-            fail("Threads are still alive. Getter iteration count: " + getterCount.get()
-                    + ", setter iteration count: " + setterCount.get());
-        }
-        // Guard against unexpected uncaught exceptions.
-        assertEquals("Setter iterations", iterations, setterCount.get());
-        assertEquals("Getter iterations", iterations, getterCount.get());
-    }
-
-    // http://b/30979219
-    @Test
-    public void testSetDefaultRace() throws InterruptedException {
-        // Since this tests a race condition, the test is probabilistic: it's not guaranteed to
-        // fail if the problem exists
-
-        // These iterations are significantly faster than the ones in #testSetDefaultDeadlock
-        final int iterations = 10000;
-        List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>());
-        Thread.UncaughtExceptionHandler handler = (t, e) -> exceptions.add(e);
-
-        CyclicBarrier startBarrier = new CyclicBarrier(2);
-        Thread clearer = new Thread(() -> {
-            waitFor(startBarrier);
-            for (int i = 0; i < iterations; i++) {
-                // This is not public API but can effectively be invoked via
-                // java.util.TimeZone.setDefault. Call it directly to reduce the amount of code
-                // involved in this test.
-                android.icu.util.TimeZone.setICUDefault(null);
-            }
-        });
-        clearer.setName("testSetDefaultRace clearer");
-        clearer.setUncaughtExceptionHandler(handler);
-
-        Thread getter = new Thread(() -> {
-            waitFor(startBarrier);
-            for (int i = 0; i < iterations; i++) {
-                android.icu.util.TimeZone.getDefault();
-            }
-        });
-        getter.setName("testSetDefaultRace getter");
-        getter.setUncaughtExceptionHandler(handler);
-
-        clearer.start();
-        getter.start();
-
-        // 20 seconds is plenty: If successful, we usually complete much faster.
-        clearer.join(10000);
-        getter.join(10000);
-
-        if (!exceptions.isEmpty()) {
-            Throwable firstException = exceptions.get(0);
-            firstException.printStackTrace();
-            fail("Threads did not succeed successfully: " + firstException);
-        }
-        assertFalse("clearer thread is still alive", clearer.isAlive());
-        assertFalse("getter thread is still alive", getter.isAlive());
-    }
-
-    private static void waitFor(CyclicBarrier barrier) {
-        try {
-            barrier.await();
-        } catch (InterruptedException | BrokenBarrierException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Confirms that ICU agrees with the rest of libcore about the version of the TZ data in use.
-     */
-    @Test
-    public void testTimeZoneDataVersion() {
-        String icu4cTzVersion = Icu4cMetadata.getTzdbVersion();
-
-        String zoneInfoTzVersion = ZoneInfoDb.getInstance().getVersion();
-        assertEquals(icu4cTzVersion, zoneInfoTzVersion);
-
-        String icu4jTzVersion = android.icu.util.TimeZone.getTZDataVersion();
-        assertEquals(icu4jTzVersion, zoneInfoTzVersion);
-
-        String tzLookupTzVersion = TimeZoneFinder.getInstance().getIanaVersion();
-        assertEquals(icu4jTzVersion, tzLookupTzVersion);
-    }
-
-    /**
-     * Asserts that the time zone format major / minor versions meets expectations.
-     *
-     * <p>If a set of time zone files is to be compatible with a device then the format of the files
-     * must meet the Android team's expectations. This is a sanity check to ensure that devices
-     * running the test (e.g. under CTS) have not modified the TzDataSetVersion major / minor
-     * versions for some reason: if they have it would render updated time zone files sent to the
-     * device incompatible.
-     */
-    @Test
-    public void testTimeZoneFormatVersion() {
-        // The code below compares the final static int constant values (inlined at test compile
-        // time) with the version reported at runtime. This saves us hardcoding the numbers in two
-        // places.
-        assertEquals(TzDataSetVersion.CURRENT_FORMAT_MAJOR_VERSION,
-                TzDataSetVersion.currentFormatMajorVersion());
-        assertEquals(TzDataSetVersion.CURRENT_FORMAT_MINOR_VERSION,
-                TzDataSetVersion.currentFormatMinorVersion());
-    }
-
-    /**
-     * Asserts that all expected sets of time zone files meet format expectations.
-     *
-     * <p>This uses the device's knowledge of the format version it expects and the
-     * {@link TzDataSetVersion} files that accompany the known time zone data files.
-     *
-     * <p>This is a sanity check to ensure that there's no way of installing incompatible data
-     * on a device. It assumes that {@link TzDataSetVersion} is updated as it should be when changes
-     * are made that might affect time zone code / time zone data compatibility.
-     */
-    @Test
-    public void testTzDataSetVersions() throws Exception {
-        // The time zone data module is required.
-        String timeZoneModuleVersionFile =
-                TimeZoneDataFiles.getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
-        assertTzDataSetVersionIsCompatible(timeZoneModuleVersionFile);
-
-        // Check getTimeZoneModuleTzVersionFile() is doing the right thing.
-        // getTimeZoneModuleTzVersionFile() should go away when its one user, RulesManagerService,
-        // is removed from the platform code. http://b/123398797
-        assertEquals(TimeZoneDataFiles.getTimeZoneModuleTzVersionFile(), timeZoneModuleVersionFile);
-
-        // TODO: Remove this once the /system copy of time zone files have gone away. See also
-        // testTimeZoneDebugInfo().
-        assertTzDataSetVersionIsCompatible(
-                TimeZoneDataFiles.getSystemTzFile(TzDataSetVersion.DEFAULT_FILE_NAME));
-    }
-
-    private static void assertTzDataSetVersionIsCompatible(String versionFile) throws Exception {
-        TzDataSetVersion actualVersion = TzDataSetVersion.readFromFile(new File(versionFile));
-        assertEquals(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                actualVersion.getFormatMajorVersion());
-        int minDeviceMinorVersion = TzDataSetVersion.currentFormatMinorVersion();
-        assertTrue(actualVersion.getFormatMinorVersion() >= minDeviceMinorVersion);
-    }
-
-    /**
-     * A test for confirming debug information matches file system state on device.
-     * It can also be used to confirm that device and host environments satisfy file system
-     * expectations.
-     */
-    @Test
-    public void testTimeZoneDebugInfo() throws Exception {
-        DebugInfo debugInfo = CoreLibraryDebug.getDebugInfo();
-
-        // Devices are expected to have a time zone module which overrides or extends the data in
-        // the runtime module depending on the file. It's not actually mandatory for all Android
-        // devices right now although it may be required for some subset of Android devices. It
-        // isn't present on host ART.
-        String tzModuleStatus = getDebugStringValue(debugInfo,
-                "core_library.timezone.source.tzdata_module_status");
-        String apexRootDir = TimeZoneDataFiles.getTimeZoneModuleFile("");
-        List<String> dataModuleFiles =
-                createModuleTzFiles(TimeZoneDataFiles::getTimeZoneModuleTzFile);
-        String icuOverlayFile = TimeZoneDataFiles.getTimeZoneModuleIcuFile("icu_tzdata.dat");
-        if (fileExists(apexRootDir)) {
-            assertEquals("OK", tzModuleStatus);
-            dataModuleFiles.forEach(TimeZoneIntegrationTest::assertFileExists);
-            assertFileExists(icuOverlayFile);
-        } else {
-            assertEquals("NOT_FOUND", tzModuleStatus);
-            dataModuleFiles.forEach(TimeZoneIntegrationTest::assertFileDoesNotExist);
-            assertFileDoesNotExist(icuOverlayFile);
-        }
-
-        String icuDatFileName = "icudt" + VersionInfo.ICU_VERSION.getMajor() + "l.dat";
-        String i18nModuleIcuData = TimeZoneDataFiles.getI18nModuleIcuFile(icuDatFileName);
-        assertFileExists(i18nModuleIcuData);
-
-        // Devices currently have a subset of the time zone files in /system. These are going away
-        // but we test them while they exist. Host ART should match device.
-        assertEquals("OK", getDebugStringValue(debugInfo,
-                "core_library.timezone.source.system_status"));
-        assertFileExists(
-                TimeZoneDataFiles.getSystemTzFile(TzDataSetVersion.DEFAULT_FILE_NAME));
-        assertFileExists(TimeZoneDataFiles.getSystemTzFile(ZoneInfoDb.TZDATA_FILE_NAME));
-        // The following files once existed in /system but have been removed as part of APEX work.
-        assertFileDoesNotExist(
-                TimeZoneDataFiles.getSystemTzFile(TimeZoneFinder.TZLOOKUP_FILE_NAME));
-
-        // It's hard to assert much about this file as there is a symlink in /system on device for
-        // app compatibility (b/122985829) but it doesn't exist in host environments. If the file
-        // exists we can say it should resolve (realpath) to the same file as the runtime module.
-        String systemIcuData = TimeZoneDataFiles.getSystemIcuFile(icuDatFileName);
-        if (new File(systemIcuData).exists()) {
-            assertEquals(Os.realpath(i18nModuleIcuData), Os.realpath(systemIcuData));
-        }
-    }
-
-    private static List<String> createModuleTzFiles(
-            Function<String, String> pathCreationFunction) {
-        List<String> relativePaths = Arrays.asList(
-                TzDataSetVersion.DEFAULT_FILE_NAME,
-                ZoneInfoDb.TZDATA_FILE_NAME,
-                TimeZoneFinder.TZLOOKUP_FILE_NAME);
-        return relativePaths.stream().map(pathCreationFunction).collect(Collectors.toList());
-    }
-
-    private static boolean fileExists(String fileName) {
-        return new File(fileName).exists();
-    }
-
-    private static void assertFileDoesNotExist(String fileName) {
-        assertFalse(fileName + " must not exist", fileExists(fileName));
-    }
-
-    private static void assertFileExists(String fileName) {
-        assertTrue(fileName + " must exist", fileExists(fileName));
-    }
-
-    private String getDebugStringValue(DebugInfo debugInfo, String key) {
-        return debugInfo.getDebugEntry(key).getStringValue();
-    }
-
-    /**
-     * Confirms that ICU can recognize all the time zone IDs used by the ZoneInfoDB data.
-     * ICU's IDs may be a superset.
-     */
-    @Test
-    public void testTimeZoneIdLookup() {
-        String[] zoneInfoDbAvailableIds = ZoneInfoDb.getInstance().getAvailableIDs();
-
-        // ICU has a known set of IDs. We want ANY because we don't want to filter to ICU's
-        // canonical IDs only.
-        Set<String> icuAvailableIds = android.icu.util.TimeZone.getAvailableIDs(
-                android.icu.util.TimeZone.SystemTimeZoneType.ANY, null /* region */,
-                null /* rawOffset */);
-
-        List<String> nonIcuAvailableIds = new ArrayList<>();
-        List<String> creationFailureIds = new ArrayList<>();
-        List<String> noCanonicalLookupIds = new ArrayList<>();
-        List<String> nonSystemIds = new ArrayList<>();
-        for (String zoneInfoDbId : zoneInfoDbAvailableIds) {
-            if (!icuAvailableIds.contains(zoneInfoDbId)) {
-                nonIcuAvailableIds.add(zoneInfoDbId);
-            }
-
-            boolean[] isSystemId = new boolean[1];
-            String canonicalId = android.icu.util.TimeZone.getCanonicalID(zoneInfoDbId, isSystemId);
-            if (canonicalId == null) {
-                noCanonicalLookupIds.add(zoneInfoDbId);
-            }
-            if (!isSystemId[0]) {
-                nonSystemIds.add(zoneInfoDbId);
-            }
-
-            android.icu.util.TimeZone icuTimeZone =
-                    android.icu.util.TimeZone.getTimeZone(zoneInfoDbId);
-            if (icuTimeZone.getID().equals(android.icu.util.TimeZone.UNKNOWN_ZONE_ID)) {
-                creationFailureIds.add(zoneInfoDbId);
-            }
-        }
-        assertTrue("Non-ICU available IDs: " + nonIcuAvailableIds
-                        + ", creation failed IDs: " + creationFailureIds
-                        + ", non-system IDs: " + nonSystemIds
-                        + ", ids without canonical IDs: " + noCanonicalLookupIds,
-                nonIcuAvailableIds.isEmpty()
-                        && creationFailureIds.isEmpty()
-                        && nonSystemIds.isEmpty()
-                        && noCanonicalLookupIds.isEmpty());
-    }
-
-    // http://b/30527513
-    @Test
-    public void testDisplayNamesWithScript() throws Exception {
-        Locale latinLocale = Locale.forLanguageTag("sr-Latn-RS");
-        Locale cyrillicLocale = Locale.forLanguageTag("sr-Cyrl-RS");
-        Locale noScriptLocale = Locale.forLanguageTag("sr-RS");
-        java.util.TimeZone tz = java.util.TimeZone.getTimeZone("Europe/London");
-
-        final String latinName = "Srednje vreme po Griniču";
-        final String cyrillicName = "Средње време по Гриничу";
-
-        // Check java.util.TimeZone
-        assertEquals(latinName, tz.getDisplayName(latinLocale));
-        assertEquals(cyrillicName, tz.getDisplayName(cyrillicLocale));
-        assertEquals(cyrillicName, tz.getDisplayName(noScriptLocale));
-
-        // Check ICU TimeZoneNames
-        // The one-argument getDisplayName() override uses LONG_GENERIC style which is different
-        // from what java.util.TimeZone uses. Force the LONG style to get equivalent results.
-        final int style = android.icu.util.TimeZone.LONG;
-        android.icu.util.TimeZone utz = android.icu.util.TimeZone.getTimeZone(tz.getID());
-        assertEquals(latinName, utz.getDisplayName(false, style, latinLocale));
-        assertEquals(cyrillicName, utz.getDisplayName(false, style, cyrillicLocale));
-        assertEquals(cyrillicName, utz.getDisplayName(false, style, noScriptLocale));
-    }
-
-    /**
-     * This test is to catch issues with the rules update process that could let the
-     * "negative DST" scheme enter the Android data set for either java.util.TimeZone or
-     * android.icu.util.TimeZone.
-     */
-    @Test
-    public void testDstMeansSummer() {
-        // Ireland was the original example that caused the default IANA upstream tzdata to contain
-        // a zone where DST is in the Winter (since tzdata 2018e, though it was tried in 2018a
-        // first). This change was made to historical and future transitions.
-        //
-        // The upstream reasoning went like this: "Irish *Standard* Time" is summer, so the other
-        // time must be the DST. So, DST is considered to be in the winter and the associated DST
-        // adjustment is negative from the standard time. In the old scheme "Irish Standard Time" /
-        // summer was just modeled as the DST in common with all other global time zones.
-        //
-        // Unfortunately, various users of formatting APIs assume standard and DST times are
-        // consistent and (effectively) that "DST" means "summer". We likely cannot adopt the
-        // concept of a winter DST without risking app compat issues.
-        //
-        // For example, getDisplayName(boolean daylight) has always returned the winter time for
-        // false, and the summer time for true. If we change this then it should be changed on a
-        // major release boundary, with improved APIs (e.g. a version of getDisplayName() that takes
-        // a millis), existing API behavior made dependent on target API version, and after fixing
-        // any platform code that makes incorrect assumptions about DST meaning "1 hour forward".
-
-        final String timeZoneId = "Europe/Dublin";
-        final Locale locale = Locale.UK;
-        // 26 Oct 2015 01:00:00 GMT - one day after the start of "Greenwich Mean Time" in
-        // Europe/Dublin in 2015. An arbitrary historical example of winter in Ireland.
-        final long winterTimeMillis = 1445821200000L;
-        final String winterTimeName = "Greenwich Mean Time";
-        final int winterOffsetRawMillis = 0;
-        final int winterOffsetDstMillis = 0;
-
-        // 30 Mar 2015 01:00:00 GMT - one day after the start of "Irish Standard Time" in
-        // Europe/Dublin in 2015. An arbitrary historical example of summer in Ireland.
-        final long summerTimeMillis = 1427677200000L;
-        final String summerTimeName = "Irish Standard Time";
-        final int summerOffsetRawMillis = 0;
-        final int summerOffsetDstMillis = (int) TimeUnit.HOURS.toMillis(1);
-
-        // There is no common interface between java.util.TimeZone and android.icu.util.TimeZone
-        // so the tests are for each are effectively duplicated.
-
-        // java.util.TimeZone
-        {
-            java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(timeZoneId);
-            assertTrue(timeZone.useDaylightTime());
-
-            assertFalse(timeZone.inDaylightTime(new Date(winterTimeMillis)));
-            assertTrue(timeZone.inDaylightTime(new Date(summerTimeMillis)));
-
-            assertEquals(winterOffsetRawMillis + winterOffsetDstMillis,
-                    timeZone.getOffset(winterTimeMillis));
-            assertEquals(summerOffsetRawMillis + summerOffsetDstMillis,
-                    timeZone.getOffset(summerTimeMillis));
-            assertEquals(winterTimeName,
-                    timeZone.getDisplayName(false /* daylight */, java.util.TimeZone.LONG,
-                            locale));
-            assertEquals(summerTimeName,
-                    timeZone.getDisplayName(true /* daylight */, java.util.TimeZone.LONG,
-                            locale));
-        }
-
-        // android.icu.util.TimeZone
-        {
-            android.icu.util.TimeZone timeZone = android.icu.util.TimeZone.getTimeZone(timeZoneId);
-            assertTrue(timeZone.useDaylightTime());
-
-            assertFalse(timeZone.inDaylightTime(new Date(winterTimeMillis)));
-            assertTrue(timeZone.inDaylightTime(new Date(summerTimeMillis)));
-
-            assertEquals(winterOffsetRawMillis + winterOffsetDstMillis,
-                    timeZone.getOffset(winterTimeMillis));
-            assertEquals(summerOffsetRawMillis + summerOffsetDstMillis,
-                    timeZone.getOffset(summerTimeMillis));
-
-            // These methods show the trouble we'd have if callers were to take the output from
-            // inDaylightTime() and pass it to getDisplayName().
-            assertEquals(winterTimeName,
-                    timeZone.getDisplayName(false /* daylight */, android.icu.util.TimeZone.LONG,
-                            locale));
-            assertEquals(summerTimeName,
-                    timeZone.getDisplayName(true /* daylight */, android.icu.util.TimeZone.LONG,
-                            locale));
-
-            // APIs not identical to java.util.TimeZone tested below.
-            int[] offsets = new int[2];
-            timeZone.getOffset(winterTimeMillis, false /* local */, offsets);
-            assertEquals(winterOffsetRawMillis, offsets[0]);
-            assertEquals(winterOffsetDstMillis, offsets[1]);
-
-            timeZone.getOffset(summerTimeMillis, false /* local */, offsets);
-            assertEquals(summerOffsetRawMillis, offsets[0]);
-            assertEquals(summerOffsetDstMillis, offsets[1]);
-        }
-
-        // icu TimeZoneNames
-        TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
-        // getDisplayName: date = winterTimeMillis
-        assertEquals(winterTimeName, timeZoneNames.getDisplayName(
-                timeZoneId, TimeZoneNames.NameType.LONG_STANDARD, winterTimeMillis));
-        assertEquals(summerTimeName, timeZoneNames.getDisplayName(
-                timeZoneId, TimeZoneNames.NameType.LONG_DAYLIGHT, winterTimeMillis));
-        // getDisplayName: date = summerTimeMillis
-        assertEquals(winterTimeName, timeZoneNames.getDisplayName(
-                timeZoneId, TimeZoneNames.NameType.LONG_STANDARD, summerTimeMillis));
-        assertEquals(summerTimeName, timeZoneNames.getDisplayName(
-                timeZoneId, TimeZoneNames.NameType.LONG_DAYLIGHT, summerTimeMillis));
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/icu/TransliteratorTest.java b/luni/src/test/java/libcore/libcore/icu/TransliteratorTest.java
deleted file mode 100644
index d1e1483..0000000
--- a/luni/src/test/java/libcore/libcore/icu/TransliteratorTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2013 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.libcore.icu;
-
-import android.icu.text.Transliterator;
-
-import java.util.Enumeration;
-
-public class TransliteratorTest extends junit.framework.TestCase {
-  public void testAll() throws Exception {
-    Enumeration<String> ids = Transliterator.getAvailableIDs();
-    while (ids.hasMoreElements()) {
-      String id = ids.nextElement();
-      System.err.println(id);
-      Transliterator t = Transliterator.getInstance(id);
-      t.transliterate("hello");
-    }
-  }
-
-  public void test_Unknown() throws Exception {
-    try {
-      Transliterator t = Transliterator.getInstance("Unknown");
-      fail();
-    } catch (RuntimeException expected) {
-    }
-  }
-
-  public void test_null_id() throws Exception {
-    try {
-      Transliterator t = Transliterator.getInstance(null);
-      fail();
-    } catch (NullPointerException expected) {
-    }
-  }
-
-  public void test_Any_Upper() throws Exception {
-    Transliterator t = Transliterator.getInstance("Any-Upper");
-    assertEquals("HELLO WORLD!", t.transliterate("HeLlO WoRlD!"));
-    assertEquals("STRASSE", t.transliterate("Straße"));
-  }
-
-  public void test_Any_Lower() throws Exception {
-    Transliterator t = Transliterator.getInstance("Any-Lower");
-    assertEquals("hello world!", t.transliterate("HeLlO WoRlD!"));
-  }
-
-  public void test_Greek_Latin() throws Exception {
-    String greek = "Καλημέρα κόσμε!";
-
-    // Transliterate Greek to Latin, then to plain ASCII.
-    Transliterator t = Transliterator.getInstance("Greek-Latin");
-    String latin = t.transliterate(greek);
-    t = Transliterator.getInstance("Latin-Ascii");
-    String ascii = t.transliterate(latin);
-    assertEquals("Kalēméra kósme!", latin);
-    assertEquals("Kalemera kosme!", ascii);
-
-    // Use alternative transliteration variants.
-    t = Transliterator.getInstance("Greek-Latin/BGN");
-    assertEquals("Kaliméra kósme!", t.transliterate(greek));
-    t = Transliterator.getInstance("Greek-Latin/UNGEGN");
-    assertEquals("Kali̱méra kósme!",t.transliterate(greek));
-  }
-
-  public void test_Han_Latin() throws Exception {
-    Transliterator t = Transliterator.getInstance("Han-Latin");
-    assertEquals("hàn zì/hàn zì", t.transliterate("汉字/漢字"));
-
-    assertEquals("chén", t.transliterate("\u6c88"));
-    assertEquals("shěn", t.transliterate("\u700b"));
-    assertEquals("jiǎ", t.transliterate("\u8d3e"));
-
-    t = Transliterator.getInstance("Han-Latin/Names");
-    assertEquals("shěn", t.transliterate("\u6c88"));
-    assertEquals("shěn", t.transliterate("\u700b"));
-    assertEquals("jiǎ", t.transliterate("\u8d3e"));
-
-    t = Transliterator.getInstance("Han-Latin/Names; Latin-Ascii; Any-Upper");
-    assertEquals("SHEN", t.transliterate("\u6c88"));
-    assertEquals("SHEN", t.transliterate("\u700b"));
-    assertEquals("JIA", t.transliterate("\u8d3e"));
-  }
-}
diff --git a/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java b/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
index 218678e..adbf399 100644
--- a/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
+++ b/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
@@ -17,10 +17,16 @@
 package libcore.libcore.io;
 
 import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNIX;
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.F_SETFL;
 import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.O_NONBLOCK;
 import static android.system.OsConstants.SOCK_STREAM;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import org.junit.After;
 import org.junit.Before;
@@ -33,12 +39,14 @@
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.system.StructAddrinfo;
+import android.system.UnixSocketAddress;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.net.InetAddress;
+import java.net.SocketAddress;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -143,6 +151,47 @@
     }
 
     @Test
+    public void test_nonblock() throws ErrnoException, IOException {
+        FileDescriptor unixSocket = Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0);
+        FileDescriptor udpSocket = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+        Libcore.os.fcntlInt(unixSocket, F_SETFL, O_NONBLOCK);
+        Libcore.os.fcntlInt(udpSocket, F_SETFL, O_NONBLOCK);
+        try {
+            assertTrue(BlockGuardOs.isNonBlockingFile(unixSocket));
+            assertTrue(BlockGuardOs.isNonBlockingFile(udpSocket));
+        } finally {
+            IoUtils.closeQuietly(unixSocket);
+        }
+    }
+
+    @Test
+    public void test_unixSocket() throws ErrnoException, IOException {
+        FileDescriptor unixSocket = Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0);
+        FileDescriptor udpSocket = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+        try {
+            assertTrue(BlockGuardOs.isUnixSocket(unixSocket));
+            assertFalse(BlockGuardOs.isUnixSocket(udpSocket));
+        } finally {
+            IoUtils.closeQuietly(unixSocket);
+        }
+    }
+
+    @Test
+    public void test_accept_networkPolicy() throws ErrnoException, IOException {
+        BlockGuardOs blockGuardOs = new BlockGuardOs(mockOsDelegate);
+
+        FileDescriptor unixSocket = Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0);
+        Libcore.os.fcntlInt(unixSocket, F_SETFL, O_NONBLOCK);
+        SocketAddress address = UnixSocketAddress.createAbstract("test_accept_networkPolicy");
+        Libcore.os.bind(unixSocket, address);
+        try {
+            assertNull(blockGuardOs.accept(unixSocket, address));
+        } finally {
+            IoUtils.closeQuietly(unixSocket);
+        }
+    }
+
+    @Test
     public void test_connect_networkPolicy() throws ErrnoException, IOException {
         BlockGuardOs blockGuardOs = new BlockGuardOs(mockOsDelegate);
 
@@ -220,7 +269,7 @@
                 "inet_pton(int,java.lang.String)",
                 "ioctlFlags(java.io.FileDescriptor,java.lang.String)",
                 "ioctlInetAddress(java.io.FileDescriptor,int,java.lang.String)",
-                "ioctlInt(java.io.FileDescriptor,int,android.system.Int32Ref)",
+                "ioctlInt(java.io.FileDescriptor,int)",
                 "ioctlMTU(java.io.FileDescriptor,java.lang.String)",
                 "isatty(java.io.FileDescriptor)",
                 "kill(int,int)",
diff --git a/luni/src/test/java/libcore/libcore/net/event/NetworkEventDispatcherTest.java b/luni/src/test/java/libcore/libcore/net/event/NetworkEventDispatcherTest.java
index 1a5cd40bb..361fadd 100644
--- a/luni/src/test/java/libcore/libcore/net/event/NetworkEventDispatcherTest.java
+++ b/luni/src/test/java/libcore/libcore/net/event/NetworkEventDispatcherTest.java
@@ -31,7 +31,7 @@
   }
 
   public void testAddListener_null() throws Exception {
-    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher() {};
+    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher();
     try {
       networkEventDispatcher.addListener(null);
       fail();
@@ -40,27 +40,27 @@
   }
 
   public void testOnNetworkConfigurationChanged_noListeners() throws Exception {
-    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher() {};
-    networkEventDispatcher.onNetworkConfigurationChanged();
+    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher();
+    networkEventDispatcher.dispatchNetworkConfigurationChange();
   }
 
   public void testFireNetworkEvent_oneListener() throws Exception {
     FakeNetworkEventListener listener = new FakeNetworkEventListener();
-    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher() {};
+    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher();
     networkEventDispatcher.addListener(listener);
 
-    networkEventDispatcher.onNetworkConfigurationChanged();
+    networkEventDispatcher.dispatchNetworkConfigurationChange();
 
     listener.assertNetworkConfigurationChangedEvent(1);
   }
 
   public void testRemoveEventListener() throws Exception {
     FakeNetworkEventListener listener = new FakeNetworkEventListener();
-    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher() {};
+    NetworkEventDispatcher networkEventDispatcher = new NetworkEventDispatcher();
     networkEventDispatcher.addListener(listener);
     networkEventDispatcher.removeListener(listener);
 
-    networkEventDispatcher.onNetworkConfigurationChanged();
+    networkEventDispatcher.dispatchNetworkConfigurationChange();
 
     listener.assertNetworkConfigurationChangedEvent(0);
   }
diff --git a/luni/src/test/java/libcore/libcore/net/http/HttpURLConnectionFactoryTest.java b/luni/src/test/java/libcore/libcore/net/http/HttpURLConnectionFactoryTest.java
new file mode 100644
index 0000000..09e78b2
--- /dev/null
+++ b/luni/src/test/java/libcore/libcore/net/http/HttpURLConnectionFactoryTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 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.libcore.net.http;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import libcore.net.http.HttpURLConnectionFactory;
+import libcore.net.http.Dns;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.SocketFactory;
+
+@RunWith(JUnit4.class)
+public class HttpURLConnectionFactoryTest {
+
+    @Test
+    public void testCreateInstance() {
+        final HttpURLConnectionFactory factory = HttpURLConnectionFactory.createInstance();
+        assertNotNull(factory);
+        assertTrue(factory instanceof HttpURLConnectionFactory);
+    }
+
+    @Test
+    public void testOpenConnection() throws IOException {
+        final Map<String, InetAddress> lookupResult = new HashMap();
+        lookupResult.put("test.com", Inet4Address.ANY);
+        final HttpURLConnectionFactory factory = HttpURLConnectionFactory.createInstance();
+        final Dns dns = hostname -> Arrays.asList(lookupResult.get(hostname));
+        factory.setDns(dns);
+        factory.setNewConnectionPool(5, 5000, TimeUnit.MILLISECONDS);
+        final URL url = new URL("http://test.com");
+        final URLConnection connection = factory.openConnection(url, SocketFactory.getDefault(),
+                java.net.Proxy.NO_PROXY);
+        assertEquals(connection.getURL(), url);
+    }
+
+    @Test
+    public void testDns() throws IOException  {
+        final String testHostName1 = "test1.com";
+        final String testHostName2 = "test2.com";
+        final String testHostName3 = "test3.com";
+        final List<InetAddress> testHostAddresses1 = List.of(Inet4Address.ALL);
+        final List<InetAddress> testHostAddresses2 = List.of(Inet6Address.ANY);
+        final List<InetAddress> testHostAddresses3 =
+                List.of(Inet4Address.ANY, Inet6Address.LOOPBACK);
+        final Map<String, List<InetAddress>> lookupResult = new HashMap();
+        lookupResult.put(testHostName1, testHostAddresses1);
+        lookupResult.put(testHostName2, testHostAddresses2);
+        lookupResult.put(testHostName3, testHostAddresses3);
+
+        final Dns dns = hostname -> lookupResult.get(hostname);
+        assertEquals(testHostAddresses1, dns.lookup(testHostName1));
+        assertEquals(testHostAddresses2, dns.lookup(testHostName2));
+        assertEquals(testHostAddresses3, dns.lookup(testHostName3));
+    }
+}
diff --git a/luni/src/test/java/libcore/libcore/timezone/CountryTimeZonesTest.java b/luni/src/test/java/libcore/libcore/timezone/CountryTimeZonesTest.java
deleted file mode 100644
index cf32896..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/CountryTimeZonesTest.java
+++ /dev/null
@@ -1,552 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.libcore.timezone;
-
-import org.junit.Test;
-
-import android.icu.util.TimeZone;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import libcore.timezone.CountryTimeZones;
-import libcore.timezone.CountryTimeZones.OffsetResult;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class CountryTimeZonesTest {
-
-    private static final int HOUR_MILLIS = 60 * 60 * 1000;
-
-    private static final String INVALID_TZ_ID = "Moon/Tranquility_Base";
-
-    // Zones used in the tests. NY_TZ and LON_TZ chosen because they never overlap but both have
-    // DST.
-    private static final TimeZone NY_TZ = TimeZone.getTimeZone("America/New_York");
-    private static final TimeZone LON_TZ = TimeZone.getTimeZone("Europe/London");
-    // A zone that matches LON_TZ for WHEN_NO_DST. It does not have DST so differs for WHEN_DST.
-    private static final TimeZone REYK_TZ = TimeZone.getTimeZone("Atlantic/Reykjavik");
-    // Another zone that matches LON_TZ for WHEN_NO_DST. It does not have DST so differs for
-    // WHEN_DST.
-    private static final TimeZone UTC_TZ = TimeZone.getTimeZone("Etc/UTC");
-
-    // 22nd July 2017, 13:14:15 UTC (DST time in all the timezones used in these tests that observe
-    // DST).
-    private static final long WHEN_DST = 1500729255000L;
-    // 22nd January 2018, 13:14:15 UTC (non-DST time in all timezones used in these tests).
-    private static final long WHEN_NO_DST = 1516626855000L;
-
-    // The offset applied to most zones during DST.
-    private static final int NORMAL_DST_ADJUSTMENT = HOUR_MILLIS;
-
-    private static final int LON_NO_DST_TOTAL_OFFSET = 0;
-    private static final int LON_DST_TOTAL_OFFSET = LON_NO_DST_TOTAL_OFFSET
-            + NORMAL_DST_ADJUSTMENT;
-
-    private static final int NY_NO_DST_TOTAL_OFFSET = -5 * HOUR_MILLIS;
-    private static final int NY_DST_TOTAL_OFFSET = NY_NO_DST_TOTAL_OFFSET
-            + NORMAL_DST_ADJUSTMENT;
-
-    @Test
-    public void createValidated() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */,
-                true /* everUsesUtc */, timeZoneMappings("Europe/London"), "test");
-        assertTrue(countryTimeZones.isForCountryCode("gb"));
-        assertEquals("Europe/London", countryTimeZones.getDefaultTimeZoneId());
-        assertZoneEquals(zone("Europe/London"), countryTimeZones.getDefaultTimeZone());
-        assertEquals(timeZoneMappings("Europe/London"), countryTimeZones.getTimeZoneMappings());
-        assertEquals(timeZoneMappings("Europe/London"),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
-    }
-
-    @Test
-    public void createValidated_nullDefault() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "gb", null, false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-        assertNull(countryTimeZones.getDefaultTimeZoneId());
-        assertNull(countryTimeZones.getDefaultTimeZone());
-    }
-
-    @Test
-    public void createValidated_invalidDefault() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "gb", INVALID_TZ_ID, false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London", INVALID_TZ_ID), "test");
-        assertNull(countryTimeZones.getDefaultTimeZoneId());
-        assertNull(countryTimeZones.getDefaultTimeZone());
-        assertEquals(timeZoneMappings("Europe/London"), countryTimeZones.getTimeZoneMappings());
-        assertEquals(timeZoneMappings("Europe/London"),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
-    }
-
-    @Test
-    public void createValidated_unknownTimeZoneIdIgnored() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Unknown_Id", "Europe/London"), "test");
-        assertEquals(timeZoneMappings("Europe/London"), countryTimeZones.getTimeZoneMappings());
-        assertEquals(timeZoneMappings("Europe/London"),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
-    }
-
-    @Test
-    public void isForCountryCode() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-        assertTrue(countryTimeZones.isForCountryCode("GB"));
-        assertTrue(countryTimeZones.isForCountryCode("Gb"));
-        assertTrue(countryTimeZones.isForCountryCode("gB"));
-    }
-
-    @Test
-    public void structuresAreImmutable() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-
-        assertImmutableTimeZone(countryTimeZones.getDefaultTimeZone());
-
-        List<TimeZoneMapping> timeZoneMappings = countryTimeZones.getTimeZoneMappings();
-        assertEquals(1, timeZoneMappings.size());
-        assertImmutableList(timeZoneMappings);
-
-        List<TimeZoneMapping> effectiveTimeZoneMappings =
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */);
-        assertEquals(1, effectiveTimeZoneMappings.size());
-        assertImmutableList(effectiveTimeZoneMappings);
-    }
-
-    @Test
-    public void lookupByOffsetWithBias_oneCandidate() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-
-        OffsetResult lonMatch = new OffsetResult(LON_TZ, true /* oneMatch */);
-
-        // Placeholder constants to improve test case readability.
-        final Boolean isDst = true;
-        final Boolean notDst = false;
-        final Boolean unkIsDst = null;
-        final TimeZone noBias = null;
-        final OffsetResult noMatch = null;
-
-        Object[][] testCases = new Object[][] {
-                // totalOffsetMillis, isDst, whenMillis, bias, expectedMatch
-
-                // The parameters match the zone: total offset and time.
-                { LON_DST_TOTAL_OFFSET, unkIsDst, WHEN_DST, noBias, lonMatch },
-                { LON_NO_DST_TOTAL_OFFSET, unkIsDst, WHEN_NO_DST, noBias, lonMatch },
-
-                // The parameters match the zone: total offset, isDst and time.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_DST, noBias, lonMatch },
-                { LON_NO_DST_TOTAL_OFFSET, notDst, WHEN_NO_DST, noBias, lonMatch },
-
-                // Some lookup failure cases where the total offset, isDst and time do not match the
-                // zone.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_NO_DST, noBias, noMatch },
-                { LON_DST_TOTAL_OFFSET, notDst, WHEN_NO_DST, noBias, noMatch },
-                { LON_NO_DST_TOTAL_OFFSET, isDst, WHEN_DST, noBias, noMatch },
-                { LON_NO_DST_TOTAL_OFFSET, isDst, WHEN_NO_DST, noBias, noMatch },
-                { LON_DST_TOTAL_OFFSET, notDst, WHEN_DST, noBias, noMatch },
-                { LON_NO_DST_TOTAL_OFFSET, notDst, WHEN_DST, noBias, noMatch },
-
-                // Some bias cases below.
-
-                // The bias is irrelevant here: it matches what would be returned anyway.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_DST, LON_TZ, lonMatch },
-                { LON_NO_DST_TOTAL_OFFSET, notDst, WHEN_NO_DST, LON_TZ, lonMatch },
-
-                // A sample of a non-matching case with bias.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_NO_DST, LON_TZ, noMatch },
-
-                // The bias should be ignored: it doesn't match any of the country's zones.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_DST, NY_TZ, lonMatch },
-
-                // The bias should still be ignored even though it matches the offset information
-                // given it doesn't match any of the country's zones.
-                { NY_DST_TOTAL_OFFSET, isDst, WHEN_DST, NY_TZ, noMatch },
-        };
-        executeLookupByOffsetWithBiasTestCases(countryTimeZones, testCases);
-    }
-
-    @Test
-    public void lookupByOffsetWithBias_multipleNonOverlappingCandidates() throws Exception {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("America/New_York", "Europe/London"), "test");
-
-        OffsetResult lonMatch = new OffsetResult(LON_TZ, true /* oneMatch */);
-        OffsetResult nyMatch = new OffsetResult(NY_TZ, true /* oneMatch */);
-
-        // Placeholder constants to improve test case readability.
-        final Boolean isDst = true;
-        final Boolean notDst = false;
-        final Boolean unkIsDst = null;
-        final TimeZone noBias = null;
-        final OffsetResult noMatch = null;
-
-        Object[][] testCases = new Object[][] {
-                // totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias, expectedMatch
-
-                // The parameters match the zone: total offset and time.
-                { LON_DST_TOTAL_OFFSET, unkIsDst, WHEN_DST, noBias, lonMatch },
-                { LON_NO_DST_TOTAL_OFFSET, unkIsDst, WHEN_NO_DST, noBias, lonMatch },
-                { NY_NO_DST_TOTAL_OFFSET, unkIsDst, WHEN_NO_DST, noBias, nyMatch },
-                { NY_DST_TOTAL_OFFSET, unkIsDst, WHEN_DST, noBias, nyMatch },
-
-                // The parameters match the zone: total offset, isDst and time.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_DST, noBias, lonMatch },
-                { LON_NO_DST_TOTAL_OFFSET, notDst, WHEN_NO_DST, noBias, lonMatch },
-                { NY_DST_TOTAL_OFFSET, isDst, WHEN_DST, noBias, nyMatch },
-                { NY_NO_DST_TOTAL_OFFSET, notDst, WHEN_NO_DST, noBias, nyMatch },
-
-                // Some lookup failure cases where the total offset, isDst and time do not match the
-                // zone. This is a sample, not complete.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_NO_DST, noBias, noMatch },
-                { LON_DST_TOTAL_OFFSET, unkIsDst, WHEN_NO_DST, noBias, noMatch },
-                { LON_DST_TOTAL_OFFSET, notDst, WHEN_DST, noBias, noMatch },
-                { LON_NO_DST_TOTAL_OFFSET, isDst, WHEN_DST, noBias, noMatch },
-                { LON_NO_DST_TOTAL_OFFSET, isDst, WHEN_NO_DST, noBias, noMatch },
-                { LON_NO_DST_TOTAL_OFFSET, unkIsDst, WHEN_DST, noBias, noMatch },
-                { LON_NO_DST_TOTAL_OFFSET, notDst, WHEN_DST, noBias, noMatch },
-
-                // Some bias cases below.
-
-                // The bias is irrelevant here: it matches what would be returned anyway.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_DST, LON_TZ, lonMatch },
-                { LON_DST_TOTAL_OFFSET, unkIsDst, WHEN_DST, LON_TZ, lonMatch },
-                { LON_NO_DST_TOTAL_OFFSET, unkIsDst, WHEN_NO_DST, LON_TZ, lonMatch },
-
-                // A sample of non-matching cases with bias.
-                { LON_NO_DST_TOTAL_OFFSET, isDst, WHEN_NO_DST, LON_TZ, noMatch },
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_NO_DST, LON_TZ, noMatch },
-                { LON_DST_TOTAL_OFFSET, unkIsDst, WHEN_NO_DST, LON_TZ, noMatch },
-
-                // The bias should be ignored: it matches a zone, but the offset is wrong so
-                // should not be considered a match.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_DST, NY_TZ, lonMatch },
-                { LON_DST_TOTAL_OFFSET, unkIsDst, WHEN_DST, NY_TZ, lonMatch },
-        };
-        executeLookupByOffsetWithBiasTestCases(countryTimeZones, testCases);
-    }
-
-    // This is an artificial case very similar to America/Denver and America/Phoenix in the US: both
-    // have the same offset for 6 months of the year but diverge. Australia/Lord_Howe too.
-    @Test
-    public void lookupByOffsetWithBias_multipleOverlappingCandidates() throws Exception {
-        // Three zones that have the same offset for some of the year. Europe/London changes
-        // offset WHEN_DST, the others do not.
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Atlantic/Reykjavik", "Europe/London", "Etc/UTC"), "test");
-
-        // Placeholder constants to improve test case readability.
-        final Boolean isDst = true;
-        final Boolean notDst = false;
-        final Boolean unkIsDst = null;
-        final TimeZone noBias = null;
-        final OffsetResult noMatch = null;
-
-        // This is the no-DST offset for LON_TZ, REYK_TZ. UTC_TZ.
-        final int noDstTotalOffset = LON_NO_DST_TOTAL_OFFSET;
-        // This is the DST offset for LON_TZ.
-        final int dstTotalOffset = LON_DST_TOTAL_OFFSET;
-
-        OffsetResult lonOnlyMatch = new OffsetResult(LON_TZ, true /* oneMatch */);
-        OffsetResult lonBestMatch = new OffsetResult(LON_TZ, false /* oneMatch */);
-        OffsetResult reykBestMatch = new OffsetResult(REYK_TZ, false /* oneMatch */);
-        OffsetResult utcBestMatch = new OffsetResult(UTC_TZ, false /* oneMatch */);
-
-        Object[][] testCases = new Object[][] {
-                // totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias, expectedMatch
-
-                // The parameters match one zone: total offset and time.
-                { dstTotalOffset, unkIsDst, WHEN_DST, noBias, lonOnlyMatch },
-                { dstTotalOffset, unkIsDst, WHEN_DST, noBias, lonOnlyMatch },
-
-                // The parameters match several zones: total offset and time.
-                { noDstTotalOffset, unkIsDst, WHEN_NO_DST, noBias, reykBestMatch },
-                { noDstTotalOffset, unkIsDst, WHEN_DST, noBias, reykBestMatch },
-
-                // The parameters match one zone: total offset, isDst and time.
-                { dstTotalOffset, isDst, WHEN_DST, noBias, lonOnlyMatch },
-                { dstTotalOffset, isDst, WHEN_DST, noBias, lonOnlyMatch },
-
-                { noDstTotalOffset, notDst, WHEN_NO_DST, noBias, reykBestMatch },
-                { noDstTotalOffset, notDst, WHEN_DST, noBias, reykBestMatch },
-
-                // Some lookup failure cases where the total offset, isDst and time do not match any
-                // zone.
-                { dstTotalOffset, isDst, WHEN_NO_DST, noBias, noMatch },
-                { dstTotalOffset, unkIsDst, WHEN_NO_DST, noBias, noMatch },
-                { noDstTotalOffset, isDst, WHEN_NO_DST, noBias, noMatch },
-                { noDstTotalOffset, isDst, WHEN_DST, noBias, noMatch },
-
-                // Some bias cases below.
-
-                // Multiple zones match but Reykjavik is the bias.
-                { noDstTotalOffset, notDst, WHEN_NO_DST, REYK_TZ, reykBestMatch },
-
-                // Multiple zones match but London is the bias.
-                { noDstTotalOffset, notDst, WHEN_NO_DST, LON_TZ, lonBestMatch },
-
-                // Multiple zones match but UTC is the bias.
-                { noDstTotalOffset, notDst, WHEN_NO_DST, UTC_TZ, utcBestMatch },
-
-                // The bias should be ignored: it matches a zone, but the offset is wrong so
-                // should not be considered a match.
-                { LON_DST_TOTAL_OFFSET, isDst, WHEN_DST, REYK_TZ, lonOnlyMatch },
-                { LON_DST_TOTAL_OFFSET, unkIsDst, WHEN_DST, REYK_TZ, lonOnlyMatch },
-        };
-        executeLookupByOffsetWithBiasTestCases(countryTimeZones, testCases);
-    }
-
-    private static void executeLookupByOffsetWithBiasTestCases(
-            CountryTimeZones countryTimeZones, Object[][] testCases) {
-
-        List<String> failures = new ArrayList<>();
-        for (int i = 0; i < testCases.length; i++) {
-            Object[] testCase = testCases[i];
-            int totalOffsetMillis = (int) testCase[0];
-            Boolean isDst = (Boolean) testCase[1];
-            long whenMillis = (Long) testCase[2];
-            TimeZone bias = (TimeZone) testCase[3];
-            OffsetResult expectedMatch = (OffsetResult) testCase[4];
-
-            OffsetResult actualMatch;
-            if (isDst == null) {
-                actualMatch = countryTimeZones.lookupByOffsetWithBias(
-                        whenMillis, bias, totalOffsetMillis);
-            } else {
-                actualMatch = countryTimeZones.lookupByOffsetWithBias(
-                        whenMillis, bias, totalOffsetMillis, isDst);
-            }
-
-            if (!offsetResultEquals(expectedMatch, actualMatch)) {
-                Function<TimeZone, String> timeZoneFormatter =
-                        x -> x == null ? "null" : x.getID();
-                Function<OffsetResult, String> offsetResultFormatter =
-                        x -> x == null ? "null"
-                                : "{" + x.getTimeZone().getID() + ", " + x.isOnlyMatch() + "}";
-                failures.add("Fail: case=" + i
-                        + ", totalOffsetMillis=" + totalOffsetMillis
-                        + ", isDst=" + isDst
-                        + ", whenMillis=" + whenMillis
-                        + ", bias=" + timeZoneFormatter.apply(bias)
-                        + ", expectedMatch=" + offsetResultFormatter.apply(expectedMatch)
-                        + ", actualMatch=" + offsetResultFormatter.apply(actualMatch)
-                        + "\n");
-            }
-        }
-        if (!failures.isEmpty()) {
-            fail("Failed:\n" + failures);
-        }
-    }
-
-    @Test
-    public void getEffectiveTimeZonesAt_noZones() {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings(), "test");
-        assertEquals(timeZoneMappings(),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(0 /* whenMillis */));
-        assertEquals(timeZoneMappings(),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MIN_VALUE));
-        assertEquals(timeZoneMappings(),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MAX_VALUE));
-    }
-
-    @Test
-    public void getEffectiveTimeZonesAt_oneZone() {
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-        assertEquals(timeZoneMappings("Europe/London"),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(0));
-        assertEquals(timeZoneMappings("Europe/London"),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MIN_VALUE));
-        assertEquals(timeZoneMappings("Europe/London"),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MAX_VALUE));
-    }
-
-    @Test
-    public void getEffectiveTimeZonesAt_filtering() {
-        TimeZoneMapping alwaysUsed = timeZoneMapping("Europe/London", null /* notUsedAfter */);
-
-        long mappingNotUsedAfterMillis = 0L;
-        TimeZoneMapping notAlwaysUsed = timeZoneMapping("Europe/Paris",
-                mappingNotUsedAfterMillis /* notUsedAfter */);
-
-        List<TimeZoneMapping> timeZoneMappings = list(alwaysUsed, notAlwaysUsed);
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings, "test");
-
-        // Before and at mappingNotUsedAfterMillis, both mappings are "effective".
-        assertEquals(list(alwaysUsed, notAlwaysUsed),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MIN_VALUE));
-        assertEquals(list(alwaysUsed, notAlwaysUsed),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(mappingNotUsedAfterMillis));
-
-        // The following should filter the second mapping because it's not "effective" after
-        // mappingNotUsedAfterMillis.
-        assertEquals(list(alwaysUsed),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(mappingNotUsedAfterMillis + 1));
-        assertEquals(list(alwaysUsed),
-                countryTimeZones.getEffectiveTimeZoneMappingsAt(Long.MAX_VALUE));
-    }
-
-    @Test
-    public void hasUtcZone_everUseUtcHintOverridesZoneInformation() {
-        // The country has a single zone. Europe/London uses UTC in Winter.
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Etc/UTC", false /* defaultTimeZoneBoost */, false /* everUsesUtc */,
-                timeZoneMappings("Etc/UTC"), "test");
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_NO_DST));
-    }
-
-    @Test
-    public void hasUtcZone_singleZone() {
-        // The country has a single zone. Europe/London uses UTC in Winter.
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
-        assertTrue(countryTimeZones.hasUtcZone(WHEN_NO_DST));
-    }
-
-    @Test
-    public void hasUtcZone_multipleZonesWithUtc() {
-        // The country has multiple zones. Europe/London uses UTC in Winter.
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "America/Los_Angeles", false /* defaultTimeZoneBoost */,
-                true /* everUsesUtc */,
-                timeZoneMappings("America/Los_Angeles", "America/New_York", "Europe/London"),
-                "test");
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
-        assertTrue(countryTimeZones.hasUtcZone(WHEN_NO_DST));
-    }
-
-    @Test
-    public void hasUtcZone_multipleZonesWithoutUtc() {
-        // The country has multiple zones, none of which use UTC.
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", "Europe/Paris", false /* defaultTimeZoneBoost */, false /* everUsesUtc */,
-                timeZoneMappings("America/Los_Angeles", "America/New_York", "Europe/Paris"),
-                "test");
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_NO_DST));
-    }
-
-    @Test
-    public void hasUtcZone_emptyZones() {
-        // The country has no valid zones.
-        CountryTimeZones countryTimeZones = CountryTimeZones.createValidated(
-                "xx", INVALID_TZ_ID, false /* defaultTimeZoneBoost */, false /* everUsesUtc */,
-                timeZoneMappings(INVALID_TZ_ID), "test");
-        assertTrue(countryTimeZones.getTimeZoneMappings().isEmpty());
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_DST));
-        assertFalse(countryTimeZones.hasUtcZone(WHEN_NO_DST));
-    }
-
-    @Test
-    public void timeZoneMapping_getTimeZone_badZoneId() {
-        TimeZoneMapping timeZoneMapping =
-                TimeZoneMapping.createForTests("DOES_NOT_EXIST", true, 1234L);
-        try {
-            timeZoneMapping.getTimeZone();
-            fail();
-        } catch (RuntimeException expected) {
-        }
-    }
-
-    @Test
-    public void timeZoneMapping_getTimeZone_validZoneId() {
-        TimeZoneMapping timeZoneMapping =
-                TimeZoneMapping.createForTests("Europe/London", true, 1234L);
-        TimeZone timeZone = timeZoneMapping.getTimeZone();
-        assertTrue(timeZone.isFrozen());
-        assertEquals("Europe/London", timeZone.getID());
-    }
-
-    private void assertImmutableTimeZone(TimeZone timeZone) {
-        try {
-            timeZone.setRawOffset(1000);
-            fail();
-        } catch (UnsupportedOperationException expected) {
-        }
-    }
-
-    private static <X> void assertImmutableList(List<X> list) {
-        try {
-            list.add(null);
-            fail();
-        } catch (UnsupportedOperationException expected) {
-        }
-    }
-
-    private static void assertZoneEquals(TimeZone expected, TimeZone actual) {
-        // TimeZone.equals() only checks the ID, but that's ok for these tests.
-        assertEquals(expected, actual);
-    }
-
-    private static boolean offsetResultEquals(OffsetResult expected, OffsetResult actual) {
-        return expected == actual
-                || (expected != null && actual != null
-                && Objects.equals(expected.getTimeZone().getID(), actual.getTimeZone().getID())
-                && expected.isOnlyMatch() == actual.isOnlyMatch());
-    }
-
-    /**
-     * Creates a list of default {@link TimeZoneMapping} objects with the specified time zone IDs.
-     */
-    private static TimeZoneMapping timeZoneMapping(String timeZoneId, Long notUsedAfterMillis) {
-        return TimeZoneMapping.createForTests(
-                        timeZoneId, true /* picker */, notUsedAfterMillis);
-    }
-
-    /**
-     * Creates a list of default {@link TimeZoneMapping} objects with the specified time zone IDs.
-     */
-    private static List<TimeZoneMapping> timeZoneMappings(String... timeZoneIds) {
-        return Arrays.stream(timeZoneIds)
-                .map(x -> TimeZoneMapping.createForTests(
-                        x, true /* picker */, null /* notUsedAfter */))
-                .collect(Collectors.toList());
-    }
-
-    private static TimeZone zone(String id) {
-        return TimeZone.getFrozenTimeZone(id);
-    }
-
-    private static <X> List<X> list(X... xes) {
-        return Arrays.asList(xes);
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/timezone/CountryZonesFinderTest.java b/luni/src/test/java/libcore/libcore/timezone/CountryZonesFinderTest.java
deleted file mode 100644
index ecd5201..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/CountryZonesFinderTest.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2018 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.libcore.timezone;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-import libcore.timezone.CountryTimeZones;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-import libcore.timezone.CountryZonesFinder;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.fail;
-
-public class CountryZonesFinderTest {
-
-    private static final CountryTimeZones GB_ZONES = CountryTimeZones.createValidated(
-            "gb", "Europe/London", false /* defaultTimeZoneBoost */, true,
-            timeZoneMappings("Europe/London"), "test");
-
-    private static final CountryTimeZones IM_ZONES = CountryTimeZones.createValidated(
-            "im", "Europe/London", false /* defaultTimeZoneBoost */, true,
-            timeZoneMappings("Europe/London"), "test");
-
-    private static final CountryTimeZones FR_ZONES = CountryTimeZones.createValidated(
-            "fr", "Europe/Paris", false /* defaultTimeZoneBoost */, true,
-            timeZoneMappings("Europe/Paris"), "test");
-
-    private static final CountryTimeZones US_ZONES = CountryTimeZones.createValidated(
-            "us", "America/New_York", false /* defaultTimeZoneBoost */, true,
-            timeZoneMappings("America/New_York", "America/Los_Angeles"), "test");
-
-    @Test
-    public void lookupAllCountryIsoCodes() throws Exception {
-        CountryZonesFinder countryZonesFinder =
-                CountryZonesFinder.createForTests(list(GB_ZONES, FR_ZONES));
-
-        List<String> isoList = countryZonesFinder.lookupAllCountryIsoCodes();
-        assertEqualsAndImmutable(list(GB_ZONES.getCountryIso(), FR_ZONES.getCountryIso()), isoList);
-    }
-
-    @Test
-    public void lookupAllCountryIsoCodes_empty() throws Exception {
-        CountryZonesFinder countryZonesFinder = CountryZonesFinder.createForTests(list());
-        List<String> isoList = countryZonesFinder.lookupAllCountryIsoCodes();
-        assertEqualsAndImmutable(list(), isoList);
-    }
-
-    @Test
-    public void lookupCountryCodesForZoneId() throws Exception {
-        CountryZonesFinder countryZonesFinder =
-                CountryZonesFinder.createForTests(list(GB_ZONES, IM_ZONES, FR_ZONES, US_ZONES));
-
-        assertEqualsAndImmutable(list(GB_ZONES, IM_ZONES),
-                countryZonesFinder.lookupCountryTimeZonesForZoneId("Europe/London"));
-        assertEqualsAndImmutable(list(US_ZONES),
-                countryZonesFinder.lookupCountryTimeZonesForZoneId("America/New_York"));
-        assertEqualsAndImmutable(list(US_ZONES),
-                countryZonesFinder.lookupCountryTimeZonesForZoneId("America/Los_Angeles"));
-        assertEqualsAndImmutable(list(),
-                countryZonesFinder.lookupCountryTimeZonesForZoneId("DOES_NOT_EXIST"));
-    }
-
-    @Test
-    public void lookupCountryTimeZones() throws Exception {
-        CountryZonesFinder countryZonesFinder =
-                CountryZonesFinder.createForTests(list(GB_ZONES, IM_ZONES, FR_ZONES, US_ZONES));
-        assertSame(GB_ZONES, countryZonesFinder.lookupCountryTimeZones(GB_ZONES.getCountryIso()));
-        assertSame(IM_ZONES, countryZonesFinder.lookupCountryTimeZones(IM_ZONES.getCountryIso()));
-        assertNull(countryZonesFinder.lookupCountryTimeZones("DOES_NOT_EXIST"));
-    }
-
-    private static <X> void assertEqualsAndImmutable(List<X> expected, List<X> actual) {
-        assertEquals(expected, actual);
-        assertImmutableList(actual);
-    }
-
-    private static <X> void assertImmutableList(List<X> list) {
-        try {
-            list.add(null);
-            fail();
-        } catch (UnsupportedOperationException expected) {
-        }
-    }
-
-    private static <X> List<X> list(X... values) {
-        return Arrays.asList(values);
-    }
-
-    /**
-     * Creates a list of default {@link TimeZoneMapping} objects with the specified time zone IDs.
-     */
-    private static List<TimeZoneMapping> timeZoneMappings(String... timeZoneIds) {
-        return Arrays.stream(timeZoneIds)
-                .map(x -> TimeZoneMapping.createForTests(
-                        x, true /* picker */, null /* notUsedAfter */))
-                .collect(Collectors.toList());
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/timezone/TelephonyLookupTest.java b/luni/src/test/java/libcore/libcore/timezone/TelephonyLookupTest.java
deleted file mode 100644
index 8b4d867..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/TelephonyLookupTest.java
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.libcore.timezone;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import libcore.timezone.TelephonyLookup;
-import libcore.timezone.TelephonyNetwork;
-import libcore.timezone.TelephonyNetworkFinder;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-public class TelephonyLookupTest {
-
-    private Path testDir;
-
-    @Before
-    public void setUp() throws Exception {
-        testDir = Files.createTempDirectory("TelephonyLookupTest");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        // Delete the testDir and all contents.
-        Files.walkFileTree(testDir, new SimpleFileVisitor<Path>() {
-            @Override
-            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
-                    throws IOException {
-                Files.delete(file);
-                return FileVisitResult.CONTINUE;
-            }
-
-            @Override
-            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
-                    throws IOException {
-                Files.delete(dir);
-                return FileVisitResult.CONTINUE;
-            }
-        });
-    }
-
-    @Test
-    public void createInstanceWithFallback() throws Exception {
-        String validXml1 = "<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n";
-        TelephonyNetwork expectedTelephonyNetwork1 =
-                TelephonyNetwork.create("123", "456", "gb");
-
-        String validXml2 = "<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"234\" mnc=\"567\" country=\"fr\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n";
-        TelephonyNetwork expectedTelephonyNetwork2 =
-                TelephonyNetwork.create("234", "567", "fr");
-
-        String invalidXml = "<foo></foo>\n";
-        checkValidateThrowsParserException(invalidXml);
-
-        String validFile1 = createFile(validXml1);
-        String validFile2 = createFile(validXml2);
-        String invalidFile = createFile(invalidXml);
-        String missingFile = createMissingFile();
-
-        TelephonyLookup file1ThenFile2 =
-                TelephonyLookup.createInstanceWithFallback(validFile1, validFile2);
-        assertEquals(list(expectedTelephonyNetwork1),
-                file1ThenFile2.getTelephonyNetworkFinder().getAll());
-
-        TelephonyLookup missingFileThenFile1 =
-                TelephonyLookup.createInstanceWithFallback(missingFile, validFile1);
-        assertEquals(list(expectedTelephonyNetwork1),
-                missingFileThenFile1.getTelephonyNetworkFinder().getAll());
-
-        TelephonyLookup file2ThenFile1 =
-                TelephonyLookup.createInstanceWithFallback(validFile2, validFile1);
-        assertEquals(list(expectedTelephonyNetwork2),
-                file2ThenFile1.getTelephonyNetworkFinder().getAll());
-
-        // We assume the file has been validated so an invalid file is not checked ahead of time.
-        // We will find out when we look something up.
-        TelephonyLookup invalidThenValid =
-                TelephonyLookup.createInstanceWithFallback(invalidFile, validFile1);
-        assertNull(invalidThenValid.getTelephonyNetworkFinder());
-
-        // This is not a normal case: It would imply a device shipped without a file anywhere!
-        TelephonyLookup missingFiles =
-                TelephonyLookup.createInstanceWithFallback(missingFile, missingFile);
-        assertEmpty(missingFiles.getTelephonyNetworkFinder().getAll());
-    }
-
-    @Test
-    public void xmlParsing_emptyFile() throws Exception {
-        checkValidateThrowsParserException("");
-    }
-
-    @Test
-    public void xmlParsing_unexpectedRootElement() throws Exception {
-        checkValidateThrowsParserException("<foo></foo>\n");
-    }
-
-    @Test
-    public void xmlParsing_missingNetworks() throws Exception {
-        checkValidateThrowsParserException("<telephony_lookup></telephony_lookup>\n");
-    }
-
-    @Test
-    public void xmlParsing_emptyNetworksOk() throws Exception {
-        {
-            TelephonyLookup telephonyLookup =
-                    validate("<telephony_lookup>\n"
-                            + "  <networks>\n"
-                            + "  </networks>\n"
-                            + "</telephony_lookup>\n");
-            TelephonyNetworkFinder telephonyNetworkFinder = telephonyLookup
-                    .getTelephonyNetworkFinder();
-            assertEquals(list(), telephonyNetworkFinder.getAll());
-        }
-        {
-            TelephonyLookup telephonyLookup =
-                    validate("<telephony_lookup>\n"
-                            + "  <networks/>\n"
-                            + "</telephony_lookup>\n");
-            TelephonyNetworkFinder telephonyNetworkFinder = telephonyLookup
-                    .getTelephonyNetworkFinder();
-            assertEquals(list(), telephonyNetworkFinder.getAll());
-        }
-    }
-
-    @Test
-    public void xmlParsing_unexpectedComments() throws Exception {
-        TelephonyNetwork expectedTelephonyNetwork =
-                TelephonyNetwork.create("123", "456", "gb");
-
-        TelephonyLookup telephonyLookup = validate("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <!-- This is a comment -->"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-        assertEquals(list(expectedTelephonyNetwork), telephonyLookup.getTelephonyNetworkFinder().getAll());
-    }
-
-    @Test
-    public void xmlParsing_unexpectedElementsIgnored() throws Exception {
-        TelephonyNetwork expectedTelephonyNetwork =
-                TelephonyNetwork.create("123", "456", "gb");
-        List<TelephonyNetwork> expectedNetworks = list(expectedTelephonyNetwork);
-
-        String unexpectedElement = "<unexpected-element>\n<a /></unexpected-element>\n";
-
-        // These tests are important because they ensure we can extend the format in future with
-        // more information but could continue using the same file on older devices.
-        TelephonyLookup telephonyLookup = validate("<telephony_lookup>\n"
-                + " " + unexpectedElement
-                + "  <networks>\n"
-                + "    " + unexpectedElement
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n"
-                + "    " + unexpectedElement
-                + "  </networks>\n"
-                + "  " + unexpectedElement
-                + "</telephony_lookup>\n");
-        assertEquals(expectedNetworks, telephonyLookup.getTelephonyNetworkFinder().getAll());
-
-        telephonyLookup = validate("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\">\n"
-                + "    " + unexpectedElement
-                + "    </network>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-        assertEquals(expectedNetworks, telephonyLookup.getTelephonyNetworkFinder().getAll());
-
-        expectedNetworks = list(expectedTelephonyNetwork,
-                TelephonyNetwork.create("234", "567", "fr"));
-        telephonyLookup = validate("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n"
-                + "    " + unexpectedElement
-                + "    <network mcc=\"234\" mnc=\"567\" country=\"fr\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-        assertEquals(expectedNetworks, telephonyLookup.getTelephonyNetworkFinder().getAll());
-    }
-
-    @Test
-    public void xmlParsing_unexpectedTextIgnored() throws Exception {
-        TelephonyNetwork expectedTelephonyNetwork =
-                TelephonyNetwork.create("123", "456", "gb");
-        List<TelephonyNetwork> expectedNetworks = list(expectedTelephonyNetwork);
-
-        String unexpectedText = "unexpected-text";
-        TelephonyLookup telephonyLookup = validate("<telephony_lookup>\n"
-                + "  " + unexpectedText
-                + "  <networks>\n"
-                + "  " + unexpectedText
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n"
-                + "    " + unexpectedText
-                + "  </networks>\n"
-                + "  " + unexpectedText
-                + "</telephony_lookup>\n");
-        assertEquals(expectedNetworks, telephonyLookup.getTelephonyNetworkFinder().getAll());
-
-        telephonyLookup = validate("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\">\n"
-                + "      " + unexpectedText
-                + "    </network>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-        assertEquals(expectedNetworks, telephonyLookup.getTelephonyNetworkFinder().getAll());
-    }
-
-    @Test
-    public void xmlParsing_truncatedInput() throws Exception {
-        checkValidateThrowsParserException("<telephony_lookup>\n");
-
-        checkValidateThrowsParserException("<telephony_lookup>\n"
-                + "  <networks>\n");
-
-        checkValidateThrowsParserException("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n");
-
-        checkValidateThrowsParserException("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n"
-                + "  </networks>\n");
-    }
-
-    @Test
-    public void validateDuplicateMccMnc() {
-        checkValidateThrowsParserException("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" countryCode=\"gb\"/>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" countryCode=\"fr\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-    }
-
-    @Test
-    public void validateCountryCodeLowerCase() {
-        checkValidateThrowsParserException("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" countryCode=\"GB\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-    }
-
-
-    @Test
-    public void getTelephonyNetworkFinder() throws Exception {
-        TelephonyLookup telephonyLookup = TelephonyLookup.createInstanceForTests(
-                "<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\" country=\"gb\"/>\n"
-                + "    <network mcc=\"234\" mnc=\"567\" country=\"fr\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-
-        TelephonyNetworkFinder telephonyNetworkFinder = telephonyLookup.getTelephonyNetworkFinder();
-        TelephonyNetwork expectedNetwork1 = TelephonyNetwork.create("123", "456", "gb");
-        TelephonyNetwork expectedNetwork2 = TelephonyNetwork.create("234", "567", "fr");
-        assertEquals(list(expectedNetwork1, expectedNetwork2), telephonyNetworkFinder.getAll());
-        assertEquals(expectedNetwork1, telephonyNetworkFinder.findNetworkByMccMnc("123", "456"));
-        assertEquals(expectedNetwork2, telephonyNetworkFinder.findNetworkByMccMnc("234", "567"));
-        assertNull(telephonyNetworkFinder.findNetworkByMccMnc("999", "999"));
-    }
-
-    @Test
-    public void xmlParsing_missingMccAttribute() throws Exception {
-        checkValidateThrowsParserException("<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mnc=\"456\" country=\"gb\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-    }
-
-    @Test
-    public void xmlParsing_missingMncAttribute() throws Exception {
-        TelephonyLookup telephonyLookup = TelephonyLookup.createInstanceForTests(
-                "<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" country=\"gb\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-        assertNull(telephonyLookup.getTelephonyNetworkFinder());
-    }
-
-    @Test
-    public void xmlParsing_missingCountryCodeAttribute() throws Exception {
-        TelephonyLookup telephonyLookup = TelephonyLookup.createInstanceForTests(
-                "<telephony_lookup>\n"
-                + "  <networks>\n"
-                + "    <network mcc=\"123\" mnc=\"456\"/>\n"
-                + "  </networks>\n"
-                + "</telephony_lookup>\n");
-        assertNull(telephonyLookup.getTelephonyNetworkFinder());
-    }
-
-    private static void checkValidateThrowsParserException(String xml) {
-        try {
-            validate(xml);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    private static TelephonyLookup validate(String xml) throws IOException {
-        TelephonyLookup telephonyLookup = TelephonyLookup.createInstanceForTests(xml);
-        telephonyLookup.validate();
-        return telephonyLookup;
-    }
-
-    private static void assertEmpty(Collection<?> collection) {
-        assertTrue("Expected empty:" + collection, collection.isEmpty());
-    }
-
-    private static <X> List<X> list(X... values) {
-        return Arrays.asList(values);
-    }
-
-    private String createFile(String fileContent) throws IOException {
-        Path filePath = Files.createTempFile(testDir, null, null);
-        Files.write(filePath, fileContent.getBytes(StandardCharsets.UTF_8));
-        return filePath.toString();
-    }
-
-    private String createMissingFile() throws IOException {
-        Path filePath = Files.createTempFile(testDir, null, null);
-        Files.delete(filePath);
-        return filePath.toString();
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/timezone/TelephonyNetworkFinderTest.java b/luni/src/test/java/libcore/libcore/timezone/TelephonyNetworkFinderTest.java
deleted file mode 100644
index 44dfc81..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/TelephonyNetworkFinderTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.libcore.timezone;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import libcore.timezone.TelephonyNetwork;
-import libcore.timezone.TelephonyNetworkFinder;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.List;
-
-public class TelephonyNetworkFinderTest {
-
-    @Test
-    public void testCreateAndLookups() {
-        TelephonyNetwork network = TelephonyNetwork.create("123", "456", "gb");
-        List<TelephonyNetwork> networkList = list(network);
-        TelephonyNetworkFinder finder = TelephonyNetworkFinder.create(networkList);
-        assertEquals(network, finder.findNetworkByMccMnc("123", "456"));
-        assertNull(finder.findNetworkByMccMnc("XXX", "XXX"));
-        assertNull(finder.findNetworkByMccMnc("123", "XXX"));
-        assertNull(finder.findNetworkByMccMnc("456", "123"));
-        assertNull(finder.findNetworkByMccMnc("111", "222"));
-        assertEquals(networkList, finder.getAll());
-    }
-
-    private static <X> List<X> list(X... values) {
-        return Arrays.asList(values);
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/timezone/TimeZoneDataFilesTest.java b/luni/src/test/java/libcore/libcore/timezone/TimeZoneDataFilesTest.java
deleted file mode 100644
index a74ba30..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/TimeZoneDataFilesTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.libcore.timezone;
-
-import org.junit.Test;
-
-import libcore.timezone.TimeZoneDataFiles;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class TimeZoneDataFilesTest {
-
-    private static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT";
-    private static final String ANDROID_I18N_ROOT_ENV = "ANDROID_I18N_ROOT";
-    private static final String ANDROID_DATA_ENV = "ANDROID_DATA";
-
-    @Test
-    public void expectedEnvironmentVariables() {
-        // These environment variables are required to locate data files used by libcore / ICU.
-        assertNotNull(System.getenv(ANDROID_DATA_ENV));
-        assertNotNull(System.getenv(ANDROID_TZDATA_ROOT_ENV));
-        assertNotNull(System.getenv(ANDROID_I18N_ROOT_ENV));
-    }
-
-    @Test
-    public void getTimeZoneFilePaths() {
-        String[] paths = TimeZoneDataFiles.getTimeZoneFilePaths("foo");
-        assertEquals(2, paths.length);
-
-        assertTrue(paths[0].startsWith(System.getenv(ANDROID_DATA_ENV)));
-        assertTrue(paths[0].contains("/misc/zoneinfo/current/"));
-        assertTrue(paths[0].endsWith("/foo"));
-
-        assertTrue(paths[1].startsWith(System.getenv(ANDROID_TZDATA_ROOT_ENV)));
-        assertTrue(paths[1].endsWith("/foo"));
-    }
-
-    // http://b/34867424
-    @Test
-    public void generateIcuDataPath_includesTimeZoneOverride() {
-        String icuDataPath = System.getProperty("android.icu.impl.ICUBinary.dataPath");
-        assertEquals(icuDataPath, TimeZoneDataFiles.generateIcuDataPath());
-
-        String[] paths = icuDataPath.split(":");
-        assertEquals(3, paths.length);
-
-        String dataDirPath = paths[0];
-        assertTrue(dataDirPath.startsWith(System.getenv(ANDROID_DATA_ENV)));
-        assertTrue(dataDirPath + " invalid", dataDirPath.contains("/misc/zoneinfo/current/icu"));
-
-        String tzdataModulePath = paths[1];
-        assertTrue(tzdataModulePath + " invalid",
-                tzdataModulePath.startsWith(System.getenv(ANDROID_TZDATA_ROOT_ENV)));
-
-        String runtimeModulePath = paths[2];
-        assertTrue(runtimeModulePath + " invalid",
-                runtimeModulePath.startsWith(System.getenv(ANDROID_I18N_ROOT_ENV)));
-        assertTrue(runtimeModulePath + " invalid", runtimeModulePath.contains("/etc/icu"));
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/timezone/TimeZoneFinderTest.java b/luni/src/test/java/libcore/libcore/timezone/TimeZoneFinderTest.java
deleted file mode 100644
index ef07371..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/TimeZoneFinderTest.java
+++ /dev/null
@@ -1,657 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.libcore.timezone;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-import libcore.timezone.CountryTimeZones;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-import libcore.timezone.CountryZonesFinder;
-import libcore.timezone.TimeZoneFinder;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class TimeZoneFinderTest {
-
-    private Path testDir;
-
-    @Before
-    public void setUp() throws Exception {
-        testDir = Files.createTempDirectory("TimeZoneFinderTest");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        // Delete the testDir and all contents.
-        Files.walkFileTree(testDir, new SimpleFileVisitor<Path>() {
-            @Override
-            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
-                    throws IOException {
-                Files.delete(file);
-                return FileVisitResult.CONTINUE;
-            }
-
-            @Override
-            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
-                    throws IOException {
-                Files.delete(dir);
-                return FileVisitResult.CONTINUE;
-            }
-        });
-    }
-
-    @Test
-    public void createInstanceWithFallback() throws Exception {
-        String validXml1 = "<timezones ianaversion=\"2017c\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n";
-        CountryTimeZones expectedCountryTimeZones1 = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-
-        String validXml2 = "<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/Paris\" everutc=\"n\">\n"
-                + "      <id>Europe/Paris</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n";
-        CountryTimeZones expectedCountryTimeZones2 = CountryTimeZones.createValidated(
-                "gb", "Europe/Paris", false /* defaultTimeZoneBoost */, false /* everUsesUtc */,
-                timeZoneMappings("Europe/Paris"), "test");
-
-        String invalidXml = "<foo></foo>\n";
-        checkValidateThrowsParserException(invalidXml);
-
-        String validFile1 = createFile(validXml1);
-        String validFile2 = createFile(validXml2);
-        String invalidFile = createFile(invalidXml);
-        String missingFile = createMissingFile();
-
-        TimeZoneFinder file1ThenFile2 =
-                TimeZoneFinder.createInstanceWithFallback(validFile1, validFile2);
-        assertEquals("2017c", file1ThenFile2.getIanaVersion());
-        assertEquals(expectedCountryTimeZones1, file1ThenFile2.lookupCountryTimeZones("gb"));
-
-        TimeZoneFinder missingFileThenFile1 =
-                TimeZoneFinder.createInstanceWithFallback(missingFile, validFile1);
-        assertEquals("2017c", missingFileThenFile1.getIanaVersion());
-        assertEquals(expectedCountryTimeZones1, missingFileThenFile1.lookupCountryTimeZones("gb"));
-
-        TimeZoneFinder file2ThenFile1 =
-                TimeZoneFinder.createInstanceWithFallback(validFile2, validFile1);
-        assertEquals("2017b", file2ThenFile1.getIanaVersion());
-        assertEquals(expectedCountryTimeZones2, file2ThenFile1.lookupCountryTimeZones("gb"));
-
-        // We assume the file has been validated so an invalid file is not checked ahead of time.
-        // We will find out when we look something up.
-        TimeZoneFinder invalidThenValid =
-                TimeZoneFinder.createInstanceWithFallback(invalidFile, validFile1);
-        assertNull(invalidThenValid.getIanaVersion());
-        assertNull(invalidThenValid.lookupCountryTimeZones("gb"));
-
-        // This is not a normal case: It would imply a device shipped without a file anywhere!
-        TimeZoneFinder missingFiles =
-                TimeZoneFinder.createInstanceWithFallback(missingFile, missingFile);
-        assertNull(missingFiles.getIanaVersion());
-        assertNull(missingFiles.lookupCountryTimeZones("gb"));
-    }
-
-    @Test
-    public void xmlParsing_emptyFile() throws Exception {
-        checkValidateThrowsParserException("");
-    }
-
-    @Test
-    public void xmlParsing_unexpectedRootElement() throws Exception {
-        checkValidateThrowsParserException("<foo></foo>\n");
-    }
-
-    @Test
-    public void xmlParsing_missingCountryZones() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\"></timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_noCountriesOk() throws Exception {
-        validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_unexpectedComments() throws Exception {
-        CountryTimeZones expectedCountryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <!-- This is a comment -->"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        // This is a crazy comment, but also helps prove that TEXT nodes are coalesced by the
-        // parser.
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/<!-- Don't freak out! -->London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-    }
-
-    @Test
-    public void xmlParsing_unexpectedElementsIgnored() throws Exception {
-        CountryTimeZones expectedCountryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-
-        String unexpectedElement = "<unexpected-element>\n<a /></unexpected-element>\n";
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  " + unexpectedElement
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    " + unexpectedElement
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      " + unexpectedElement
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "    " + unexpectedElement
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        // This test is important because it ensures we can extend the format in future with
-        // more information.
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "  " + unexpectedElement
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        expectedCountryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London", "Europe/Paris"), "test");
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "      " + unexpectedElement
-                + "      <id>Europe/Paris</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-    }
-
-    @Test
-    public void xmlParsing_unexpectedTextIgnored() throws Exception {
-        CountryTimeZones expectedCountryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-
-        String unexpectedText = "unexpected-text";
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  " + unexpectedText
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    " + unexpectedText
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      " + unexpectedText
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-
-        expectedCountryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London", "Europe/Paris"), "test");
-        finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "      " + unexpectedText
-                + "      <id>Europe/Paris</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-    }
-
-    @Test
-    public void xmlParsing_truncatedInput() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n");
-
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n");
-
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n");
-
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n");
-
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n");
-
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n");
-    }
-
-    @Test
-    public void xmlParsing_unexpectedChildInTimeZoneIdThrows() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id><unexpected-element /></id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_unknownTimeZoneIdIgnored() throws Exception {
-        CountryTimeZones expectedCountryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Unknown_Id</id>\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-    }
-
-    @Test
-    public void xmlParsing_missingCountryCode() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_missingCountryEverUtc() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_badCountryEverUtc() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"occasionally\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_missingCountryDefault() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_badCountryDefaultBoost() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" defaultBoost=\"nope\""
-                + "everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_countryDefaultBoost() throws Exception {
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" defaultBoost=\"y\""
-                + "everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        CountryTimeZones countryTimeZones = finder.lookupCountryTimeZones("gb");
-        assertTrue(countryTimeZones.isDefaultTimeZoneBoosted());
-    }
-
-    @Test
-    public void xmlParsing_badTimeZoneMappingPicker() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id picker=\"sometimes\">Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_timeZoneMappingPicker() throws Exception {
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"us\" default=\"America/New_York\" everutc=\"n\">\n"
-                + "      <!-- Explicit picker=\"y\" -->\n"
-                + "      <id picker=\"y\">America/New_York</id>\n"
-                + "      <!-- Implicit picker=\"y\" -->\n"
-                + "      <id>America/Los_Angeles</id>\n"
-                + "      <!-- Explicit picker=\"n\" -->\n"
-                + "      <id picker=\"n\">America/Indiana/Vincennes</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        CountryTimeZones usTimeZones = finder.lookupCountryTimeZones("us");
-        List<TimeZoneMapping> actualTimeZoneMappings = usTimeZones.getTimeZoneMappings();
-        List<TimeZoneMapping> expectedTimeZoneMappings = list(
-                TimeZoneMapping.createForTests(
-                        "America/New_York", true /* shownInPicker */, null /* notUsedAfter */),
-                TimeZoneMapping.createForTests(
-                        "America/Los_Angeles", true /* shownInPicker */, null /* notUsedAfter */),
-                TimeZoneMapping.createForTests(
-                        "America/Indiana/Vincennes", false /* shownInPicker */,
-                        null /* notUsedAfter */)
-        );
-        assertEquals(expectedTimeZoneMappings, actualTimeZoneMappings);
-    }
-
-    @Test
-    public void xmlParsing_badTimeZoneMappingNotAfter() throws Exception {
-        checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id notafter=\"sometimes\">Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-    }
-
-    @Test
-    public void xmlParsing_timeZoneMappingNotAfter() throws Exception {
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"us\" default=\"America/New_York\" everutc=\"n\">\n"
-                + "      <!-- Explicit notafter -->\n"
-                + "      <id notafter=\"1234\">America/New_York</id>\n"
-                + "      <!-- Missing notafter -->\n"
-                + "      <id>America/Indiana/Vincennes</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        CountryTimeZones usTimeZones = finder.lookupCountryTimeZones("us");
-        List<TimeZoneMapping> actualTimeZoneMappings = usTimeZones.getTimeZoneMappings();
-        List<TimeZoneMapping> expectedTimeZoneMappings = list(
-                TimeZoneMapping.createForTests(
-                        "America/New_York", true /* shownInPicker */, 1234L /* notUsedAfter */),
-                TimeZoneMapping.createForTests(
-                        "America/Indiana/Vincennes", true /* shownInPicker */,
-                        null /* notUsedAfter */)
-        );
-        assertEquals(expectedTimeZoneMappings, actualTimeZoneMappings);
-    }
-
-    @Test
-    public void getCountryZonesFinder() throws Exception {
-        TimeZoneFinder timeZoneFinder = TimeZoneFinder.createInstanceForTests(
-                "<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "    <country code=\"fr\" default=\"Europe/Paris\" everutc=\"y\">\n"
-                + "      <id>Europe/Paris</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        CountryTimeZones expectedGb = CountryTimeZones.createValidated("gb", "Europe/London",
-                false /* defaultTimeZoneBoost */, true, timeZoneMappings("Europe/London"), "test");
-        CountryTimeZones expectedFr = CountryTimeZones.createValidated("fr", "Europe/Paris",
-                false /* defaultTimeZoneBoost */, true, timeZoneMappings("Europe/Paris"), "test");
-        CountryZonesFinder countryZonesFinder = timeZoneFinder.getCountryZonesFinder();
-        assertEquals(list("gb", "fr"), countryZonesFinder.lookupAllCountryIsoCodes());
-        assertEquals(expectedGb, countryZonesFinder.lookupCountryTimeZones("gb"));
-        assertEquals(expectedFr, countryZonesFinder.lookupCountryTimeZones("fr"));
-        assertNull(countryZonesFinder.lookupCountryTimeZones("DOES_NOT_EXIST"));
-    }
-
-    @Test
-    public void getCountryZonesFinder_empty() throws Exception {
-        TimeZoneFinder timeZoneFinder = TimeZoneFinder.createInstanceForTests(
-                "<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        CountryZonesFinder countryZonesFinder = timeZoneFinder.getCountryZonesFinder();
-        assertEquals(list(), countryZonesFinder.lookupAllCountryIsoCodes());
-    }
-
-    @Test
-    public void getCountryZonesFinder_invalid() throws Exception {
-        TimeZoneFinder timeZoneFinder = TimeZoneFinder.createInstanceForTests(
-                "<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "    <!-- Missing required attributes! -->\n"
-                + "    <country code=\"fr\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertNull(timeZoneFinder.getCountryZonesFinder());
-    }
-
-    @Test
-    public void lookupCountryTimeZones_caseInsensitive() throws Exception {
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        CountryTimeZones expectedCountryTimeZones = CountryTimeZones.createValidated(
-                "gb", "Europe/London", false /* defaultTimeZoneBoost */, true /* everUsesUtc */,
-                timeZoneMappings("Europe/London"), "test");
-
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("gb"));
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("GB"));
-        assertEquals(expectedCountryTimeZones, finder.lookupCountryTimeZones("Gb"));
-    }
-
-    @Test
-    public void lookupCountryTimeZones_unknownCountryReturnsNull() throws Exception {
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
-                + "  <countryzones>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertNull(finder.lookupCountryTimeZones("gb"));
-    }
-
-    @Test
-    public void xmlParsing_missingIanaVersionAttribute() throws Exception {
-        // The <timezones> element will typically have an ianaversion attribute, but it's not
-        // required for parsing.
-        TimeZoneFinder finder = validate("<timezones>\n"
-                + "  <countryzones>\n"
-                + "    <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
-                + "      <id>Europe/London</id>\n"
-                + "    </country>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertNull(finder.getIanaVersion());
-
-        assertNotNull(finder.lookupCountryTimeZones("gb"));
-    }
-
-    @Test
-    public void getIanaVersion() throws Exception {
-        final String expectedIanaVersion = "2017b";
-
-        TimeZoneFinder finder = validate("<timezones ianaversion=\"" + expectedIanaVersion + "\">\n"
-                + "  <countryzones>\n"
-                + "  </countryzones>\n"
-                + "</timezones>\n");
-        assertEquals(expectedIanaVersion, finder.getIanaVersion());
-    }
-
-    private static void checkValidateThrowsParserException(String xml) {
-        try {
-            validate(xml);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    private static TimeZoneFinder validate(String xml) throws IOException {
-        TimeZoneFinder timeZoneFinder = TimeZoneFinder.createInstanceForTests(xml);
-        timeZoneFinder.validate();
-        return timeZoneFinder;
-    }
-
-    /**
-     * Creates a list of default {@link TimeZoneMapping} objects with the specified time zone IDs.
-     */
-    private static List<TimeZoneMapping> timeZoneMappings(String... timeZoneIds) {
-        return Arrays.stream(timeZoneIds)
-                .map(x -> TimeZoneMapping.createForTests(
-                        x, true /* showInPicker */, null /* notUsedAfter */))
-                .collect(Collectors.toList());
-    }
-
-    private static <X> List<X> list(X... values) {
-        return Arrays.asList(values);
-    }
-
-    private String createFile(String fileContent) throws IOException {
-        Path filePath = Files.createTempFile(testDir, null, null);
-        Files.write(filePath, fileContent.getBytes(StandardCharsets.UTF_8));
-        return filePath.toString();
-    }
-
-    private String createMissingFile() throws IOException {
-        Path filePath = Files.createTempFile(testDir, null, null);
-        Files.delete(filePath);
-        return filePath.toString();
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/timezone/TzDataSetVersionTest.java b/luni/src/test/java/libcore/libcore/timezone/TzDataSetVersionTest.java
deleted file mode 100644
index a653301..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/TzDataSetVersionTest.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.libcore.timezone;
-
-import junit.framework.TestCase;
-
-import libcore.timezone.TzDataSetVersion;
-import libcore.timezone.TzDataSetVersion.TzDataSetException;
-
-public class TzDataSetVersionTest extends TestCase {
-
-    private static final int INVALID_VERSION_LOW = -1;
-    private static final int VALID_VERSION = 23;
-    private static final int INVALID_VERSION_HIGH = 1000;
-    private static final String VALID_RULES_VERSION = "2016a";
-    private static final String INVALID_RULES_VERSION = "A016a";
-
-    public void testConstructorValidation() throws Exception {
-        checkConstructorThrows(
-                INVALID_VERSION_LOW, VALID_VERSION, VALID_RULES_VERSION, VALID_VERSION);
-        checkConstructorThrows(
-                INVALID_VERSION_HIGH, VALID_VERSION, VALID_RULES_VERSION, VALID_VERSION);
-        checkConstructorThrows(
-                VALID_VERSION, INVALID_VERSION_LOW, VALID_RULES_VERSION, VALID_VERSION);
-        checkConstructorThrows(
-                VALID_VERSION, INVALID_VERSION_HIGH, VALID_RULES_VERSION, VALID_VERSION);
-        checkConstructorThrows(VALID_VERSION, VALID_VERSION, INVALID_RULES_VERSION, VALID_VERSION);
-        checkConstructorThrows(VALID_VERSION, VALID_VERSION, VALID_RULES_VERSION,
-                INVALID_VERSION_LOW);
-        checkConstructorThrows(VALID_VERSION, VALID_VERSION, VALID_RULES_VERSION,
-                INVALID_VERSION_HIGH);
-    }
-
-    private static void checkConstructorThrows(
-            int majorVersion, int minorVersion, String rulesVersion, int revision) {
-        try {
-            new TzDataSetVersion(majorVersion, minorVersion, rulesVersion, revision);
-            fail();
-        } catch (TzDataSetException expected) {}
-    }
-
-    public void testConstructor() throws Exception {
-        TzDataSetVersion distroVersion = new TzDataSetVersion(1, 2, VALID_RULES_VERSION, 3);
-        assertEquals(1, distroVersion.getFormatMajorVersion());
-        assertEquals(2, distroVersion.getFormatMinorVersion());
-        assertEquals(VALID_RULES_VERSION, distroVersion.getRulesVersion());
-        assertEquals(3, distroVersion.getRevision());
-    }
-
-    public void testToFromBytesRoundTrip() throws Exception {
-        TzDataSetVersion distroVersion = new TzDataSetVersion(1, 2, VALID_RULES_VERSION, 3);
-        assertEquals(distroVersion, TzDataSetVersion.fromBytes(distroVersion.toBytes()));
-    }
-
-    public void testIsCompatibleWithThisDevice() throws Exception {
-        TzDataSetVersion exactMatch = createTzDataSetVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion());
-        assertTrue(TzDataSetVersion.isCompatibleWithThisDevice(exactMatch));
-
-        TzDataSetVersion newerMajor = createTzDataSetVersion(
-                TzDataSetVersion.currentFormatMajorVersion() + 1,
-                TzDataSetVersion.currentFormatMinorVersion());
-        assertFalse(TzDataSetVersion.isCompatibleWithThisDevice(newerMajor));
-
-        TzDataSetVersion newerMinor = createTzDataSetVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() + 1);
-        assertTrue(TzDataSetVersion.isCompatibleWithThisDevice(newerMinor));
-
-        // The constant versions should never be below 1. We allow 0 but want to start version
-        // numbers at 1 to allow testing of older version logic.
-        assertTrue(TzDataSetVersion.currentFormatMajorVersion() >= 1);
-        assertTrue(TzDataSetVersion.currentFormatMinorVersion() >= 1);
-
-        TzDataSetVersion olderMajor = createTzDataSetVersion(
-                TzDataSetVersion.currentFormatMajorVersion() - 1,
-                TzDataSetVersion.currentFormatMinorVersion());
-        assertFalse(TzDataSetVersion.isCompatibleWithThisDevice(olderMajor));
-
-        TzDataSetVersion olderMinor = createTzDataSetVersion(
-                TzDataSetVersion.currentFormatMajorVersion(),
-                TzDataSetVersion.currentFormatMinorVersion() - 1);
-        assertFalse(TzDataSetVersion.isCompatibleWithThisDevice(olderMinor));
-    }
-
-    private TzDataSetVersion createTzDataSetVersion(int majorFormatVersion, int minorFormatVersion)
-            throws TzDataSetException {
-        return new TzDataSetVersion(majorFormatVersion, minorFormatVersion, VALID_RULES_VERSION, 3);
-    }
-}
diff --git a/luni/src/test/java/libcore/libcore/timezone/ZoneInfoDbTest.java b/luni/src/test/java/libcore/libcore/timezone/ZoneInfoDbTest.java
deleted file mode 100644
index 48eb286..0000000
--- a/luni/src/test/java/libcore/libcore/timezone/ZoneInfoDbTest.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2013 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.libcore.timezone;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.RandomAccessFile;
-
-import libcore.timezone.TimeZoneDataFiles;
-import libcore.timezone.testing.ZoneInfoTestHelper;
-import libcore.util.ZoneInfo;
-import libcore.timezone.ZoneInfoDb;
-
-import static libcore.timezone.ZoneInfoDb.SIZEOF_INDEX_ENTRY;
-
-public class ZoneInfoDbTest extends junit.framework.TestCase {
-
-  // The base tzdata file, always present on a device.
-  private static final String TZDATA_FILE =
-          TimeZoneDataFiles.getTimeZoneModuleTzFile(ZoneInfoDb.TZDATA_FILE_NAME);
-
-  // An empty override file should fall back to the default file.
-  public void testLoadTzDataWithFallback_emptyOverrideFile() throws Exception {
-    String emptyFilePath = makeEmptyFile().getPath();
-    try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE);
-         ZoneInfoDb dataWithEmptyOverride =
-                 ZoneInfoDb.loadTzDataWithFallback(emptyFilePath, TZDATA_FILE)) {
-      assertEquals(data.getVersion(), dataWithEmptyOverride.getVersion());
-      assertEquals(data.getAvailableIDs().length, dataWithEmptyOverride.getAvailableIDs().length);
-    }
-  }
-
-  // A corrupt override file should fall back to the default file.
-  public void testLoadTzDataWithFallback_corruptOverrideFile() throws Exception {
-    String corruptFilePath = makeCorruptFile().getPath();
-    try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE);
-         ZoneInfoDb dataWithCorruptOverride =
-                 ZoneInfoDb.loadTzDataWithFallback(corruptFilePath, TZDATA_FILE)) {
-      assertEquals(data.getVersion(), dataWithCorruptOverride.getVersion());
-      assertEquals(data.getAvailableIDs().length, dataWithCorruptOverride.getAvailableIDs().length);
-    }
-  }
-
-  // Given no tzdata files we can use, we should fall back to built-in "GMT".
-  public void testLoadTzDataWithFallback_noGoodFile() throws Exception {
-    String emptyFilePath = makeEmptyFile().getPath();
-    try (ZoneInfoDb data = ZoneInfoDb.loadTzDataWithFallback(emptyFilePath)) {
-      assertEquals("missing", data.getVersion());
-      assertEquals(1, data.getAvailableIDs().length);
-      assertEquals("GMT", data.getAvailableIDs()[0]);
-    }
-  }
-
-  // Given a valid override file, we should find ourselves using that.
-  public void testLoadTzDataWithFallback_goodOverrideFile() throws Exception {
-    RandomAccessFile in = new RandomAccessFile(TZDATA_FILE, "r");
-    byte[] content = new byte[(int) in.length()];
-    in.readFully(content);
-    in.close();
-
-    // Bump the version number to one long past where humans will be extinct.
-    content[6] = '9';
-    content[7] = '9';
-    content[8] = '9';
-    content[9] = '9';
-    content[10] = 'z';
-
-    File goodFile = makeTemporaryFile(content);
-    try (ZoneInfoDb dataWithOverride =
-              ZoneInfoDb.loadTzDataWithFallback(goodFile.getPath(), TZDATA_FILE);
-         ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
-
-      assertEquals("9999z", dataWithOverride.getVersion());
-      assertEquals(data.getAvailableIDs().length, dataWithOverride.getAvailableIDs().length);
-    } finally {
-      goodFile.delete();
-    }
-  }
-
-  public void testLoadTzData_badHeader() throws Exception {
-    RandomAccessFile in = new RandomAccessFile(TZDATA_FILE, "r");
-    byte[] content = new byte[(int) in.length()];
-    in.readFully(content);
-    in.close();
-
-    // Break the header.
-    content[0] = 'a';
-    checkInvalidDataDetected(content);
-  }
-
-  public void testLoadTzData_validTestData() throws Exception {
-    byte[] data = new ZoneInfoTestHelper.TzDataBuilder().initializeToValid().build();
-    File testFile = makeTemporaryFile(data);
-    try {
-      assertNotNull(ZoneInfoDb.loadTzData(testFile.getPath()));
-    } finally {
-      testFile.delete();
-    }
-  }
-
-  public void testLoadTzData_invalidOffsets() throws Exception {
-    ZoneInfoTestHelper.TzDataBuilder builder =
-            new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
-
-    // Sections must be in the correct order: section sizing is calculated using them.
-    builder.setIndexOffsetOverride(10);
-    builder.setDataOffsetOverride(30);
-
-    byte[] data = builder.build();
-    // The offsets must all be under the total size of the file for this test to be valid.
-    assertTrue(30 < data.length);
-    checkInvalidDataDetected(data);
-  }
-
-  public void testLoadTzData_zoneTabOutsideFile() throws Exception {
-    ZoneInfoTestHelper.TzDataBuilder builder =
-            new ZoneInfoTestHelper.TzDataBuilder()
-                    .initializeToValid();
-
-    // Sections must be in the correct order: section sizing is calculated using them.
-    builder.setIndexOffsetOverride(10);
-    builder.setDataOffsetOverride(10 + SIZEOF_INDEX_ENTRY);
-    builder.setZoneTabOffsetOverride(3000); // This is invalid if it is outside of the file.
-
-    byte[] data = builder.build();
-    // The zoneTab offset must be outside of the file for this test to be valid.
-    assertTrue(3000 > data.length);
-    checkInvalidDataDetected(data);
-  }
-
-  public void testLoadTzData_nonDivisibleIndex() throws Exception {
-    ZoneInfoTestHelper.TzDataBuilder builder =
-            new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
-
-    // Sections must be in the correct order: section sizing is calculated using them.
-    int indexOffset = 10;
-    builder.setIndexOffsetOverride(indexOffset);
-    int dataOffset = indexOffset + ZoneInfoDb.SIZEOF_INDEX_ENTRY - 1;
-    builder.setDataOffsetOverride(dataOffset);
-    builder.setZoneTabOffsetOverride(dataOffset + 40);
-
-    byte[] data = builder.build();
-    // The zoneTab offset must be outside of the file for this test to be valid.
-    assertTrue(3000 > data.length);
-    checkInvalidDataDetected(data);
-  }
-
-  public void testLoadTzData_badId() throws Exception {
-    ZoneInfoTestHelper.TzDataBuilder builder =
-            new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
-    builder.clearZicData();
-    byte[] validZicData =
-            new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build();
-    builder.addZicData("", validZicData); // "" is an invalid ID
-
-    checkInvalidDataDetected(builder.build());
-  }
-
-  public void testLoadTzData_badIdOrder() throws Exception {
-    ZoneInfoTestHelper.TzDataBuilder builder =
-            new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
-    builder.clearZicData();
-    byte[] validZicData =
-            new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build();
-    builder.addZicData("Europe/Zurich", validZicData);
-    builder.addZicData("Europe/London", validZicData);
-
-    checkInvalidDataDetected(builder.build());
-  }
-
-  public void testLoadTzData_duplicateId() throws Exception {
-    ZoneInfoTestHelper.TzDataBuilder builder =
-            new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
-    builder.clearZicData();
-    byte[] validZicData =
-            new ZoneInfoTestHelper.ZicDataBuilder().initializeToValid().build();
-    builder.addZicData("Europe/London", validZicData);
-    builder.addZicData("Europe/London", validZicData);
-
-    checkInvalidDataDetected(builder.build());
-  }
-
-  public void testLoadTzData_badZicLength() throws Exception {
-    ZoneInfoTestHelper.TzDataBuilder builder =
-            new ZoneInfoTestHelper.TzDataBuilder().initializeToValid();
-    builder.clearZicData();
-    byte[] invalidZicData = "This is too short".getBytes();
-    builder.addZicData("Europe/London", invalidZicData);
-
-    checkInvalidDataDetected(builder.build());
-  }
-
-  private static void checkInvalidDataDetected(byte[] data) throws Exception {
-    File testFile = makeTemporaryFile(data);
-    try {
-      assertNull(ZoneInfoDb.loadTzData(testFile.getPath()));
-    } finally {
-      testFile.delete();
-    }
-  }
-
-  // Confirms any caching that exists correctly handles TimeZone mutability.
-  public void testMakeTimeZone_timeZoneMutability() throws Exception {
-    try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
-      String tzId = "Europe/London";
-      ZoneInfo first = data.makeTimeZone(tzId);
-      ZoneInfo second = data.makeTimeZone(tzId);
-      assertNotSame(first, second);
-
-      assertTrue(first.hasSameRules(second));
-
-      first.setID("Not Europe/London");
-
-      assertFalse(first.getID().equals(second.getID()));
-
-      first.setRawOffset(3600);
-      assertFalse(first.getRawOffset() == second.getRawOffset());
-    }
-  }
-
-  public void testMakeTimeZone_notFound() throws Exception {
-    try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
-      assertNull(data.makeTimeZone("THIS_TZ_DOES_NOT_EXIST"));
-      assertFalse(data.hasTimeZone("THIS_TZ_DOES_NOT_EXIST"));
-    }
-  }
-
-  public void testMakeTimeZone_found() throws Exception {
-    try (ZoneInfoDb data = ZoneInfoDb.loadTzData(TZDATA_FILE)) {
-      assertNotNull(data.makeTimeZone("Europe/London"));
-      assertTrue(data.hasTimeZone("Europe/London"));
-    }
-  }
-
-  private static File makeCorruptFile() throws Exception {
-    return makeTemporaryFile("invalid content".getBytes());
-  }
-
-  private static File makeEmptyFile() throws Exception {
-    return makeTemporaryFile(new byte[0]);
-  }
-
-  private static File makeTemporaryFile(byte[] content) throws Exception {
-    File f = File.createTempFile("temp-", ".txt");
-    FileOutputStream fos = new FileOutputStream(f);
-    fos.write(content);
-    fos.close();
-    return f;
-  }
-}
diff --git a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
index 800928f..867932b 100644
--- a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
+++ b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
@@ -64,6 +64,7 @@
 
       assertArraysEqual(encodedUpper.toCharArray(), encode(bytes));
       assertArraysEqual(encodedUpper.toCharArray(), encode(bytes, true /* upperCase */));
+      assertArraysEqual(encodedUpper.toCharArray(), encode(bytes, 0, bytes.length));
       assertArraysEqual(encodedLower.toCharArray(), encode(bytes, false /* upperCase */));
 
       assertArraysEqual(bytes, decode(encode(bytes), false /* allowSingleChar */));
diff --git a/luni/src/test/java/libcore/libcore/util/NativeAllocationRegistryTest.java b/luni/src/test/java/libcore/libcore/util/NativeAllocationRegistryTest.java
index 54c8876..9190452 100644
--- a/luni/src/test/java/libcore/libcore/util/NativeAllocationRegistryTest.java
+++ b/luni/src/test/java/libcore/libcore/util/NativeAllocationRegistryTest.java
@@ -61,11 +61,11 @@
         assertEquals("Native bytes already allocated", 0, nativeBytes);
         long max = Runtime.getRuntime().maxMemory();
         long total = Runtime.getRuntime().totalMemory();
-        int size = 1024*1024;
-        final int nativeSize = size/2;
-        int javaSize = size/2;
-        int expectedMaxNumAllocations = (int)(max-total)/javaSize;
-        int numSavedAllocations = expectedMaxNumAllocations/2;
+        int size = 1024 * 1024;
+        final int nativeSize = size / 2;
+        int javaSize = size / 2;
+        int expectedMaxNumAllocations = (int)(max-total) / javaSize;
+        int numSavedAllocations = expectedMaxNumAllocations / 2;
         Allocation[] saved = new Allocation[numSavedAllocations];
 
         NativeAllocationRegistry registry = null;
@@ -90,21 +90,31 @@
             alloc.nativeAllocation = doNativeAllocation(nativeSize);
             registry.registerNativeAllocation(alloc, alloc.nativeAllocation);
 
-            saved[i%numSavedAllocations] = alloc;
+            saved[i % numSavedAllocations] = alloc;
         }
 
-        // Verify most of the allocations have been freed.
-        // Since we use fairly large Java objects, this doesn't test the GC triggering
-        // effect; we do that elsewhere.
-        // Since native and java objects have the same size, and we can only have max
-        // Java bytes in use, there should be no more than max native bytes in use,
-        // once all enqueued deallocations have been processed. First make sure
-        // that the ReferenceQueueDaemon has processed all pending requests, and then
-        // check.
+        // Verify most of the allocations have been freed.  Since we use fairly large Java
+        // objects, this doesn't test the GC triggering effect; we do that elsewhere.
+        //
+        // Since native and java objects have the same size, and we can only have max Java bytes
+        // in use, there should ideally be no more than max native bytes in use, once all enqueued
+        // deallocations have been processed. We call runFinalization() to make sure that the
+        // ReferenceQueueDaemon has processed all pending requests, and then check.
+        // (runFinalization() isn't documented to guarantee this, but it waits for a sentinel
+        // object to make it all the way through the pending reference queue, and hence has that
+        // effect.)
+        //
+        // However the garbage collector enqueues references asynchronously, by enqueuing
+        // another heap task. If the GC runs before we finish our allocation, but reference
+        // enqueueing is delayed, and runFinalization() runs between the time the GC reclaims
+        // memory and the references are enqueued, then runFinalization() may complete
+        // immediately, and further allocation may have occurred between the GC and the invocation
+        // of runFinalization(). Thus, under unlikely conditions, we may see up to twice as much
+        // native memory as the Java heap, and that's the actual condition we test.
         System.runFinalization();
         nativeBytes = getNumNativeBytesAllocated();
         assertTrue("Excessive native bytes still allocated (" + nativeBytes + ")"
-                + " given max memory of (" + max + ")", nativeBytes <= max);
+                + " given max memory of (" + max + ")", nativeBytes <= 2 * max);
         // Check that the array is fully populated, and sufficiently many native bytes
         // are live.
         long nativeReachableBytes = numSavedAllocations * nativeSize;
@@ -171,16 +181,32 @@
         Runtime.getRuntime().gc();
     }
 
+    public void testApplyFreeFunction() {
+        if (isNativeBridgedABI()) {
+            // See the explanation in testNativeAllocation.
+            System.logI("Skipping test for native bridged ABI");
+            return;
+        }
+        long size = 1234;
+        long nativePtr = doNativeAllocation(size);
+        long numBytesAllocatedBeforeFree = getNumNativeBytesAllocated();
+
+        // Applying the free function should cause the native finalizer to run.
+        NativeAllocationRegistry.applyFreeFunction(getNativeFinalizer(), nativePtr);
+        long numBytesAllocatedAfterFree = getNumNativeBytesAllocated();
+        assertEquals(numBytesAllocatedBeforeFree - size, numBytesAllocatedAfterFree);
+    }
+
     public void testNullArguments() {
         final NativeAllocationRegistry registry
             = new NativeAllocationRegistry(classLoader, getNativeFinalizer(), 1024);
-        final long dummyNativePtr = 0x1;
+        final long fakeNativePtr = 0x1;
         final Object referent = new Object();
 
         // referent should not be null
         assertThrowsIllegalArgumentException(new Runnable() {
             public void run() {
-                registry.registerNativeAllocation(null, dummyNativePtr);
+                registry.registerNativeAllocation(null, fakeNativePtr);
             }
         });
 
diff --git a/luni/src/test/java/libcore/libcore/util/SerializationTester.java b/luni/src/test/java/libcore/libcore/util/SerializationTester.java
index 48a58b4..4f8b357 100644
--- a/luni/src/test/java/libcore/libcore/util/SerializationTester.java
+++ b/luni/src/test/java/libcore/libcore/util/SerializationTester.java
@@ -69,7 +69,7 @@
             assertTrue("User-constructed value doesn't equal itself, reserialized",
                     equals(value, reserialized));
 
-            // just a sanity check! if this fails, verify() is probably broken
+            // just a confidence check! if this fails, verify() is probably broken
             verify(value);
             verify(deserialized);
             verify(reserialized);
diff --git a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
index 8b9715f..31da1f1 100644
--- a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
+++ b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
@@ -15,19 +15,14 @@
  */
 package libcore.libcore.util;
 
+import com.android.i18n.timezone.ZoneInfoData;
 import junit.framework.TestCase;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectInputStream;
-import java.nio.ByteBuffer;
-import java.time.Duration;
 import java.time.Instant;
-import java.util.Arrays;
-import java.util.Date;
-import libcore.io.BufferIterator;
-import libcore.timezone.ZoneInfoDb;
-import libcore.timezone.testing.ZoneInfoTestHelper;
+import java.util.TimeZone;
+
 import libcore.util.ZoneInfo;
 
 /**
@@ -36,586 +31,6 @@
 public class ZoneInfoTest extends TestCase {
 
   /**
-   * Checks that a {@link ZoneInfo} cannot be created without any types.
-   */
-  public void testMakeTimeZone_NoTypes() throws Exception {
-    long[][] transitions = {};
-    int[][] types = {};
-    try {
-      createZoneInfo(transitions, types);
-      fail();
-    } catch (IOException expected) {
-    }
-  }
-
-  /**
-   * Checks that a {@link ZoneInfo} can be created with one type and no transitions.
-   */
-  public void testMakeTimeZone_OneType_NoTransitions() throws Exception {
-    long[][] transitions = {};
-    int[][] types = {
-        { 4800, 0 }
-    };
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types);
-
-    // If there are no transitions then the offset should be constant irrespective of the time.
-    Instant[] times = {
-            Instant.ofEpochMilli(Long.MIN_VALUE),
-            Instant.ofEpochMilli(0),
-            Instant.ofEpochMilli(Long.MAX_VALUE),
-    };
-    assertOffsetAt(zoneInfo, offsetFromSeconds(4800), times);
-
-    // No transitions means no DST.
-    assertFalse("Doesn't use DST", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-
-    // The raw offset should be the offset of the first type.
-    assertRawOffset(zoneInfo, offsetFromSeconds(4800));
-  }
-
-  /**
-   * Checks that a {@link ZoneInfo} can be created with one non-DST transition.
-   */
-  public void testReadTimeZone_OneNonDstTransition() throws Exception {
-    long[][] transitions = {
-        { 0, 0 }
-    };
-    int[][] types = {
-        { 3600, 0 }
-    };
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types);
-
-    // Any time before the first transition is assumed to use the first standard transition.
-    Instant[] times = { timeFromSeconds(-2), timeFromSeconds(0), timeFromSeconds(2) };
-    assertOffsetAt(zoneInfo, offsetFromSeconds(3600), times);
-
-    // No transitions means no DST.
-    assertFalse("Doesn't use DST", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-
-    // The raw offset should be the offset of the first type.
-    assertRawOffset(zoneInfo, offsetFromSeconds(3600));
-  }
-
-  /**
-   * Checks that a {@link ZoneInfo} cannot be created with one DST but no non-DSTs transitions.
-   */
-  public void testReadTimeZone_OneDstTransition() throws Exception {
-    long[][] transitions = {
-        { 0, 0 }
-    };
-    int[][] types = {
-        { 3600, 1 }
-    };
-    try {
-      createZoneInfo(transitions, types);
-      fail("Did not detect no non-DST transitions");
-    } catch (IllegalStateException expected) {
-    }
-  }
-
-  /**
-   * Checks to make sure that rounding the time from milliseconds to seconds does not cause issues
-   * around the boundary of negative transitions.
-   */
-  public void testReadTimeZone_NegativeTransition() throws Exception {
-    long[][] transitions = {
-        { -2000, 0 },
-        { -5, 1 },
-        { 0, 2 },
-    };
-    int[][] types = {
-        { 1800, 0 },
-        { 3600, 1 },
-        { 5400, 0 }
-    };
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types);
-    Instant transitionTime = timeFromSeconds(-5);
-
-    // Even a millisecond before a transition means that the transition is not active.
-    Instant beforeTransitionTime = transitionTime.minusMillis(1);
-    assertOffsetAt(zoneInfo, offsetFromSeconds(1800), beforeTransitionTime);
-    assertInDaylightTime(zoneInfo, beforeTransitionTime, false);
-
-    // A time equal to the transition point activates the transition.
-    assertOffsetAt(zoneInfo, offsetFromSeconds(3600), transitionTime);
-    assertInDaylightTime(zoneInfo, transitionTime, true);
-
-    // A time after the transition point but before the next activates the transition.
-    Instant afterTransitionTime = transitionTime.plusMillis(1);
-    assertOffsetAt(zoneInfo, offsetFromSeconds(3600), afterTransitionTime);
-    assertInDaylightTime(zoneInfo, afterTransitionTime, true);
-
-    assertFalse("Doesn't use DST", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-    assertRawOffset(zoneInfo, offsetFromSeconds(5400));
-  }
-
-  /**
-   * Checks to make sure that rounding the time from milliseconds to seconds does not cause issues
-   * around the boundary of positive transitions.
-   */
-  public void testReadTimeZone_PositiveTransition() throws Exception {
-    long[][] transitions = {
-        { 0, 0 },
-        { 5, 1 },
-        { 2000, 2 },
-    };
-    int[][] types = {
-        { 1800, 0 },
-        { 3600, 1 },
-        { 5400, 0 }
-    };
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types);
-
-    Instant transitionTime = timeFromSeconds(5);
-
-    // Even a millisecond before a transition means that the transition is not active.
-    Instant beforeTransitionTime = transitionTime.minusMillis(1);
-    assertOffsetAt(zoneInfo, offsetFromSeconds(1800), beforeTransitionTime);
-    assertInDaylightTime(zoneInfo, beforeTransitionTime, false);
-
-    // A time equal to the transition point activates the transition.
-    assertOffsetAt(zoneInfo, offsetFromSeconds(3600), transitionTime);
-    assertInDaylightTime(zoneInfo, transitionTime, true);
-
-    // A time after the transition point but before the next activates the transition.
-    Instant afterTransitionTime = transitionTime.plusMillis(1);
-    assertOffsetAt(zoneInfo, offsetFromSeconds(3600), afterTransitionTime);
-    assertInDaylightTime(zoneInfo, afterTransitionTime, true);
-
-    assertFalse("Doesn't use DST", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-    assertRawOffset(zoneInfo, offsetFromSeconds(5400));
-  }
-
-  /**
-   * Checks that creating a {@link ZoneInfo} with future DST transitions but no past DST
-   * transitions where the transition times are negative is not affected by rounding issues.
-   */
-  public void testReadTimeZone_HasFutureDST_NoPastDST_NegativeTransitions() throws Exception {
-    long[][] transitions = {
-        { -2000, 0 },
-        { -500, 1 },
-        { -100, 2 },
-    };
-    int[][] types = {
-        { 1800, 0 },
-        { 3600, 0 },
-        { 5400, 1 }
-    };
-    // The expected DST savings is the difference between the DST offset (which includes the
-    // raw offset) and the preceding non-DST offset (which should just be the raw offset).
-    // Or in other words (5400 - 3600) * 1000
-    Duration expectedDSTSavings = offsetFromSeconds(5400 - 3600);
-
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(-700));
-
-    assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, expectedDSTSavings);
-
-    // Now create one a few milliseconds before the DST transition to make sure that rounding
-    // errors don't cause a problem.
-    zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(-100).minusMillis(5));
-
-    assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, expectedDSTSavings);
-  }
-
-  /**
-   * Checks that creating a {@link ZoneInfo} with future DST transitions but no past DST
-   * transitions where the transition times are positive is not affected by rounding issues.
-   */
-  public void testReadTimeZone_HasFutureDST_NoPastDST_PositiveTransitions() throws Exception {
-    long[][] transitions = {
-        { 4000, 0 },
-        { 5500, 1 },
-        { 6000, 2 },
-    };
-    int[][] types = {
-        { 1800, 0 },
-        { 3600, 0 },
-        { 7200, 1 }
-    };
-    // The expected DST savings is the difference between the DST offset (which includes the
-    // raw offset) and the preceding non-DST offset (which should just be the raw offset).
-    // Or in other words (7200 - 3600) * 1000
-    Duration expectedDSTSavings = offsetFromSeconds(7200 - 3600);
-
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(4500) /* currentTime */);
-
-    assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, expectedDSTSavings);
-
-    // Now create one a few milliseconds before the DST transition to make sure that rounding
-    // errors don't cause a problem.
-    zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(6000).minusMillis(5));
-
-    assertTrue("Should use DST but doesn't", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, expectedDSTSavings);
-  }
-
-  /**
-   * Checks that creating a {@link ZoneInfo} with past DST transitions but no future DST
-   * transitions where the transition times are negative is not affected by rounding issues.
-   */
-  public void testReadTimeZone_HasPastDST_NoFutureDST_NegativeTransitions() throws Exception {
-    long[][] transitions = {
-        { -5000, 0 },
-        { -2000, 1 },
-        { -500, 0 },
-        { 0, 2 },
-    };
-    int[][] types = {
-        { 3600, 0 },
-        { 1800, 1 },
-        { 5400, 0 }
-    };
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(-1) /* currentTime */);
-
-    assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-
-    // Now create one a few milliseconds after the DST transition to make sure that rounding
-    // errors don't cause a problem.
-    zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(-2000).plusMillis(5));
-
-    assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-  }
-
-  /**
-   * Checks that creating a {@link ZoneInfo} with past DST transitions but no future DST
-   * transitions where the transition times are positive is not affected by rounding issues.
-   */
-  public void testReadTimeZone_HasPastDST_NoFutureDST_PositiveTransitions() throws Exception {
-    long[][] transitions = {
-        { 1000, 0 },
-        { 4000, 1 },
-        { 5500, 0 },
-        { 6000, 2 },
-    };
-    int[][] types = {
-        { 3600, 0 },
-        { 1800, 1 },
-        { 5400, 0 }
-    };
-    ZoneInfo zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(4700));
-
-    assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-
-    // Now create one a few milliseconds after the DST transition to make sure that rounding
-    // errors don't cause a problem.
-    zoneInfo = createZoneInfo(transitions, types, timeFromSeconds(4000).plusMillis(5));
-
-    assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-  }
-
-  /**
-   * TimeZone APIs use Java long times in millis.
-   *
-   * <p>Android has historically mishandled the lookup of offset for times before Integer.MIN_VALUE
-   * seconds for various reasons. The logic has been corrected. This test would fail on versions of
-   * Android <= P.
-   */
-  public void testReadTimeZone_Bug118835133_extraFirstTransition() throws Exception {
-    // A time before the first representable time in seconds with a java int.
-    Instant before32BitTime = timeFromSeconds(Integer.MIN_VALUE).minusMillis(1);
-
-    // Times around the 32-bit seconds minimum.
-    Instant[] earlyTimes = {
-            timeFromSeconds(Integer.MIN_VALUE),
-            timeFromSeconds(Integer.MIN_VALUE).plusMillis(1),
-    };
-
-    Instant firstRealTransitionTime = timeFromSeconds(1000);
-    Instant afterFirstRealTransitionTime = firstRealTransitionTime.plusSeconds(1);
-    Instant[] afterFirstRealTransitionTimes = {
-            firstRealTransitionTime,
-            afterFirstRealTransitionTime,
-    };
-
-    // A time to use as currentTime when building the TimeZone. Important for the getRawOffset()
-    // calculation.
-    Instant currentTime = afterFirstRealTransitionTime;
-
-    Duration type0Offset = offsetFromSeconds(0);
-    Duration type1Offset = offsetFromSeconds(1800);
-    Duration type2Offset = offsetFromSeconds(3600);
-    int[][] types = {
-            { offsetToSeconds(type0Offset), 0 }, // 1st type, used before first known transition.
-            { offsetToSeconds(type1Offset), 0 },
-            { offsetToSeconds(type2Offset), 0 },
-    };
-
-    // Simulates a zone with a single transition.
-    {
-      long[][] transitions = {
-              { timeToSeconds(firstRealTransitionTime), 2 /* type 2 */ },
-      };
-      ZoneInfo oldZoneInfo = createZoneInfo(transitions, types, currentTime);
-      assertRawOffset(oldZoneInfo, type2Offset);
-
-      // We use the first non-DST type for times before the first transition.
-      assertOffsetAt(oldZoneInfo, type0Offset, before32BitTime);
-      assertOffsetAt(oldZoneInfo, type0Offset, earlyTimes);
-
-      // This is after the first transition, so type 2.
-      assertOffsetAt(oldZoneInfo, type2Offset, afterFirstRealTransitionTimes);
-    }
-
-    // Simulation a zone where there is an explicit transition at Integer.MIN_VALUE seconds. This
-    // used to be common when Android used 32-bit data from the TZif file.
-    {
-      long[][] transitions = {
-              { Integer.MIN_VALUE, 1 /* type 1 */ },
-              { timeToSeconds(firstRealTransitionTime), 2 /* type 2 */ },
-      };
-      ZoneInfo newZoneInfo = createZoneInfo(transitions, types, currentTime);
-      assertRawOffset(newZoneInfo, type2Offset);
-
-      // We use the first non-DST type for times before the first transition.
-      assertOffsetAt(newZoneInfo, type0Offset, before32BitTime);
-
-      // After the first transition, so type 1.
-      assertOffsetAt(newZoneInfo, type1Offset, earlyTimes);
-
-      // This is after the second transition, so type 2.
-      assertOffsetAt(newZoneInfo, type2Offset, afterFirstRealTransitionTimes);
-    }
-  }
-
-  /**
-   * Newer versions of zic after 2014b sometime introduce an explicit transition at
-   * Integer.MAX_VALUE seconds.
-   */
-  public void testReadTimeZone_Bug118835133_extraLastTransition() throws Exception {
-    // An arbitrary time to use as currentTime. Not important for this test.
-    Instant currentTime = timeFromSeconds(4000);
-
-    // Offset before time 1000 should be consistent.
-    Instant[] timesToCheck = {
-            timeFromSeconds(2100), // arbitrary time > 2000
-            timeFromSeconds(Integer.MAX_VALUE).minusMillis(1),
-            timeFromSeconds(Integer.MAX_VALUE),
-            timeFromSeconds(Integer.MAX_VALUE).plusMillis(1),
-    };
-
-    int latestOffsetSeconds = 3600;
-    int[][] types = {
-            { 1800, 0 },
-            { latestOffsetSeconds, 0 },
-    };
-    Duration expectedLateOffset = offsetFromSeconds(latestOffsetSeconds);
-
-    // Create a simulation of a zone where there is no explicit transition at Integer.MAX_VALUE
-    // seconds.
-    {
-      long[][] transitions = {
-              { 1000, 0 },
-              { 2000, 1 },
-      };
-      ZoneInfo oldZoneInfo = createZoneInfo(transitions, types, currentTime);
-      assertOffsetAt(oldZoneInfo, expectedLateOffset, timesToCheck);
-    }
-
-    // Create a simulation of a zone where there is an explicit transition at Integer.MAX_VALUE
-    // seconds.
-    {
-      long[][] transitions = {
-              { 1000, 0 },
-              { 2000, 1 },
-              { Integer.MAX_VALUE, 1}, // The extra transition.
-      };
-      ZoneInfo newZoneInfo = createZoneInfo(transitions, types, currentTime);
-      assertOffsetAt(newZoneInfo, expectedLateOffset, timesToCheck);
-    }
-  }
-
-  /**
-   * Checks to make sure that ZoneInfo can handle up to 256 types.
-   */
-  public void testReadTimeZone_MaxTypeCount() throws Exception {
-    long[][] transitions = {
-        { -2000, 255 },
-    };
-    // Create 256 types, each with zero offset and without DST except the last, which is offset by
-    // one hour but also without DST.
-    int[][] types = new int[256][];
-    Arrays.fill(types, new int[2]);
-    types[255] = new int[] { 3600, 0 };
-
-    ZoneInfo zoneInfo = createZoneInfo(getName(), transitions, types,
-            timeFromSeconds(Integer.MIN_VALUE));
-
-    assertFalse("Shouldn't use DST but does", zoneInfo.useDaylightTime());
-    assertDSTSavings(zoneInfo, offsetFromSeconds(0));
-
-    // Make sure that WallTime works properly with a ZoneInfo with 256 types.
-    ZoneInfo.WallTime wallTime = new ZoneInfo.WallTime();
-    wallTime.localtime(0, zoneInfo);
-    wallTime.mktime(zoneInfo);
-  }
-
-  /**
-   * Create an instance for every available time zone for which we have data to ensure that they
-   * can all be handled correctly.
-   *
-   * <p>This is to ensure that ZoneInfo can read all time zone data without failing, it doesn't
-   * check that it reads it correctly or that the data itself is correct. This is a sanity test
-   * to ensure that any additional checks added to the code that reads the data source and
-   * creates the {@link ZoneInfo} instances does not prevent any of the time zones being loaded.
-   */
-  public void testReadTimeZone_All() throws Exception {
-    ZoneInfoDb instance = ZoneInfoDb.getInstance();
-    String[] availableIDs = instance.getAvailableIDs();
-    Arrays.sort(availableIDs);
-    for (String id : availableIDs) {
-      BufferIterator bufferIterator = instance.getBufferIterator(id);
-
-      // Create a ZoneInfo at the earliest possible time to allow us to use the
-      // useDaylightTime() method to check whether it ever has or ever will support daylight
-      // savings time.
-      ZoneInfo zoneInfo = ZoneInfo.readTimeZone(id, bufferIterator, Long.MIN_VALUE);
-      assertNotNull("TimeZone " + id + " was not created", zoneInfo);
-      assertEquals(id, zoneInfo.getID());
-    }
-  }
-
-  public void testReadTimeZone_Valid() throws Exception {
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .initializeToValid();
-    assertNotNull(createZoneInfo(getName(), Instant.now(), builder.build()));
-  }
-
-  public void testReadTimeZone_BadMagic() {
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .initializeToValid()
-                    .setMagic(0xdeadbeef); // Bad magic.
-    try {
-      createZoneInfo(getName(), Instant.now(), builder.build());
-      fail();
-    } catch (IOException expected) {}
-  }
-
-  /**
-   * Checks to make sure that ZoneInfo rejects more than 256 types.
-   */
-  public void testReadTimeZone_TooManyTypes() {
-    int typeCount = 257; // Max types allowed is 256
-    int transitionCount = 5;
-    long[][] transitions = createTransitions(transitionCount, typeCount);
-    int[][] types = createTypes(typeCount);
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .initializeToValid()
-                    .setTransitionsAndTypes(transitions, types);
-    byte[] bytes = builder.build();
-    try {
-      createZoneInfo(getName(), Instant.now(), bytes);
-      fail("Did not detect too many types");
-    } catch (IOException expected) {
-    }
-  }
-
-  /**
-   * Checks to make sure that ZoneInfo rejects more than 2000 transitions.
-   */
-  public void testReadTimeZone_TooManyTransitions() {
-    int typeCount = 5;
-    int transitionCount = 2001; // Max transitions allowed is 2000.
-    long[][] transitions = createTransitions(transitionCount, typeCount);
-    int[][] types = createTypes(typeCount);
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .initializeToValid()
-                    .setTransitionsAndTypes(transitions, types);
-    byte[] bytes = builder.build();
-    try {
-      createZoneInfo(getName(), Instant.now(), bytes);
-      fail("Did not detect too many transitions");
-    } catch (IOException expected) {
-    }
-  }
-
-  public void testReadTimeZone_TransitionsNotSorted() {
-    long[][] transitions = {
-            { 1000, 0 },
-            { 3000, 1 }, // Out of transition order.
-            { 2000, 0 },
-    };
-    int[][] types = {
-            { 3600, 0 },
-            { 1800, 1 },
-    };
-
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .initializeToValid()
-                    .setTransitionsAndTypes(transitions, types);
-
-    byte[] bytes = builder.build();
-    try {
-      createZoneInfo(getName(), Instant.now(), bytes);
-      fail();
-    } catch (IOException expected) {
-    }
-  }
-
-  public void testReadTimeZone_InvalidTypeIndex() {
-    long[][] transitions = {
-            { 1000, 0 },
-            { 2000, 2 }, // Invalid type index - only 0 and 1 defined below.
-            { 3000, 0 },
-    };
-    int[][] types = {
-            { 3600, 0 },
-            { 1800, 1 },
-    };
-
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .initializeToValid()
-                    .setTransitionsAndTypes(transitions, types);
-
-    byte[] bytes = builder.build();
-    try {
-      createZoneInfo(getName(), Instant.now(), bytes);
-      fail();
-    } catch (IOException expected) {
-    }
-  }
-
-  public void testReadTimeZone_InvalidIsDst() {
-    long[][] transitions = {
-            { 1000, 0 },
-            { 2000, 1 },
-            { 3000, 0 },
-    };
-    int[][] types = {
-            { 3600, 0 },
-            { 1800, 2 }, // Invalid isDst - must be 0 or 1
-    };
-
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .initializeToValid()
-                    .setTransitionsAndTypes(transitions, types);
-
-    byte[] bytes = builder.build();
-    try {
-      createZoneInfo(getName(), Instant.now(), bytes);
-      fail();
-    } catch (IOException expected) {
-    }
-  }
-
-  /**
    * Checks that we can read the serialized form of a {@link ZoneInfo} created in pre-OpenJDK
    * AOSP.
    *
@@ -634,19 +49,12 @@
       zoneInfoRead = (ZoneInfo) object;
     }
 
-    long[][] transitions = {
-        { -5000, 0 },
-        { -2000, 1 },
-        { -500, 0 },
-        { 0, 2 },
-    };
-    int[][] types = {
-        { 3600, 0 },
-        { 1800, 1 },
-        { 5400, 0 }
-    };
-    ZoneInfo zoneInfoCreated = createZoneInfo(
-            "test", transitions, types, timeFromSeconds(-1));
+    long[] transitions = { -5000, -2000, -500, 0 };
+    byte[] types =       { 0,     1,     0,    2 }; // align each entry with transitions
+    int[] offsets =    { 3600,  1800, 5400  };
+    boolean[] isDsts = { false, true, false }; // align each entry with offsets
+    ZoneInfo zoneInfoCreated = createZoneInfo("TimeZone for 'test'", transitions, types, offsets,
+            isDsts, timeFromSeconds(-1));
 
     assertEquals("Read ZoneInfo does not match created one", zoneInfoCreated, zoneInfoRead);
     assertEquals("useDaylightTime() mismatch",
@@ -655,23 +63,16 @@
         zoneInfoCreated.getDSTSavings(), zoneInfoRead.getDSTSavings());
   }
 
-  private static void assertRawOffset(ZoneInfo zoneInfo, Duration expectedOffset) {
-    assertEquals(expectedOffset.toMillis(), zoneInfo.getRawOffset());
-  }
-
-  private static void assertDSTSavings(ZoneInfo zoneInfo, Duration expectedDSTSavings) {
-    assertEquals(expectedDSTSavings.toMillis(), zoneInfo.getDSTSavings());
-  }
-
-  private static void assertInDaylightTime(ZoneInfo zoneInfo, Instant time, boolean expectedValue) {
-    assertEquals(expectedValue, zoneInfo.inDaylightTime(new Date(time.toEpochMilli())));
-  }
-
-  private static void assertOffsetAt(
-          ZoneInfo zoneInfo, Duration expectedOffset, Instant... times) {
-    for (Instant time : times) {
-      assertEquals("Unexpected offset at " + time,
-              expectedOffset.toMillis(), zoneInfo.getOffset(time.toEpochMilli()));
+  /**
+   * Test consistency among DST-related APIs supported by ZoneInfo.
+   *
+   * This test may use TimeZone APIs only, the implementation comes from ZoneInfo.
+   */
+  public void testUseDaylightTime_consistency() {
+    for (String tzId : TimeZone.getAvailableIDs()) {
+      TimeZone tz = TimeZone.getTimeZone(tzId);
+      assertEquals("TimeZone API does not report consistently in this zone:" + tzId,
+              tz.useDaylightTime(), tz.getDSTSavings() != 0);
     }
   }
 
@@ -679,148 +80,9 @@
     return Instant.ofEpochSecond(timeInSeconds);
   }
 
-  private static long timeToSeconds(Instant time) {
-    return time.getEpochSecond();
-  }
-
-  private static Duration offsetFromSeconds(int offsetSeconds) {
-    return Duration.ofSeconds(offsetSeconds);
-  }
-
-  private static int offsetToSeconds(Duration offset) {
-    long seconds = offset.getSeconds();
-    if (seconds < Integer.MIN_VALUE || seconds > Integer.MAX_VALUE) {
-      fail("Offset out of seconds range: " + offset);
-    }
-    return (int) seconds;
-  }
-
-  private ZoneInfo createZoneInfo(long[][] transitions, int[][] types)
-      throws Exception {
-    return createZoneInfo(getName(), transitions, types, Instant.now());
-  }
-
-  private ZoneInfo createZoneInfo(long[][] transitions, int[][] types, Instant currentTime)
-          throws Exception {
-    return createZoneInfo(getName(), transitions, types, currentTime);
-  }
-
-  private ZoneInfo createZoneInfo(String name, long[][] transitions, int[][] types,
-          Instant currentTime) throws Exception {
-
-    ZoneInfoTestHelper.ZicDataBuilder builder =
-            new ZoneInfoTestHelper.ZicDataBuilder()
-                    .setTransitionsAndTypes(transitions, types);
-    return createZoneInfo(name, currentTime, builder.build());
-  }
-
-  private static ZoneInfo createZoneInfo(String name, Instant currentTime, byte[] bytes)
-          throws IOException {
-    ByteBufferIterator bufferIterator = new ByteBufferIterator(ByteBuffer.wrap(bytes));
-    return ZoneInfo.readTimeZone(
-            "TimeZone for '" + name + "'", bufferIterator, currentTime.toEpochMilli());
-  }
-
-  /**
-   * Creates {@code typeCount} "types" for use with
-   * {@link ZoneInfoTestHelper.ZicDataBuilder#setTypes(int[][])} and related methods. Each type is
-   * given an arbitrary offset and "isDst" value.
-   */
-  private static int[][] createTypes(int typeCount) {
-    int[][] types = new int[typeCount][2];
-    for (int i = 0; i < typeCount; i++) {
-      // [0] holds the offset from UTC in seconds.
-      types[i][0] = typeCount;
-      // [1] holds isDst: 0 == STD, 1 == DST
-      types[i][1] = typeCount % 2;
-    }
-    return types;
-  }
-
-  /**
-   * Creates {@code transitionCount} "transition pairs" for use with
-   * {@link ZoneInfoTestHelper.ZicDataBuilder#setTransitions(long[][])} and related methods. Each
-   * transition is given an arbitrary (but increasing) time referencing an arbitrary type.
-   */
-  private static long[][] createTransitions(int transitionCount, int typeCount) {
-    long[][] transitions = new long[transitionCount][2];
-    for (int i = 0; i < transitionCount; i++) {
-      // [0] holds the transition time.
-      transitions[i][0] = (i * 3600) + 100;
-      // [1] holds the type index to use. Must be > 0 and < typeCount to be valid.
-      transitions[i][1] = i % typeCount;
-    }
-    return transitions;
-  }
-
-  /**
-   * A {@link BufferIterator} that wraps a {@link ByteBuffer}.
-   */
-  private static class ByteBufferIterator extends BufferIterator {
-
-    private final ByteBuffer buffer;
-
-    public ByteBufferIterator(ByteBuffer buffer) {
-      this.buffer = buffer;
-    }
-
-    @Override
-    public void seek(int offset) {
-      buffer.position(offset);
-    }
-
-    @Override
-    public void skip(int byteCount) {
-      buffer.position(buffer.position() + byteCount);
-    }
-
-    @Override
-    public int pos() {
-      return buffer.position();
-    }
-
-    @Override
-    public void readByteArray(byte[] bytes, int arrayOffset, int byteCount) {
-      buffer.get(bytes, arrayOffset, byteCount);
-    }
-
-    @Override
-    public byte readByte() {
-      return buffer.get();
-    }
-
-    @Override
-    public int readInt() {
-      int value = buffer.asIntBuffer().get();
-      // Using a separate view does not update the position of this buffer so do it
-      // explicitly.
-      skip(Integer.BYTES);
-      return value;
-    }
-
-    @Override
-    public void readIntArray(int[] ints, int arrayOffset, int intCount) {
-      buffer.asIntBuffer().get(ints, arrayOffset, intCount);
-      // Using a separate view does not update the position of this buffer so do it
-      // explicitly.
-      skip(Integer.BYTES * intCount);
-    }
-
-    @Override
-    public void readLongArray(long[] longs, int arrayOffset, int longCount) {
-      buffer.asLongBuffer().get(longs, arrayOffset, longCount);
-      // Using a separate view does not update the position of this buffer so do it
-      // explicitly.
-      skip(Long.BYTES * longCount);
-    }
-
-    @Override
-    public short readShort() {
-      short value = buffer.asShortBuffer().get();
-      // Using a separate view does not update the position of this buffer so do it
-      // explicitly.
-      skip(Short.BYTES);
-      return value;
-    }
+  private ZoneInfo createZoneInfo(String id, long[] transitions, byte[] types, int[] offsets,
+          boolean[] isDsts, Instant currentTime) {
+    ZoneInfoData data = ZoneInfoData.createInstance(id, transitions, types, offsets, isDsts);
+    return ZoneInfo.createZoneInfo(data, currentTime.toEpochMilli());
   }
 }
diff --git a/luni/src/test/java/libcore/sun/security/jca/ProvidersTest.java b/luni/src/test/java/libcore/sun/security/jca/ProvidersTest.java
index 8876671..45c857f 100644
--- a/luni/src/test/java/libcore/sun/security/jca/ProvidersTest.java
+++ b/luni/src/test/java/libcore/sun/security/jca/ProvidersTest.java
@@ -27,15 +27,25 @@
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.Provider;
 import java.security.Security;
 import java.security.Signature;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
 import javax.crypto.KeyGenerator;
 import javax.crypto.Mac;
+import javax.crypto.SecretKeyFactory;
 
 import sun.security.jca.Providers;
 
@@ -66,65 +76,6 @@
     // getInstance calls that result in requests to Conscrypt
     private static final List<Algorithm> CONSCRYPT_ALGORITHMS = new ArrayList<>();
     static {
-        // A concrete algorithm, provider by name
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                return Signature.getInstance("sha224withrsa", "BC");
-            }
-        });
-        // A concrete algorithm, provider by instance
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                return KeyFactory.getInstance("EC", Security.getProvider("BC"));
-            }
-        });
-        // An alias for another algorithm, provider by name
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                return Signature.getInstance("MD5withRSAEncryption", "BC");
-            }
-        });
-        // An alias for another algorithm, provider by instance
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                return KeyGenerator.getInstance("HMAC-MD5", Security.getProvider("BC"));
-            }
-        });
-        // An alias with unusual characters, provider by name
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                return Mac.getInstance("Hmac/sha256", "BC");
-            }
-        });
-        // An alias with unusual characters, provider by instance
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                return Signature.getInstance("SHA384/rsA", Security.getProvider("BC"));
-            }
-        });
-        // An alias by OID, provider by name
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                // OID for SHA-256
-                return MessageDigest.getInstance("2.16.840.1.101.3.4.2.1", "BC");
-            }
-        });
-        // An alias by OID, provider by instance
-        BC_ALGORITHMS.add(new Algorithm() {
-            @Override
-            public Object getInstance() throws GeneralSecurityException {
-                // OID for AES-128
-                return AlgorithmParameters.getInstance("2.16.840.1.101.3.4.1.2",
-                        Security.getProvider("BC"));
-            }
-        });
         // All the same algorithms as for BC, but with no provider, which should produce
         // the Conscrypt implementation
         CONSCRYPT_ALGORITHMS.add(new Algorithm() {
@@ -179,6 +130,194 @@
         });
     }
 
+    private static final Set<String> REMOVED_BC_ALGORITHMS = new HashSet<String>();
+    static {
+        REMOVED_BC_ALGORITHMS.addAll(Arrays.asList(
+                "ALGORITHMPARAMETERS.1.2.840.113549.3.7",
+                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.2",
+                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.22",
+                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.26",
+                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.42",
+                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.46",
+                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.6",
+                "ALGORITHMPARAMETERS.AES",
+                "ALGORITHMPARAMETERS.DESEDE",
+                "ALGORITHMPARAMETERS.EC",
+                "ALGORITHMPARAMETERS.GCM",
+                "ALGORITHMPARAMETERS.OAEP",
+                "ALGORITHMPARAMETERS.TDEA",
+                "CERTIFICATEFACTORY.X.509",
+                "CERTIFICATEFACTORY.X509",
+                // List of Ciphers produced by ProviderOverlap:
+                "CIPHER.1.2.840.113549.3.4",
+                "CIPHER.2.16.840.1.101.3.4.1.26",
+                "CIPHER.2.16.840.1.101.3.4.1.46",
+                "CIPHER.2.16.840.1.101.3.4.1.6",
+                "CIPHER.AES/GCM/NOPADDING",
+                "CIPHER.ARC4",
+                "CIPHER.ARCFOUR",
+                "CIPHER.OID.1.2.840.113549.3.4",
+                "CIPHER.RC4",
+                // End of Ciphers produced by ProviderOverlap
+                // Additional ciphers transformations that will resolve to the same things as
+                // the automatically-produced overlap due to the Cipher transformation rules.
+                // These have been added manually.
+                "CIPHER.ARC4/ECB/NOPADDING",
+                "CIPHER.ARC4/NONE/NOPADDING",
+                "CIPHER.ARCFOUR/ECB/NOPADDING",
+                "CIPHER.ARCFOUR/NONE/NOPADDING",
+                "CIPHER.RC4/ECB/NOPADDING",
+                "CIPHER.RC4/NONE/NOPADDING",
+                // End of additional Ciphers
+                "KEYAGREEMENT.ECDH",
+                "KEYFACTORY.1.2.840.10045.2.1",
+                "KEYFACTORY.1.2.840.113549.1.1.1",
+                "KEYFACTORY.1.2.840.113549.1.1.7",
+                "KEYFACTORY.1.3.133.16.840.63.0.2",
+                "KEYFACTORY.2.5.8.1.1",
+                "KEYFACTORY.EC",
+                "KEYGENERATOR.1.2.840.113549.2.10",
+                "KEYGENERATOR.1.2.840.113549.2.11",
+                "KEYGENERATOR.1.2.840.113549.2.7",
+                "KEYGENERATOR.1.2.840.113549.2.8",
+                "KEYGENERATOR.1.2.840.113549.2.9",
+                "KEYGENERATOR.1.3.6.1.5.5.8.1.1",
+                "KEYGENERATOR.1.3.6.1.5.5.8.1.2",
+                "KEYGENERATOR.2.16.840.1.101.3.4.2.1",
+                "KEYGENERATOR.AES",
+                "KEYGENERATOR.DESEDE",
+                "KEYGENERATOR.HMAC-MD5",
+                "KEYGENERATOR.HMAC-SHA1",
+                "KEYGENERATOR.HMAC-SHA224",
+                "KEYGENERATOR.HMAC-SHA256",
+                "KEYGENERATOR.HMAC-SHA384",
+                "KEYGENERATOR.HMAC-SHA512",
+                "KEYGENERATOR.HMAC/MD5",
+                "KEYGENERATOR.HMAC/SHA1",
+                "KEYGENERATOR.HMAC/SHA224",
+                "KEYGENERATOR.HMAC/SHA256",
+                "KEYGENERATOR.HMAC/SHA384",
+                "KEYGENERATOR.HMAC/SHA512",
+                "KEYGENERATOR.HMACMD5",
+                "KEYGENERATOR.HMACSHA1",
+                "KEYGENERATOR.HMACSHA224",
+                "KEYGENERATOR.HMACSHA256",
+                "KEYGENERATOR.HMACSHA384",
+                "KEYGENERATOR.HMACSHA512",
+                "KEYGENERATOR.TDEA",
+                "KEYPAIRGENERATOR.1.2.840.10045.2.1",
+                "KEYPAIRGENERATOR.1.2.840.113549.1.1.1",
+                "KEYPAIRGENERATOR.1.2.840.113549.1.1.7",
+                "KEYPAIRGENERATOR.1.3.133.16.840.63.0.2",
+                "KEYPAIRGENERATOR.2.5.8.1.1",
+                "KEYPAIRGENERATOR.EC",
+                "KEYPAIRGENERATOR.RSA",
+                "MAC.1.2.840.113549.2.10",
+                "MAC.1.2.840.113549.2.11",
+                "MAC.1.2.840.113549.2.7",
+                "MAC.1.2.840.113549.2.8",
+                "MAC.1.2.840.113549.2.9",
+                "MAC.1.3.6.1.5.5.8.1.1",
+                "MAC.1.3.6.1.5.5.8.1.2",
+                "MAC.2.16.840.1.101.3.4.2.1",
+                "MAC.HMAC-MD5",
+                "MAC.HMAC-SHA1",
+                "MAC.HMAC-SHA224",
+                "MAC.HMAC-SHA256",
+                "MAC.HMAC-SHA384",
+                "MAC.HMAC-SHA512",
+                "MAC.HMAC/MD5",
+                "MAC.HMAC/SHA1",
+                "MAC.HMAC/SHA224",
+                "MAC.HMAC/SHA256",
+                "MAC.HMAC/SHA384",
+                "MAC.HMAC/SHA512",
+                "MAC.HMACMD5",
+                "MAC.HMACSHA1",
+                "MAC.HMACSHA224",
+                "MAC.HMACSHA256",
+                "MAC.HMACSHA384",
+                "MAC.HMACSHA512",
+                "MAC.PBEWITHHMACSHA224",
+                "MAC.PBEWITHHMACSHA256",
+                "MAC.PBEWITHHMACSHA384",
+                "MAC.PBEWITHHMACSHA512",
+                "MESSAGEDIGEST.1.2.840.113549.2.5",
+                "MESSAGEDIGEST.1.3.14.3.2.26",
+                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.1",
+                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.2",
+                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.3",
+                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.4",
+                "MESSAGEDIGEST.MD5",
+                "MESSAGEDIGEST.SHA",
+                "MESSAGEDIGEST.SHA-1",
+                "MESSAGEDIGEST.SHA-224",
+                "MESSAGEDIGEST.SHA-256",
+                "MESSAGEDIGEST.SHA-384",
+                "MESSAGEDIGEST.SHA-512",
+                "MESSAGEDIGEST.SHA1",
+                "MESSAGEDIGEST.SHA224",
+                "MESSAGEDIGEST.SHA256",
+                "MESSAGEDIGEST.SHA384",
+                "MESSAGEDIGEST.SHA512",
+                "SECRETKEYFACTORY.DESEDE",
+                "SECRETKEYFACTORY.TDEA",
+                "SIGNATURE.1.2.840.10045.4.1",
+                "SIGNATURE.1.2.840.10045.4.3.1",
+                "SIGNATURE.1.2.840.10045.4.3.2",
+                "SIGNATURE.1.2.840.10045.4.3.3",
+                "SIGNATURE.1.2.840.10045.4.3.4",
+                "SIGNATURE.1.2.840.113549.1.1.11",
+                "SIGNATURE.1.2.840.113549.1.1.12",
+                "SIGNATURE.1.2.840.113549.1.1.13",
+                "SIGNATURE.1.2.840.113549.1.1.14",
+                "SIGNATURE.1.2.840.113549.1.1.4",
+                "SIGNATURE.1.2.840.113549.1.1.5",
+                "SIGNATURE.1.3.14.3.2.29",
+                "SIGNATURE.ECDSA",
+                "SIGNATURE.ECDSAWITHSHA1",
+                "SIGNATURE.MD5/RSA",
+                "SIGNATURE.MD5WITHRSA",
+                "SIGNATURE.MD5WITHRSAENCRYPTION",
+                "SIGNATURE.NONEWITHECDSA",
+                "SIGNATURE.OID.1.2.840.10045.4.3.1",
+                "SIGNATURE.OID.1.2.840.10045.4.3.2",
+                "SIGNATURE.OID.1.2.840.10045.4.3.3",
+                "SIGNATURE.OID.1.2.840.10045.4.3.4",
+                "SIGNATURE.OID.1.2.840.113549.1.1.11",
+                "SIGNATURE.OID.1.2.840.113549.1.1.12",
+                "SIGNATURE.OID.1.2.840.113549.1.1.13",
+                "SIGNATURE.OID.1.2.840.113549.1.1.14",
+                "SIGNATURE.OID.1.2.840.113549.1.1.4",
+                "SIGNATURE.OID.1.2.840.113549.1.1.5",
+                "SIGNATURE.OID.1.3.14.3.2.29",
+                "SIGNATURE.SHA1/RSA",
+                "SIGNATURE.SHA1WITHECDSA",
+                "SIGNATURE.SHA1WITHRSA",
+                "SIGNATURE.SHA1WITHRSAENCRYPTION",
+                "SIGNATURE.SHA224/ECDSA",
+                "SIGNATURE.SHA224/RSA",
+                "SIGNATURE.SHA224WITHECDSA",
+                "SIGNATURE.SHA224WITHRSA",
+                "SIGNATURE.SHA224WITHRSAENCRYPTION",
+                "SIGNATURE.SHA256/ECDSA",
+                "SIGNATURE.SHA256/RSA",
+                "SIGNATURE.SHA256WITHECDSA",
+                "SIGNATURE.SHA256WITHRSA",
+                "SIGNATURE.SHA256WITHRSAENCRYPTION",
+                "SIGNATURE.SHA384/ECDSA",
+                "SIGNATURE.SHA384/RSA",
+                "SIGNATURE.SHA384WITHECDSA",
+                "SIGNATURE.SHA384WITHRSA",
+                "SIGNATURE.SHA384WITHRSAENCRYPTION",
+                "SIGNATURE.SHA512/ECDSA",
+                "SIGNATURE.SHA512/RSA",
+                "SIGNATURE.SHA512WITHECDSA",
+                "SIGNATURE.SHA512WITHRSA",
+                "SIGNATURE.SHA512WITHRSAENCRYPTION"
+        ));
+    }
+
     private static Provider getProvider(Object object) throws Exception {
         // Every JCA object has a getProvider() method
         Method m = object.getClass().getMethod("getProvider");
@@ -287,4 +426,47 @@
             Security.insertProviderAt(originalBouncyCastle, originalBouncyCastleIndex);
         }
     }
+    
+    @Test
+    public void testRemovedBCAlgorithms() throws Exception {
+        for (String fullAlgorithm : REMOVED_BC_ALGORITHMS) {
+            String[] parts = fullAlgorithm.split("\\.", 2);
+            assertEquals("Algortihm names are expected to be of format Type.Name",
+                2, parts.length);
+
+            Provider bcProvider = Security.getProvider("BC");
+            String type = parts[0];
+            String algorithm = parts[1];
+            try {
+                switch (parts[0]) {
+                    case "ALGORITHMPARAMETERS":
+                        AlgorithmParameters.getInstance(algorithm, bcProvider);
+                    case "CERTIFICATEFACTORY":
+                        CertificateFactory.getInstance(algorithm, bcProvider);
+                    case "CIPHER":
+                        Cipher.getInstance(algorithm, bcProvider);
+                    case "KEYAGREEMENT":
+                        KeyAgreement.getInstance(algorithm, bcProvider);
+                    case "KEYFACTORY":
+                        KeyFactory.getInstance(algorithm, bcProvider);
+                    case "KEYGENERATOR":
+                        KeyGenerator.getInstance(algorithm, bcProvider);
+                    case "KEYPAIRGENERATOR":
+                        KeyPairGenerator.getInstance(algorithm, bcProvider);
+                    case "MAC":
+                        Mac.getInstance(algorithm, bcProvider);
+                    case "MESSAGEDIGEST":
+                        MessageDigest.getInstance(algorithm, bcProvider);
+                    case "SECRETKEYFACTORY":
+                        SecretKeyFactory.getInstance(algorithm, bcProvider);
+                    case "SIGNATURE":
+                        Signature.getInstance(algorithm, bcProvider);
+                    default:
+                        fail("unhandled algorithm type " + parts[0]);
+                }
+                fail("getInstance should have thrown for type: " + parts[0] + ", name: " + algorithm);
+            } catch(CertificateException | NoSuchAlgorithmException expected) {
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/xml/PullParserTest.java b/luni/src/test/java/libcore/xml/PullParserTest.java
index a52db1d..3c74bb6 100644
--- a/luni/src/test/java/libcore/xml/PullParserTest.java
+++ b/luni/src/test/java/libcore/xml/PullParserTest.java
@@ -770,7 +770,7 @@
         XmlPullParser parser = newPullParser();
         String test = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                 "<!DOCTYPE root [\n" +
-                "<!ENTITY dummy \"dummy\">\n" +
+                "<!ENTITY fake \"fake\">\n" +
                 "]>  \n" +
                 "<root></root>";
         assertParseSuccess(test, parser);
@@ -781,7 +781,7 @@
         XmlPullParser parser = newPullParser();
         String test = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                 "<!DOCTYPE root [\n" +
-                "<!ENTITY dummy \"dummy\">\n" +
+                "<!ENTITY fake \"fake\">\n" +
                 "]>  \n" +
                 "<root></root>";
         assertParseSuccess(test, parser);
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/AlgorithmParametersTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/AlgorithmParametersTest.java
index 2315492..0da2d1b 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/AlgorithmParametersTest.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/AlgorithmParametersTest.java
@@ -74,11 +74,11 @@
     public void test_getAlgorithm() throws Exception {
 
         // test: null value
-        AlgorithmParameters ap = new DummyAlgorithmParameters(null, p, null);
+        AlgorithmParameters ap = new FakeAlgorithmParameters(null, p, null);
         assertNull(ap.getAlgorithm());
 
         // test: not null value
-        ap = new DummyAlgorithmParameters(null, p, "AAA");
+        ap = new FakeAlgorithmParameters(null, p, "AAA");
         assertEquals("AAA", ap.getAlgorithm());
     }
 
@@ -95,7 +95,7 @@
             }
         };
 
-        AlgorithmParameters params = new DummyAlgorithmParameters(paramSpi, p,
+        AlgorithmParameters params = new FakeAlgorithmParameters(paramSpi, p,
                 "algorithm");
 
         //
@@ -131,7 +131,7 @@
             }
         };
 
-        AlgorithmParameters params = new DummyAlgorithmParameters(paramSpi, p,
+        AlgorithmParameters params = new FakeAlgorithmParameters(paramSpi, p,
                 "algorithm");
 
         //
@@ -161,7 +161,7 @@
             }
         };
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(new MyAlgorithmParameterSpec());
         assertSame(enc, params.getEncoded(null));
     }
@@ -254,7 +254,7 @@
             }
         };
 
-        AlgorithmParameters params = new DummyAlgorithmParameters(paramSpi, p,
+        AlgorithmParameters params = new FakeAlgorithmParameters(paramSpi, p,
                 "algorithm");
 
         //
@@ -293,7 +293,7 @@
             }
         };
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(new MyAlgorithmParameterSpec());
         assertNull(params.getParameterSpec(null));
     }
@@ -340,11 +340,11 @@
      */
     public void test_getProvider() throws Exception {
         // test: null value
-        AlgorithmParameters ap = new DummyAlgorithmParameters(null, null, "AAA");
+        AlgorithmParameters ap = new FakeAlgorithmParameters(null, null, "AAA");
         assertNull(ap.getProvider());
 
         // test: not null value
-        ap = new DummyAlgorithmParameters(null, p, "AAA");
+        ap = new FakeAlgorithmParameters(null, p, "AAA");
         assertSame(p, ap.getProvider());
     }
 
@@ -367,7 +367,7 @@
             }
         };
 
-        AlgorithmParameters params = new DummyAlgorithmParameters(paramSpi, p,
+        AlgorithmParameters params = new FakeAlgorithmParameters(paramSpi, p,
                 "algorithm");
 
         params.init(spec);
@@ -383,7 +383,7 @@
             // expected
         }
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(new byte[0]);
         try {
             params.init(spec);
@@ -392,7 +392,7 @@
             // expected
         }
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(new byte[0], "format");
         try {
             params.init(spec);
@@ -413,7 +413,7 @@
             }
         };
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init((AlgorithmParameterSpec) null);
         assertTrue(paramSpi.runEngineInit_AlgParamSpec);
     }
@@ -435,7 +435,7 @@
             }
         };
 
-        AlgorithmParameters params = new DummyAlgorithmParameters(paramSpi, p,
+        AlgorithmParameters params = new FakeAlgorithmParameters(paramSpi, p,
                 "algorithm");
 
         params.init(enc);
@@ -451,7 +451,7 @@
             // expected
         }
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(new MyAlgorithmParameterSpec());
         try {
             params.init(enc);
@@ -460,7 +460,7 @@
             // expected
         }
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(enc, "format");
         try {
             params.init(enc);
@@ -480,7 +480,7 @@
             }
         };
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init((byte[]) null);
         assertTrue(paramSpi.runEngineInitB$);
     }
@@ -506,7 +506,7 @@
             }
         };
 
-        AlgorithmParameters params = new DummyAlgorithmParameters(paramSpi, p,
+        AlgorithmParameters params = new FakeAlgorithmParameters(paramSpi, p,
                 "algorithm");
 
         params.init(enc, strFormatParam);
@@ -522,7 +522,7 @@
             // expected
         }
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(new MyAlgorithmParameterSpec());
         try {
             params.init(enc, strFormatParam);
@@ -531,7 +531,7 @@
             // expected
         }
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(enc);
         try {
             params.init(enc, strFormatParam);
@@ -557,7 +557,7 @@
             }
         };
 
-        params = new DummyAlgorithmParameters(paramSpi, p, "algorithm");
+        params = new FakeAlgorithmParameters(paramSpi, p, "algorithm");
         params.init(null, null);
         assertTrue(paramSpi.runEngineInitB$String);
     }
@@ -575,7 +575,7 @@
             }
         };
 
-        AlgorithmParameters params = new DummyAlgorithmParameters(paramSpi, p,
+        AlgorithmParameters params = new FakeAlgorithmParameters(paramSpi, p,
                 "algorithm");
 
         assertNull("unititialized", params.toString());
@@ -690,8 +690,8 @@
     private class MyAlgorithmParameterSpec implements java.security.spec.AlgorithmParameterSpec{
     }
 
-    private class DummyAlgorithmParameters extends AlgorithmParameters {
-        public DummyAlgorithmParameters(AlgorithmParametersSpi paramSpi,
+    private class FakeAlgorithmParameters extends AlgorithmParameters {
+        public FakeAlgorithmParameters(AlgorithmParametersSpi paramSpi,
                 Provider provider, String algorithm) {
             super(paramSpi, provider, algorithm);
         }
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/Identity2Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/Identity2Test.java
index 1362fc6..2cbf1ce 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/Identity2Test.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/Identity2Test.java
@@ -253,6 +253,9 @@
         java.security.Certificate[] certs = sub.certificates();
         assertEquals("Certificate not contained in the identity",
                      certs[0], certImpl);
+
+        sub.removeCertificate(certs[0]);
+        assertEquals(0, sub.certificates().length);
     }
 
     /**
@@ -333,7 +336,7 @@
      * java.security.Identity#toString()
      */
     public void test_toString() throws Exception {
-        IdentitySubclass sub = new IdentitySubclass("test", null);
+        Identity sub = new IdentitySubclass("test", null);
         assertNotNull(sub.toString());
         assertTrue("The String returned is not valid", sub.toString().length() > 0);
         // Regression for HARMONY-1566
@@ -344,7 +347,7 @@
      * java.security.Identity#toString(boolean)
      */
     public void test_toStringZ() throws Exception {
-        IdentitySubclass sub = new IdentitySubclass("test", null);
+        Identity sub = new IdentitySubclass("test", null);
         assertNotNull(sub.toString(true));
         assertTrue("The String returned is not valid", sub.toString(true).length() > 0);
     }
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyFactory2Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyFactory2Test.java
index f6210a9..594219b 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyFactory2Test.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyFactory2Test.java
@@ -329,7 +329,14 @@
         while (e.hasMoreElements()) {
             String algorithm = (String) e.nextElement();
             if (algorithm.startsWith(KEYFACTORY_ID) && !algorithm.contains(" ")) {
-                algs.addElement(algorithm.substring(KEYFACTORY_ID.length()));
+                String keyFactoryName = algorithm.substring(KEYFACTORY_ID.length());
+                if ("XDH".equals(keyFactoryName)) {
+                    // Skip Elliptic curve DH as the KeySpec classes needed for this
+                    // test aren't yet available on Android. XDH is fully tested in Conscrypt
+                    // tests. b/179675498
+                    continue;
+                }
+                algs.addElement(keyFactoryName);
             }
         }
 
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore4Test.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore4Test.java
index 4178db0..de8a161 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore4Test.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/KeyStore4Test.java
@@ -271,7 +271,7 @@
         }
 
         try {
-            String certificateAlias = keyStore.getCertificateAlias(new MyCertificate("dummy", null));
+            String certificateAlias = keyStore.getCertificateAlias(new MyCertificate("fake", null));
             assertNull("alias was not null", certificateAlias);
         } catch (KeyStoreException e) {
             fail("unexpected exception: " + e);
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 be3e6d1..494ba65 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
@@ -97,7 +97,7 @@
         BadKeyStoreEntry badEntry = new BadKeyStoreEntry();
         BadKeyStoreProtectionParameter badParameter = new BadKeyStoreProtectionParameter();
 
-        KeyStore.SecretKeyEntry dummyEntry = new KeyStore.SecretKeyEntry(new SecretKey() {
+        KeyStore.SecretKeyEntry fakeEntry = new KeyStore.SecretKeyEntry(new SecretKey() {
             @Override public String getAlgorithm() { return null; }
             @Override public String getFormat() { return null; }
             @Override public byte[] getEncoded() { return null; }
@@ -132,7 +132,7 @@
         } catch (KeyStoreException expected) {
         }
 
-        ksSpi.engineSetEntry("aaa", dummyEntry, null);
+        ksSpi.engineSetEntry("aaa", fakeEntry, null);
         assertTrue(keyEntryWasSet[0]);
     }
 
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureSpiTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureSpiTest.java
index ea0bd7f..97f66e6 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureSpiTest.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/SignatureSpiTest.java
@@ -80,7 +80,7 @@
         // or rather test that no UnsupportedOperationException is thrown?
 
         @SuppressWarnings("unused")
-        Signature s = new Signature("dummy") {
+        Signature s = new Signature("fake") {
             protected AlgorithmParameters engineGetParameters() {
                 engineGetParametersCalled = true;
                 try {
@@ -310,7 +310,7 @@
     }
 
     public void testEngineInitSign_PrivateKey_SecureRandom() {
-        MySignature signature = new MySignature("dummy");
+        MySignature signature = new MySignature("fake");
 
         try {
             signature.initSign(null, null);
@@ -323,7 +323,7 @@
 
     public void testEngineSetParameter()
     {
-        MySignature signature = new MySignature("dummy");
+        MySignature signature = new MySignature("fake");
 
         try {
             signature.setParameter(null);
@@ -337,7 +337,7 @@
     }
 
     public void testEngineSign_BII() {
-        MySignature signature = new MySignature("dummy");
+        MySignature signature = new MySignature("fake");
         try {
             signature.initSign(new PrivateKey() {
 
@@ -367,7 +367,7 @@
     }
 
     public void testEngineUpdate_ByteBuffer() {
-        MySignature signature = new MySignature("dummy");
+        MySignature signature = new MySignature("fake");
         try {
             signature.initSign(new PrivateKey() {
 
@@ -397,7 +397,7 @@
     }
 
     public void testEngineVerify_BII() {
-        MySignature signature = new MySignature("dummy");
+        MySignature signature = new MySignature("fake");
 
         try {
             signature.initVerify(new PublicKey() {
diff --git a/luni/src/test/java/tests/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java b/luni/src/test/java/tests/com/android/org/bouncycastle/jce/provider/CertBlocklistTest.java
similarity index 73%
rename from luni/src/test/java/tests/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
rename to luni/src/test/java/tests/com/android/org/bouncycastle/jce/provider/CertBlocklistTest.java
index 40ba176..4aebdb1 100644
--- a/luni/src/test/java/tests/com/android/org/bouncycastle/jce/provider/CertBlacklistTest.java
+++ b/luni/src/test/java/tests/com/android/org/bouncycastle/jce/provider/CertBlocklistTest.java
@@ -18,7 +18,7 @@
 
 import junit.framework.TestCase;
 
-import com.android.org.bouncycastle.jce.provider.CertBlacklist;
+import com.android.org.bouncycastle.jce.provider.CertBlocklist;
 import com.android.org.bouncycastle.util.encoders.Base64;
 import com.android.org.bouncycastle.util.encoders.Hex;
 
@@ -35,7 +35,7 @@
 import java.util.HashSet;
 import java.util.Set;
 
-public class CertBlacklistTest extends TestCase {
+public class CertBlocklistTest extends TestCase {
 
     private File tmpFile;
 
@@ -125,7 +125,7 @@
                     "Y3ldKwvYBW3W3yNZdTHbPyNsPJdhqA55mDNsteE5YTp1PyySDb1MSVrbxDEruoH6ZE99Hob4Ih8A" +
                     "mn7MHZatGClECgjXWFZ2Gxa7OUCaQpcH8g==";
 
-    public CertBlacklistTest() throws IOException {
+    public CertBlocklistTest() throws IOException {
         tmpFile = File.createTempFile("test", "");
         DEFAULT_PUBKEYS = getDefaultPubkeys();
         DEFAULT_SERIALS = getDefaultSerials();
@@ -147,11 +147,11 @@
         }
     }
 
-    private Set<String> getPubkeyBlacklist(String path) throws IOException {
-        // set our blacklist path
-        CertBlacklist bl = new CertBlacklist(path, "");
-        // call readPubkeyBlacklist
-        Set<byte[]> arr = bl.pubkeyBlacklist;
+    private Set<String> getPubkeyBlocklist(String path) throws IOException {
+        // set our blocklist path
+        CertBlocklist bl = new CertBlocklist(path, "");
+        // call readPubkeyBlocklist
+        Set<byte[]> arr = bl.pubkeyBlocklist;
         // convert the results to a hashset of strings
         Set<String> results = new HashSet<String>();
         for (byte[] value: arr) {
@@ -160,11 +160,11 @@
         return results;
     }
 
-    private Set<String> getSerialBlacklist(String path) throws IOException {
-        // set our blacklist path
-        CertBlacklist bl = new CertBlacklist("", path);
-        // call readPubkeyBlacklist
-        Set<BigInteger> arr = bl.serialBlacklist;
+    private Set<String> getSerialBlocklist(String path) throws IOException {
+        // set our blocklist path
+        CertBlocklist bl = new CertBlocklist("", path);
+        // call readPubkeyBlocklist
+        Set<BigInteger> arr = bl.serialBlocklist;
         // convert the results to a hashset of strings
         Set<String> results = new HashSet<String>();
         for (BigInteger value: arr) {
@@ -181,28 +181,28 @@
     }
 
     private Set<String> getDefaultPubkeys() throws IOException {
-        return getPubkeyBlacklist("");
+        return getPubkeyBlocklist("");
     }
 
     private Set<String> getDefaultSerials() throws IOException {
-        return getSerialBlacklist("");
+        return getSerialBlocklist("");
     }
 
-    private Set<String> getCurrentPubkeyBlacklist() throws IOException {
-        return getPubkeyBlacklist(tmpFile.getCanonicalPath());
+    private Set<String> getCurrentPubkeyBlocklist() throws IOException {
+        return getPubkeyBlocklist(tmpFile.getCanonicalPath());
     }
 
-    private Set<String> getCurrentSerialBlacklist() throws IOException {
-        return getSerialBlacklist(tmpFile.getCanonicalPath());
+    private Set<String> getCurrentSerialBlocklist() throws IOException {
+        return getSerialBlocklist(tmpFile.getCanonicalPath());
     }
 
-    private void blacklistToFile(String blacklist) throws IOException {
+    private void blocklistToFile(String blocklist) throws IOException {
         FileOutputStream out = new FileOutputStream(tmpFile);
-        out.write(blacklist.toString().getBytes());
+        out.write(blocklist.toString().getBytes());
         out.close();
     }
 
-    private void writeBlacklist(HashSet<String> values) throws IOException {
+    private void writeBlocklist(HashSet<String> values) throws IOException {
         StringBuilder result = new StringBuilder();
         // join the values into a string
         for (String value : values) {
@@ -211,7 +211,7 @@
             }
             result.append(value);
         }
-        blacklistToFile(result.toString());
+        blocklistToFile(result.toString());
     }
 
     private static PublicKey createPublicKey(String cert) throws Exception {
@@ -229,203 +229,203 @@
         return xCert.getSerialNumber();
     }
 
-    public void testPubkeyBlacklistLegit() throws Exception {
-        // build the blacklist
+    public void testPubkeyBlocklistLegit() throws Exception {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccc");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default pubkeys into the bl
         bl.addAll(DEFAULT_PUBKEYS);
         // do the test
-        assertEquals(bl, getCurrentPubkeyBlacklist());
+        assertEquals(bl, getCurrentPubkeyBlocklist());
     }
 
-    public void testLegitPubkeyIsntBlacklisted() throws Exception {
+    public void testLegitPubkeyIsntBlocklisted() throws Exception {
         // build the public key
         PublicKey pk = createPublicKey(TEST_CERT);
-        // write that to the test blacklist
-        writeBlacklist(new HashSet<String>());
-        // set our blacklist path
-        CertBlacklist bl = new CertBlacklist(tmpFile.getCanonicalPath(), "");
-        // check to make sure it isn't blacklisted
-        assertEquals(bl.isPublicKeyBlackListed(pk), false);
+        // write that to the test blocklist
+        writeBlocklist(new HashSet<String>());
+        // set our blocklist path
+        CertBlocklist bl = new CertBlocklist(tmpFile.getCanonicalPath(), "");
+        // check to make sure it isn't blocklisted
+        assertEquals(bl.isPublicKeyBlockListed(pk), false);
     }
 
-    public void testPubkeyIsBlacklisted() throws Exception {
+    public void testPubkeyIsBlocklisted() throws Exception {
         // build the public key
         PublicKey pk = createPublicKey(TEST_CERT);
         // get its hash
         String hash = getHash(pk);
-        // write that to the test blacklist
-        HashSet<String> testBlackList = new HashSet<String>();
-        testBlackList.add(hash);
-        writeBlacklist(testBlackList);
-        // set our blacklist path
-        CertBlacklist bl = new CertBlacklist(tmpFile.getCanonicalPath(), "");
-        // check to make sure it isn't blacklited
-        assertTrue(bl.isPublicKeyBlackListed(pk));
+        // write that to the test blocklist
+        HashSet<String> testBlockList = new HashSet<String>();
+        testBlockList.add(hash);
+        writeBlocklist(testBlockList);
+        // set our blocklist path
+        CertBlocklist bl = new CertBlocklist(tmpFile.getCanonicalPath(), "");
+        // check to make sure it isn't blocklited
+        assertTrue(bl.isPublicKeyBlockListed(pk));
     }
 
-    public void testSerialBlacklistLegit() throws IOException {
-        // build the blacklist
+    public void testSerialBlocklistLegit() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("22e514121e61c643b1e9b06bd4b9f7d0");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default serials into the bl
         bl.addAll(DEFAULT_SERIALS);
         // do the test
-        assertEquals(bl, getCurrentSerialBlacklist());
+        assertEquals(bl, getCurrentSerialBlocklist());
     }
 
-    public void testPubkeyBlacklistMultipleLegit() throws IOException {
-        // build the blacklist
+    public void testPubkeyBlocklistMultipleLegit() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccc");
         bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccd");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default pubkeys into the bl
         bl.addAll(DEFAULT_PUBKEYS);
         // do the test
-        assertEquals(bl, getCurrentPubkeyBlacklist());
+        assertEquals(bl, getCurrentPubkeyBlocklist());
     }
 
-    public void testSerialBlacklistMultipleLegit() throws IOException {
-        // build the blacklist
+    public void testSerialBlocklistMultipleLegit() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("22e514121e61c643b1e9b06bd4b9f7d0");
         bl.add("22e514121e61c643b1e9b06bd4b9f7d1");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default serials into the bl
         bl.addAll(DEFAULT_SERIALS);
         // do the test
-        assertEquals(bl, getCurrentSerialBlacklist());
+        assertEquals(bl, getCurrentSerialBlocklist());
     }
 
-    public void testPubkeyBlacklistMultipleBad() throws IOException {
-        // build the blacklist
+    public void testPubkeyBlocklistMultipleBad() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccc");
         bl.add("");
         bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091ccd");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default pubkeys into the bl
         bl.addAll(DEFAULT_PUBKEYS);
         // remove the bad one
         bl.remove("");
         // do the test- results should be all but the bad one are handled
-        assertEquals(bl, getCurrentPubkeyBlacklist());
+        assertEquals(bl, getCurrentPubkeyBlocklist());
     }
 
-    public void testSerialBlacklistMultipleBad() throws IOException {
-        // build the blacklist
+    public void testSerialBlocklistMultipleBad() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("22e514121e61c643b1e9b06bd4b9f7d0");
         bl.add("");
         bl.add("22e514121e61c643b1e9b06bd4b9f7d1");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default serials into the bl
         bl.addAll(DEFAULT_SERIALS);
         // remove the bad one
         bl.remove("");
         // do the test- results should be all but the bad one are handled
-        assertEquals(bl, getCurrentSerialBlacklist());
+        assertEquals(bl, getCurrentSerialBlocklist());
     }
 
-    public void testPubkeyBlacklistDoesntExist() throws IOException {
-        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlacklist());
+    public void testPubkeyBlocklistDoesntExist() throws IOException {
+        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlocklist());
     }
 
-    public void testSerialBlacklistDoesntExist() throws IOException {
-        assertEquals(DEFAULT_SERIALS, getCurrentSerialBlacklist());
+    public void testSerialBlocklistDoesntExist() throws IOException {
+        assertEquals(DEFAULT_SERIALS, getCurrentSerialBlocklist());
     }
 
-    public void testPubkeyBlacklistNotHexValues() throws IOException {
-        // build the blacklist
+    public void testPubkeyBlocklistNotHexValues() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // do the test
-        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlacklist());
+        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlocklist());
     }
 
-    public void testSerialBlacklistNotHexValues() throws IOException {
-        // build the blacklist
+    public void testSerialBlocklistNotHexValues() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // do the test
-        assertEquals(DEFAULT_SERIALS, getCurrentSerialBlacklist());
+        assertEquals(DEFAULT_SERIALS, getCurrentSerialBlocklist());
     }
 
-    public void testPubkeyBlacklistIncorrectLength() throws IOException {
-        // build the blacklist
+    public void testPubkeyBlocklistIncorrectLength() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("6ccabd7db47e94a5759901b6a7dfd45d1c091cc");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // do the test
-        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlacklist());
+        assertEquals(DEFAULT_PUBKEYS, getCurrentPubkeyBlocklist());
     }
 
-    public void testSerialBlacklistZero() throws IOException {
-        // build the blacklist
+    public void testSerialBlocklistZero() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("0");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default serials
         bl.addAll(DEFAULT_SERIALS);
         // do the test
-        assertEquals(bl, getCurrentSerialBlacklist());
+        assertEquals(bl, getCurrentSerialBlocklist());
     }
 
-    public void testSerialBlacklistNegative() throws IOException {
-        // build the blacklist
+    public void testSerialBlocklistNegative() throws IOException {
+        // build the blocklist
         HashSet<String> bl = new HashSet<String>();
         bl.add("-1");
-        // write the blacklist
-        writeBlacklist(bl);
+        // write the blocklist
+        writeBlocklist(bl);
         // add the default serials
         bl.addAll(DEFAULT_SERIALS);
         // do the test
-        assertEquals(bl, getCurrentSerialBlacklist());
+        assertEquals(bl, getCurrentSerialBlocklist());
     }
 
-    public void testTurkTrustIntermediate1PubkeyBlacklist() throws Exception {
+    public void testTurkTrustIntermediate1PubkeyBlocklist() throws Exception {
         // build the public key
         PublicKey pk = createPublicKey(TURKTRUST_1);
-        // write that to the test blacklist
-        writeBlacklist(new HashSet<String>());
-        // set our blacklist path
-        CertBlacklist bl = new CertBlacklist();
-        // check to make sure it isn't blacklisted
-        assertEquals(bl.isPublicKeyBlackListed(pk), true);
+        // write that to the test blocklist
+        writeBlocklist(new HashSet<String>());
+        // set our blocklist path
+        CertBlocklist bl = new CertBlocklist();
+        // check to make sure it isn't blocklisted
+        assertEquals(bl.isPublicKeyBlockListed(pk), true);
     }
 
-    public void testTurkTrustIntermediate2PubkeyBlacklist() throws Exception {
+    public void testTurkTrustIntermediate2PubkeyBlocklist() throws Exception {
         // build the public key
         PublicKey pk = createPublicKey(TURKTRUST_2);
-        // set our blacklist path
-        CertBlacklist bl = new CertBlacklist();
-        // check to make sure it isn't blacklisted
-        assertEquals(bl.isPublicKeyBlackListed(pk), true);
+        // set our blocklist path
+        CertBlocklist bl = new CertBlocklist();
+        // check to make sure it isn't blocklisted
+        assertEquals(bl.isPublicKeyBlockListed(pk), true);
     }
 
-    public void testANSSIIntermediatePubkeyBlacklist() throws Exception {
+    public void testANSSIIntermediatePubkeyBlocklist() throws Exception {
         // build the public key
         PublicKey pk = createPublicKey(ANSSI);
-        // set our blacklist path
-        CertBlacklist bl = new CertBlacklist();
-        // check to make sure it isn't blacklisted
-        assertEquals(bl.isPublicKeyBlackListed(pk), true);
+        // set our blocklist path
+        CertBlocklist bl = new CertBlocklist();
+        // check to make sure it isn't blocklisted
+        assertEquals(bl.isPublicKeyBlockListed(pk), true);
     }
 
     private static void printHash(String cert) throws Exception {
diff --git a/luni/src/test/java/tests/java/security/SecureClassLoaderTest.java b/luni/src/test/java/tests/java/security/SecureClassLoaderTest.java
index 9a0761b..c88ff50 100644
--- a/luni/src/test/java/tests/java/security/SecureClassLoaderTest.java
+++ b/luni/src/test/java/tests/java/security/SecureClassLoaderTest.java
@@ -22,15 +22,12 @@
 
 package tests.java.security;
 
-import dalvik.annotation.KnownFailure;
-
 import junit.framework.TestCase;
 
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.nio.ByteBuffer;
 import java.security.CodeSource;
-import java.security.Permission;
 import java.security.PermissionCollection;
 import java.security.ProtectionDomain;
 import java.security.SecureClassLoader;
@@ -165,13 +162,24 @@
             (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00,
             (byte) 0x21, };
 
-    @KnownFailure("Android doesn't allow null parent.")
     public void testSecureClassLoaderClassLoader() throws Exception {
         URL[] urls = new URL[] { new URL("http://localhost") };
         URLClassLoader ucl = URLClassLoader.newInstance(urls);
-        new MyClassLoader(ucl);
+        MyClassLoader classLoader = new MyClassLoader(ucl);
 
         try {
+            classLoader.tryDefineClass("ClassA", new byte[0], 0, 0, null);
+            fail("SecureClassloader.defineClass doesn't throw");
+        } catch (UnsupportedOperationException e) {}
+
+        try {
+            classLoader.tryDefineClass("ClassB", ByteBuffer.wrap(new byte[0]), null);
+            fail("SecureClassloader.defineClass doesn't throw");
+        } catch (UnsupportedOperationException e) {}
+    }
+
+    public void testNullParent() {
+        try {
             new MyClassLoader(null);
         } catch (Exception e) {
             fail("unexpected exception: " + e);
@@ -233,5 +241,14 @@
             return defineClass(name, b, off, len, cs);
         }
 
+        public Class<?> tryDefineClass(String name, byte[] bytes, int off, int len,
+                CodeSource codeSource) {
+            return this.defineClass(name, bytes, off, len, codeSource);
+        }
+
+        public Class<?> tryDefineClass(String name, ByteBuffer byteBuffer, CodeSource codeSource) {
+            return this.defineClass(name, byteBuffer, codeSource);
+        }
+
     }
 }
diff --git a/luni/src/test/java/tests/java/sql/StressTest.java b/luni/src/test/java/tests/java/sql/StressTest.java
index b981d5e..03c6b85 100644
--- a/luni/src/test/java/tests/java/sql/StressTest.java
+++ b/luni/src/test/java/tests/java/sql/StressTest.java
@@ -16,31 +16,40 @@
 
 package tests.java.sql;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
-import java.sql.Driver;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.util.Properties;
 import java.util.Vector;
 import java.util.logging.Logger;
 
 import tests.support.DatabaseCreator;
 import tests.support.Support_SQL;
 import tests.support.ThreadPool;
-import junit.framework.TestCase;
 
-public class StressTest extends TestCase {
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class StressTest {
     Vector<Connection> vc = new Vector<Connection>();
 
     private static Connection conn;
 
     private static Statement statement;
 
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         Support_SQL.loadDriver();
         conn = Support_SQL.getConnection();
         statement = conn.createStatement();
@@ -48,11 +57,11 @@
         vc.clear();
     }
 
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         closeConnections();
         statement.close();
         conn.close();
-        super.tearDown();
     }
 
     private void createTestTables() {
@@ -105,6 +114,7 @@
      * StressTest#testManyConnectionsUsingOneThread(). Create many
      *        connections to the DataBase using one thread.
      */
+    @Test
     public void testManyConnectionsUsingOneThread() {
         try {
             int maxConnections = getConnectionNum();
@@ -120,6 +130,7 @@
      * StressTest#testManyConnectionsUsingManyThreads(). Create many
      *        connections to the DataBase using some threads.
      */
+    @Test
     public void testManyConnectionsUsingManyThreads() {
         int numTasks = getConnectionNum();
 
@@ -149,6 +160,7 @@
      * StressTest#testInsertOfManyRowsUsingOneThread(). Insert a lot of
      *        records to the Database using a maximum number of connections.
      */
+    @Test
     public void testInsertOfManyRowsUsingOneThread() {
 
         Logger.global
@@ -187,7 +199,10 @@
 
     /**
      * @tests
+     * TODO(b/169673091): Investigate why it could occasionally take 10min rather than 10s to
+     * finish.
      */
+    @Test
     public void testInsertOfManyRowsUsingManyThreads() {
         Logger.global.info("java.sql stress test: multiple threads and many operations.");
 
@@ -204,6 +219,7 @@
         }
         // close the pool and wait for all tasks to finish.
         threadPool.join();
+        Logger.global.info("All threads joined");
         assertEquals("Unable to create a connection", numConnections, vc.size());
 
         try {
@@ -211,11 +227,12 @@
                     .executeQuery("SELECT COUNT(*) as counter FROM "
                             + DatabaseCreator.TEST_TABLE2);
             assertTrue("RecordSet is empty", rs.next());
-
+            Logger.global.info("Counting statement returned");
 
             assertEquals("Incorrect number of records", tasksPerConnection
                     * numConnections, rs.getInt("counter"));
             rs.close();
+            Logger.global.info("ResultSet closed");
         } catch (SQLException sql) {
             fail("Unexpected SQLException " + sql.toString());
 
diff --git a/luni/src/test/java/tests/org/w3c/dom/LSExceptionTest.java b/luni/src/test/java/tests/org/w3c/dom/LSExceptionTest.java
new file mode 100644
index 0000000..cafe3fd
--- /dev/null
+++ b/luni/src/test/java/tests/org/w3c/dom/LSExceptionTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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 tests.org.w3c.dom;
+
+import junit.framework.TestCase;
+
+import org.w3c.dom.DOMError;
+import org.w3c.dom.ls.LSException;
+
+public class LSExceptionTest extends TestCase {
+
+    public void testConstructor() {
+        String msg = "Test message";
+        try {
+            throw new LSException(DOMError.SEVERITY_FATAL_ERROR, msg);
+        } catch (LSException e) {
+            assertEquals(DOMError.SEVERITY_FATAL_ERROR, e.code);
+            assertEquals(msg, e.getMessage());
+        }
+    }
+}
diff --git a/luni/src/test/java/tests/org/w3c/dom/OwnerDocument.java b/luni/src/test/java/tests/org/w3c/dom/OwnerDocument.java
index eb1a8df..e168bea 100644
--- a/luni/src/test/java/tests/org/w3c/dom/OwnerDocument.java
+++ b/luni/src/test/java/tests/org/w3c/dom/OwnerDocument.java
@@ -31,7 +31,7 @@
  *     The "getOwnerDocument()" method returns null if the target
  *     node itself is a DocumentType which is not used with any document yet.
  *
- *     Invoke the "getOwnerDocument()" method on the master
+ *     Invoke the "getOwnerDocument()" method on the root
  *     document.   The DocumentType returned should be null.
 * @author NIST
 * @author Mary Brady
diff --git a/luni/src/test/java/tests/security/cert/CertPathBuilderSpiTest.java b/luni/src/test/java/tests/security/cert/CertPathBuilderSpiTest.java
index b5615cc..941db69 100644
--- a/luni/src/test/java/tests/security/cert/CertPathBuilderSpiTest.java
+++ b/luni/src/test/java/tests/security/cert/CertPathBuilderSpiTest.java
@@ -22,6 +22,8 @@
 
 package tests.security.cert;
 
+import java.security.cert.CertPathChecker;
+import java.security.cert.Certificate;
 import junit.framework.TestCase;
 
 import java.security.InvalidAlgorithmParameterException;
@@ -50,8 +52,55 @@
             certPathBuilder.engineBuild(cpp);
             fail("CertPathBuilderException must be thrown");
         } catch (CertPathBuilderException e) {
+            // Expected
         }
         CertPathBuilderResult cpbResult = certPathBuilder.engineBuild(cpp);
         assertNull("Not null CertPathBuilderResult", cpbResult);
     }
+
+    public void testEngineGetRevocationChecker_Unsupported() {
+        // MyCertPathBuilderSpi doesn't implement engineGetRevocationChecker()
+        CertPathBuilderSpi certPathBuilder = new MyCertPathBuilderSpi();
+        try {
+            certPathBuilder.engineGetRevocationChecker();
+            fail();
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    public void testEngineGetRevocationChecker_Supported() {
+        CertPathBuilderSpi certPathBuilder = new PathBuilderSpi();
+        assertEquals(PathChecker.class, certPathBuilder.engineGetRevocationChecker().getClass());
+    }
+
+    // Stub CertPathBuilderSpi which has a revocation checker.
+    private static class PathBuilderSpi extends CertPathBuilderSpi {
+        @Override
+        public CertPathBuilderResult engineBuild(CertPathParameters params) {
+            throw new AssertionError("Stub");
+        }
+
+        @Override
+        public CertPathChecker engineGetRevocationChecker() {
+            return new PathChecker();
+        }
+    }
+
+    private static class PathChecker implements CertPathChecker {
+        @Override
+        public void init(boolean forward) {
+            throw new AssertionError("Stub");
+        }
+
+        @Override
+        public boolean isForwardCheckingSupported() {
+            throw new AssertionError("Stub");
+        }
+
+        @Override
+        public void check(Certificate cert) {
+            throw new AssertionError("Stub");
+        }
+    }
 }
diff --git a/luni/src/test/java/tests/security/cert/CertPathValidatorSpiTest.java b/luni/src/test/java/tests/security/cert/CertPathValidatorSpiTest.java
new file mode 100644
index 0000000..8235ee7
--- /dev/null
+++ b/luni/src/test/java/tests/security/cert/CertPathValidatorSpiTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 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 tests.security.cert;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.security.cert.CertPath;
+import java.security.cert.CertPathChecker;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertPathValidatorResult;
+import java.security.cert.CertPathValidatorSpi;
+import java.security.cert.Certificate;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CertPathValidatorSpiTest {
+  // engineValidate() implementations are tested via the CertPathValidator*Tests.
+
+  @Test
+  public void engineGetRevocationChecker_Unsupported() {
+    CertPathValidatorSpi spi = new PathValidatorSpiNoRevocation();
+    try {
+      spi.engineGetRevocationChecker();
+      fail();
+    } catch (UnsupportedOperationException e) {
+      // Expected
+    }
+  }
+
+  @Test
+  public void engineGetRevocationChecker_Supported() {
+    CertPathValidatorSpi spi = new PathValidatorSpiWithRevocation();
+    assertEquals(PathChecker.class, spi.engineGetRevocationChecker().getClass());
+  }
+
+  // Stub CertPathValidatorSpi which has no revocation checker.
+  private static class PathValidatorSpiNoRevocation extends CertPathValidatorSpi {
+    @Override
+    public CertPathValidatorResult engineValidate(CertPath certPath, CertPathParameters params) {
+      throw new AssertionError("Stub");
+    }
+  }
+
+  // Stub CertPathValidatorSpi which has a revocation checker.
+  private static class PathValidatorSpiWithRevocation extends CertPathValidatorSpi {
+
+    @Override
+    public CertPathValidatorResult engineValidate(CertPath certPath, CertPathParameters params) {
+      throw new AssertionError("Stub");
+    }
+
+    @Override
+    public CertPathChecker engineGetRevocationChecker() {
+      return new PathChecker();
+    }
+  }
+
+  private static class PathChecker implements CertPathChecker {
+    @Override
+    public void init(boolean forward) {
+      throw new AssertionError("Stub");
+    }
+
+    @Override
+    public boolean isForwardCheckingSupported() {
+      throw new AssertionError("Stub");
+    }
+
+    @Override
+    public void check(Certificate cert) {
+      throw new AssertionError("Stub");
+    }
+  }
+}
diff --git a/luni/src/test/java/tests/security/cert/PKIXCertPathCheckerTest.java b/luni/src/test/java/tests/security/cert/PKIXCertPathCheckerTest.java
index 44a2fa4..ebf82b2 100644
--- a/luni/src/test/java/tests/security/cert/PKIXCertPathCheckerTest.java
+++ b/luni/src/test/java/tests/security/cert/PKIXCertPathCheckerTest.java
@@ -74,21 +74,24 @@
         PKIXCertPathChecker pc = TestUtils.getTestCertPathChecker();
         pc.getSupportedExtensions();
     }
-    public final void testCheck() throws CertPathValidatorException {
+    public final void testCheck_Cert_Collection() throws CertPathValidatorException {
         PKIXCertPathChecker pc = TestUtils.getTestCertPathChecker();
-        pc.check(new MyCertificate("", null), new HashSet<String>());
+        pc.check(new MyCertificate("", null), new HashSet<>());
     }
 
-    class MyPKIXCertPathChecker extends PKIXCertPathChecker {
+    public final void testCheck_Cert() throws CertPathValidatorException {
+        PKIXCertPathChecker pc = TestUtils.getTestCertPathChecker();
+        pc.check(new MyCertificate("", null));
+    }
+
+    private static class MyPKIXCertPathChecker extends PKIXCertPathChecker {
 
         public MyPKIXCertPathChecker() {
             super();
         }
 
         @Override
-        public void check(Certificate cert,
-                Collection<String> unresolvedCritExts)
-        throws CertPathValidatorException {
+        public void check(Certificate cert, Collection<String> unresolvedCritExts) {
         }
 
         @Override
@@ -97,14 +100,12 @@
         }
 
         @Override
-        public void init(boolean forward) throws CertPathValidatorException {
+        public void init(boolean forward) {
         }
 
         @Override
         public boolean isForwardCheckingSupported() {
             return false;
         }
-
     }
-
 }
diff --git a/luni/src/test/java/tests/security/cert/X509Certificate2Test.java b/luni/src/test/java/tests/security/cert/X509Certificate2Test.java
index bf736d5..c1f5993 100644
--- a/luni/src/test/java/tests/security/cert/X509Certificate2Test.java
+++ b/luni/src/test/java/tests/security/cert/X509Certificate2Test.java
@@ -110,44 +110,47 @@
             + "XEa7ONzcHQTYTG10poHfOK/a0BaULF3GlctDESilwQYbW5BdfpAlZpbH"
             + "AFLcUDh6Eq50kc0A/anh/j3mgBNuvbIMo7hHNnZB6k/prswm2BszyLD"
             + "yw==";
-    private static String CERT_CORRECT =
-        "-----BEGIN CERTIFICATE-----\n"
-        + "MIIC+jCCAragAwIBAgICAiswDAYHKoZIzjgEAwEBADAdMRswGQYDVQQKExJDZXJ0a"
-        + "WZpY2F0ZSBJc3N1ZXIwIhgPMTk3MDAxMTIxMzQ2NDBaGA8xOTcwMDEyNDAzMzMyMF"
-        + "owHzEdMBsGA1UEChMUU3ViamVjdCBPcmdhbml6YXRpb24wGTAMBgcqhkjOOAQDAQE"
-        + "AAwkAAQIDBAUGBwiBAgCqggIAVaOCAhQwggIQMA8GA1UdDwEB/wQFAwMBqoAwEgYD"
-        + "VR0TAQH/BAgwBgEB/wIBBTAUBgNVHSABAf8ECjAIMAYGBFUdIAAwZwYDVR0RAQH/B"
-        + "F0wW4EMcmZjQDgyMi5OYW1lggdkTlNOYW1lpBcxFTATBgNVBAoTDE9yZ2FuaXphdG"
-        + "lvboYaaHR0cDovL3VuaWZvcm0uUmVzb3VyY2UuSWSHBP///wCIByoDolyDsgMwDAY"
-        + "DVR0eAQH/BAIwADAMBgNVHSQBAf8EAjAAMIGZBgNVHSUBAf8EgY4wgYsGBFUdJQAG"
-        + "CCsGAQUFBwMBBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDB"
-        + "AYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBwYIKwYBBQUHAwgGCCsGAQUFBw"
-        + "MJBggrBgEFBQgCAgYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GA1UdNgEB/wQDAgE"
-        + "BMA4GBCpNhgkBAf8EAwEBATBkBgNVHRIEXTBbgQxyZmNAODIyLk5hbWWCB2ROU05h"
-        + "bWWkFzEVMBMGA1UEChMMT3JnYW5pemF0aW9uhhpodHRwOi8vdW5pZm9ybS5SZXNvd"
-        + "XJjZS5JZIcE////AIgHKgOiXIOyAzAJBgNVHR8EAjAAMAoGA1UdIwQDAQEBMAoGA1"
-        + "UdDgQDAQEBMAoGA1UdIQQDAQEBMAwGByqGSM44BAMBAQADMAAwLQIUAL4QvoazNWP"
-        + "7jrj84/GZlhm09DsCFQCBKGKCGbrP64VtUt4JPmLjW1VxQA==\n"
-        + "-----END CERTIFICATE-----";
+    private static String CERT_CORRECT = "-----BEGIN CERTIFICATE-----\n"
+        + "MIIDWzCCAxmgAwIBAgICAiswCgYIKoZIzj0EAwIwHTEbMBkGA1UEChMSQ2VydGlm\n"
+        + "aWNhdGUgSXNzdWVyMCIYDzE5NzAwMTEyMTM0NjQwWhgPMTk3MDAxMjQwMzMzMjBa\n"
+        + "MB8xHTAbBgNVBAoTFFN1YmplY3QgT3JnYW5pemF0aW9uMFkwEwYHKoZIzj0CAQYI\n"
+        + "KoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aX\n"
+        + "qAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwYECAKqCAgBVo4ICOTCCAjUwDwYD\n"
+        + "VR0PAQH/BAUDAwGqgDASBgNVHRMBAf8ECDAGAQH/AgEFMBQGA1UdIAEB/wQKMAgw\n"
+        + "BgYEVR0gADBpBgNVHREBAf8EXzBdgQxyZmNAODIyLk5hbWWCB2ROU05hbWWkGTAX\n"
+        + "MRUwEwYDVQQKEwxPcmdhbml6YXRpb26GGmh0dHA6Ly91bmlmb3JtLlJlc291cmNl\n"
+        + "LklkhwT///8AiAcqA6Jcg7IDMAwGA1UdHgEB/wQCMAAwDAYDVR0kAQH/BAIwADCB\n"
+        + "mQYDVR0lAQH/BIGOMIGLBgRVHSUABggrBgEFBQcDAQYIKwYBBQUHAwEGCCsGAQUF\n"
+        + "BwMCBggrBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUFBwMFBggrBgEFBQcDBgYIKwYB\n"
+        + "BQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCQYIKwYBBQUIAgIGCisGAQQBgjcKAwMG\n"
+        + "CWCGSAGG+EIEATANBgNVHTYBAf8EAwIBATAOBgQqTYYJAQH/BAMBAQEwZgYDVR0S\n"
+        + "BF8wXYEMcmZjQDgyMi5OYW1lggdkTlNOYW1lpBkwFzEVMBMGA1UEChMMT3JnYW5p\n"
+        + "emF0aW9uhhpodHRwOi8vdW5pZm9ybS5SZXNvdXJjZS5JZIcE////AIgHKgOiXIOy\n"
+        + "AzAJBgNVHR8EAjAAMAwGA1UdIwQFMAOAAQEwCgYDVR0OBAMEAQEwKQYDVR0hBCIw\n"
+        + "IDAeBg0qhkiG9xIEAYS3CQIBBg0qhkiG9xIEAYS3CQICMAoGCCqGSM49BAMCAzAA\n"
+        + "MC0CFAC+EL6GszVj+464/OPxmZYZtPQ7AhUAgShighm6z+uFbVLeCT5i41tVcUA=\n"
+        + "-----END CERTIFICATE-----\n";
 
     private static String CERT_TAMPERED = "-----BEGIN CERTIFICATE-----\n"
-        + "MIIC+jCCAragAwIBAgICAiswDAYHKoZIzjgEAwEBADAdMRswGQYDVQQKExJDZXJ0a"
-        + "WZpY2F0ZSBJc3N1ZXIwIhgPMTk3MDAxMTIxMzQ2NDBaGA8xOTcwMDEyNDAzMzMyMF"
-        + "owHzEdMBsGA1UEChMUU3ViamVjdCBPcmdhbml6YXRpb24wGTAMBgcqhkjOOAQDAQE"
-        + "AAwkAAQIDBAUGBwiBAgCqggIAVaOCAhQwggIQMA8GA1UdDwEB/wQFAwMBqoAwEgYD"
-        + "VR0TAQH/BAgwBgEB/wIBBTAUBgNVHSABAf8ECjAIMAyGBFUdIAAwZwYDVR0RAQH/B"
-        + "F0wW4EMcmZjQDgyMi5OYW1lggdkTlNOYW1lpBcxFTATBgNVBAoTDE9yZ2FuaXphdG"
-        + "lvboYaaHR0cDovL3VuaWZvcm0uUmVzb3VyY2UuSWSHBP///wCIByoDolyDsgMwDAY"
-        + "DVR0eAQH/BAIwADAMBgNVHSQBAf8EAjAAMIGZBgNVHSUBAf8EgY4wgYsGBFUdJQAG"
-        + "CCsGAQUFBwMBBggrBgEFBQcDAQYIKxYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDB"
-        + "AYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBwYIKwYBBQUHAwgGCCsGAQUFBw"
-        + "MJBggrBgEFBQgCAgYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GA1UdNgEB/wQDAgE"
-        + "BMA4GBCpNhgkBAf8EAwEBATBkBgNVHRIEXTBbgQxyZmNAODIyLk5hbWWCB2ROU05h"
-        + "bWWkFzEVMBMGA1UEChMMT3JnYW5pemF0aW9uhhpodHRwOi8vdW5pZm9ybS5SZXNvd"
-        + "XJjZS5JZIcE////AIgHKgOiXIOyAzAJBgNVHR8EAjAAMAoGA1UdIwQDAQEBMAoGA1"
-        + "UdDgQDAQEBMAoGA1UdIQQDAQEBMAwHByqGSM44BAMBAQADMAAwLQIUAL4QvoazNWP"
-        + "7jrj84/GZlhm09DsCFQCBKGKCGbrP64VtUt4JPmLjW1VxQA==\n"
-        + "-----END CERTIFICATE-----";
+        + "MIIDWjCCAxmgAwIBAgICAiswCgYIKoZIzj0EAwIwHTEbMBkGA1UEChMSQ2VydGlm\n"
+        + "aWNhdGUgSXNzdWVyMCIYDzE5NzAwMTEyMTM0NjQwWhgPMTk3MDAxMjQwMzMzMjBa\n"
+        + "MB8xHTAbBgNVBAoTFFN1YmplY3QgT3JnYW5pemF0aW9uMFkwEwYHKoZIzj0CAQYI\n"
+        + "KoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aX\n"
+        + "qAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwYECAKqCAgBVo4ICOTCCAjUwDwYD\n"
+        + "VR0PAQH/BAUDAwGqgDASBgNVHRMBAf8ECDAGAQH/AgEFMBQGA1UdIAEB/wQKMAgw\n"
+        + "DIYEVR0gADBpBgNVHREBAf8EXzBdgQxyZmNAODIyLk5hbWWCB2ROU05hbWWkGTAX\n"
+        + "MRUwEwYDVQQKEwxPcmdhbml6YXRpb26GGmh0dHA6Ly91bmlmb3JtLlJlc291cmNl\n"
+        + "LklkhwT///8AiAcqA6Jcg7IDMAwGA1UdHgEB/wQCMAAwDAYDVR0kAQH/BAIwADCB\n"
+        + "mQYDVR0lAQH/BIGOMIGLBgRVHSUABggrBgEFBQcDAQYIKwYBBQUHAwEGCCsWAQUF\n"
+        + "BwMCBggrBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUFBwMFBggrBgEFBQcDBgYIKwYB\n"
+        + "BQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCQYIKwYBBQUIAgIGCisGAQQBgjcKAwMG\n"
+        + "CWCGSAGG+EIEATANBgNVHTYBAf8EAwIBATAOBgQqTYYJAQH/BAMBAQEwZgYDVR0S\n"
+        + "BF8wXYEMcmZjQDgyMi5OYW1lggdkTlNOYW1lpBkwFzEVMBMGA1UEChMMT3JnYW5p\n"
+        + "emF0aW9uhhpodHRwOi8vdW5pZm9ybS5SZXNvdXJjZS5JZIcE////AIgHKgOiXIOy\n"
+        + "AzAJBgNVHR8EAjAAMAwGA1UdIwQFMAOAAQEwCgYDVR0OBAMEAQEwKQYDVR0hBCIw\n"
+        + "IDAeBg0qhkiG9xIEAYS3CQIBBg0qhkiG9xIEAYS3CQICMAkHByqGSM44BAMDMAAw\n"
+        + "LQIUAL4QvoazNWP7jrj84/GZlhm09DsCFQCBKGKCGbrP64VtUt4JPmLjW1VxQA==\n"
+        + "-----END CERTIFICATE-----\n";
 
     // Base64 encoded form of ASN.1 DER encoded X.509 CRL
     // (see RFC 3280 at http://www.ietf.org/rfc/rfc3280.txt)
diff --git a/luni/src/test/java9language/Android.bp b/luni/src/test/java9language/Android.bp
index dddc184..5c10041 100644
--- a/luni/src/test/java9language/Android.bp
+++ b/luni/src/test/java9language/Android.bp
@@ -15,6 +15,14 @@
 // Android tests related to Java 9 language features.
 
 // Use jarjar to repackage Java9LanguageFeatures, to be used in tests below.
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_luni_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_luni_license"],
+}
+
 java_library {
     name: "core-java-9-language-features-repackaged-for-test",
     hostdex: true,
diff --git a/luni/src/test/parameter_metadata/Android.bp b/luni/src/test/parameter_metadata/Android.bp
index 0eec841..d5ec1c7 100644
--- a/luni/src/test/parameter_metadata/Android.bp
+++ b/luni/src/test/parameter_metadata/Android.bp
@@ -19,6 +19,14 @@
 //
 // Included as a resource by //libcore:core-tests and loaded into its own
 // class loader by libcore.java.lang.reflect.ParameterTest.
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_luni_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_luni_license"],
+}
+
 java_library {
     name: "parameter-metadata-test",
     visibility: [
diff --git a/luni/src/test/resources/libcore/java/util/zip/cp1251.zip b/luni/src/test/resources/libcore/java/util/zip/cp1251.zip
new file mode 100644
index 0000000..30426f9
--- /dev/null
+++ b/luni/src/test/resources/libcore/java/util/zip/cp1251.zip
Binary files differ
diff --git a/luni/src/test/resources/math_java_only.csv b/luni/src/test/resources/math_java_only.csv
index 7497244..336e737 100644
--- a/luni/src/test/resources/math_java_only.csv
+++ b/luni/src/test/resources/math_java_only.csv
@@ -240,15 +240,15 @@
 toDegrees,Infinity,0x1.fbc8d827034c3p1022
 toDegrees,Infinity,0x1.18a1877e00a5fp1023
 toDegrees,-Infinity,-0x1.424779e743077p1022
-toRadians,-0x1.bd6ca335c8f8bp1017,-0x1.8ec3beb2d4185p1023
-toRadians,-0x1.d5d636d37463cp1017,-0x1.a49ea827e7d05p1023
+toRadians,-0x1.bd6ca335c8f8cp1017,-0x1.8ec3beb2d4185p1023
+toRadians,-0x1.d5d636d37463bp1017,-0x1.a49ea827e7d05p1023
 toRadians,-0x1.6e07b010242a3p1015,-0x1.47afe754a89fep1021
 toRadians,-0x1.11c7c808a0662p1017,-0x1.ea33b08c7fbfbp1022
 toRadians,0x1.6651facd75f48p1017,0x1.40c8f2c416881p1023
 toRadians,-0x1.048574b80ff57p1017,-0x1.d2762c5e7c22bp1022
 toRadians,0x1.2b69e24b71de8p1015,0x1.0c0c8b67b7daep1021
-toRadians,-0x1.acfe9cbf85fcep1017,-0x1.800e50b80ea85p1023
-toRadians,0x1.0173811fff46ap1015,0x1.ccf6eeb86909cp1020
+toRadians,-0x1.acfe9cbf85fcfp1017,-0x1.800e50b80ea85p1023
+toRadians,0x1.0173811fff46bp1015,0x1.ccf6eeb86909cp1020
 toRadians,-0x1.982fa40f44f9ap1016,-0x1.6d6d5ccb7231fp1022
 toRadians,0x1.61a934e496335p1017,0x1.3c9d1f4fb1b51p1023
 toRadians,-0x1.6cf8ccc8ea027p1016,-0x1.46bd646b348a7p1022
@@ -256,15 +256,15 @@
 toRadians,0x1.1456c8928aff4p1018,0x1.eec87765e84c1p1023
 toRadians,0x1.235fec19c543cp1015,0x1.04da2953b2f46p1021
 toRadians,0x1.882719ca8a188p1016,0x1.5f12c97e6cc4fp1022
-toRadians,-0x1.c2f640afddd61p1016,-0x1.93b8dc284f903p1022
+toRadians,-0x1.c2f640afddd62p1016,-0x1.93b8dc284f903p1022
 toRadians,-0x1.40992763eae48p1017,-0x1.1f03b54fdb6b3p1023
 toRadians,-0x1.8205d95283762p1017,-0x1.5995eb7752bb1p1023
 toRadians,-0x1.00db6068f8009p1015,-0x1.cbe68c7a0015cp1020
 toRadians,0x1.ff045cc909d01p1016,0x1.c97c7a91bfb13p1022
-toRadians,-0x1.dd58c2d344925p1017,-0x1.ab57cf7150fc7p1023
-toRadians,-0x1.e8c0b050f0382p1016,-0x1.b58ddd93bfea3p1022
+toRadians,-0x1.dd58c2d344926p1017,-0x1.ab57cf7150fc7p1023
+toRadians,-0x1.e8c0b050f0383p1016,-0x1.b58ddd93bfea3p1022
 toRadians,-0x1.125c16633ee83p1018,-0x1.eb3d3b0b459ffp1023
-toRadians,-0x1.4390148899c34p1017,-0x1.21ab226ac9ce9p1023
+toRadians,-0x1.4390148899c35p1017,-0x1.21ab226ac9ce9p1023
 toRadians,-0x1.1663e3e36d831p1017,-0x1.f274aa94c0813p1022
 toRadians,0x1.0c15cb5b07c69p1018,0x1.e0012c863f62bp1023
 toRadians,0x1.1b99b34e2a2c9p1017,0x1.fbc8d827034c3p1022
@@ -3530,26 +3530,26 @@
 signum,0x1.0p0,0x1.c31faf27fa01ep3
 signum,-0x1.0p0,-0x1.06058171a93d9p0
 signum,-0x1.0p0,-0x1.852592119514fp0
-toDegrees,0x1.9cc4da55f56cbp4,0x1.cd113dd73a73cp-2
-toDegrees,-0x1.94c416d548e15p2,-0x1.c420c0252959cp-4
+toDegrees,0x1.9cc4da55f56ccp4,0x1.cd113dd73a73cp-2
+toDegrees,-0x1.94c416d548e14p2,-0x1.c420c0252959cp-4
 toDegrees,-0x1.7b02a36e7559fp5,-0x1.a75bcb9bdae6fp-1
-toDegrees,0x1.0f6e61abce825p7,0x1.2f31087c78663p1
-toDegrees,-0x1.be896265c1388p6,-0x1.f2c946a2c6a66p0
-toDegrees,-0x1.560657ad9eebdp4,-0x1.7e0b9b7822823p-2
+toDegrees,0x1.0f6e61abce826p7,0x1.2f31087c78663p1
+toDegrees,-0x1.be896265c1387p6,-0x1.f2c946a2c6a66p0
+toDegrees,-0x1.560657ad9eebcp4,-0x1.7e0b9b7822823p-2
 toDegrees,0x1.c97fc36ec2798p0,0x1.ff080809e5354p-6
 toDegrees,-0x1.231218346bfap6,-0x1.45210ae12d5dep0
-toDegrees,0x1.acd981a0f8f16p5,0x1.df07967013bfbp-1
+toDegrees,0x1.acd981a0f8f17p5,0x1.df07967013bfbp-1
 toDegrees,0x1.ffc04d8008847p5,0x1.1dd0d6e0a9014p0
 toDegrees,-0x1.22b4bf371bcefp6,-0x1.44b8c5afa5478p0
-toDegrees,-0x1.38f0f9d667f87p6,-0x1.5d8f0d930782dp0
-toDegrees,-0x1.516ecfff380abp7,-0x1.78ea87163eeabp1
+toDegrees,-0x1.38f0f9d667f88p6,-0x1.5d8f0d930782dp0
+toDegrees,-0x1.516ecfff380acp7,-0x1.78ea87163eeabp1
 toDegrees,-0x1.2ae526db45ae4p7,-0x1.4dde7a996b9c8p1
-toDegrees,-0x1.a6b4650d4c965p5,-0x1.d82a67b42deaap-1
+toDegrees,-0x1.a6b4650d4c964p5,-0x1.d82a67b42deaap-1
 toDegrees,0x1.7773b321056e7p6,0x1.a362445ee7c18p0
 toDegrees,-0x1.190c89382e7e9p4,-0x1.39ef493e52db9p-2
 toDegrees,0x1.42d69a1fb31b4p6,0x1.689d23fbea30ap0
 toDegrees,0x1.c8f48084d3c8fp6,0x1.fe6c7995f196fp0
-toDegrees,0x1.c5c438960265fp6,0x1.fadcae2a0e937p0
+toDegrees,0x1.c5c438960265ep6,0x1.fadcae2a0e937p0
 toDegrees,-0x1.a296ca69d0b03p4,-0x1.d391849fee2e3p-2
 toDegrees,-0x1.90854d79fef1ep-1,-0x1.bf62cc5fffe35p-7
 toDegrees,-0x1.8e1570b872b7ap9,-0x1.bca9effb7253dp3
@@ -3560,8 +3560,8 @@
 toDegrees,0x1.c4324f9a9f8bdp5,0x1.f91bbe117d1cp-1
 toDegrees,0x1.18467de646d62p7,0x1.3912118f3c746p1
 toDegrees,0x1.0eed528a37375p9,0x1.2ea0df6c23f34p3
-toDegrees,-0x1.450d273c40222p6,-0x1.6b15fbfe1b148p0
-toDegrees,0x1.dd9b163174e74p3,0x1.0abed2c6e1761p-2
+toDegrees,-0x1.450d273c40223p6,-0x1.6b15fbfe1b148p0
+toDegrees,0x1.dd9b163174e75p3,0x1.0abed2c6e1761p-2
 toDegrees,0x1.60880fe3738a2p3,0x1.89c80d8ae0428p-3
 toDegrees,0x1.12bfe174fb77ap2,0x1.32e5eecd756dbp-4
 toDegrees,0x1.90c57dddec21ap1,0x1.bfaa7f87fb8a7p-5
@@ -3569,10 +3569,10 @@
 toDegrees,0x1.75031f7ed32cdp6,0x1.a0a89bb3a76ap0
 toDegrees,0x1.fcb5dee07145fp4,0x1.1c1e14b2b9db4p-1
 toDegrees,0x1.69114d2c22ba8p4,0x1.9350fd1d452d5p-2
-toDegrees,-0x1.526b1f129d2c1p5,-0x1.7a045c02d25bfp-1
-toDegrees,0x1.e2dfecd8124b2p3,0x1.0db028288dd15p-2
+toDegrees,-0x1.526b1f129d2c2p5,-0x1.7a045c02d25bfp-1
+toDegrees,0x1.e2dfecd8124b1p3,0x1.0db028288dd15p-2
 toDegrees,-0x1.0dd14c6709a48p7,-0x1.2d639d6b5ce98p1
-toDegrees,0x1.2a01518efb356p6,0x1.4cdffc9db8986p0
+toDegrees,0x1.2a01518efb357p6,0x1.4cdffc9db8986p0
 toDegrees,-0x1.9bc784f0c6b48p5,-0x1.cbf643e726dd8p-1
 toDegrees,-0x1.4f0025a5e36cep6,-0x1.763300f441a5p0
 toDegrees,0x1.689f3d35e9e25p3,0x1.92d19471a60b9p-3
@@ -3581,42 +3581,42 @@
 toDegrees,-0x1.713bef68c5494p3,-0x1.9c703fc03e9d5p-3
 toDegrees,0x1.6618e48537401p3,0x1.8fff9a9c68351p-3
 toDegrees,-0x1.358164697bc5cp4,-0x1.59b88c71a52ddp-2
-toDegrees,0x1.7717dd5882015p6,0x1.a2fbafb0d679cp0
+toDegrees,0x1.7717dd5882014p6,0x1.a2fbafb0d679cp0
 toDegrees,-0x1.1b5f7b4512444p5,-0x1.3c87d8bc0f4f4p-1
 toDegrees,0x1.044ba83c26953p6,0x1.22c0be546d685p0
 toDegrees,-0x1.6d40cb9cad76ep9,-0x1.97fddbe158cc3p3
-toDegrees,0x1.3708b29f0dc25p5,0x1.5b6da41c12f38p-1
+toDegrees,0x1.3708b29f0dc24p5,0x1.5b6da41c12f38p-1
 toDegrees,-0x1.975ed02c432d9p6,-0x1.c7097d08166fp0
 toDegrees,0x1.4c557ad19a278p6,0x1.733875000be6cp0
 toDegrees,0x1.4734be48e506dp5,0x1.6d7e1dc7db526p-1
-toDegrees,0x1.485976815821dp6,0x1.6ec51657a80b1p0
+toDegrees,0x1.485976815821ep6,0x1.6ec51657a80b1p0
 toDegrees,0x1.f2596d0c82a2fp5,0x1.1654ad221e6ep0
-toDegrees,0x1.8eff75442afa9p5,0x1.bdaf56785b7cbp-1
+toDegrees,0x1.8eff75442afa8p5,0x1.bdaf56785b7cbp-1
 toDegrees,0x1.e8e4b12a2aba2p8,0x1.110caef53c05dp3
 toDegrees,-0x1.c4fa982ea8112p6,-0x1.f9fb761682d11p0
 toDegrees,0x1.1a5f7514640c3p5,0x1.3b69dd67cf39fp-1
 toDegrees,-0x1.794600ac5da92p7,-0x1.a56b21e64998p1
-toDegrees,0x1.e35d13fe74edfp5,0x1.0df60e3206348p0
+toDegrees,0x1.e35d13fe74eep5,0x1.0df60e3206348p0
 toDegrees,-0x1.c4e1b476eda0ep4,-0x1.f9dfa8cf9b3d5p-2
 toDegrees,0x1.69682c8023cc2p5,0x1.93b206aeab843p-1
 toDegrees,-0x1.6b3653d3330f4p4,-0x1.95b641b33e0c1p-2
-toDegrees,0x1.b4a966dff8b42p5,0x1.e7c17e0aedc62p-1
-toDegrees,0x1.90ec5ba49fbb5p4,0x1.bfd5e989b0585p-2
+toDegrees,0x1.b4a966dff8b41p5,0x1.e7c17e0aedc62p-1
+toDegrees,0x1.90ec5ba49fbb4p4,0x1.bfd5e989b0585p-2
 toDegrees,-0x1.e889680f077cap5,-0x1.10d9b32fdd367p0
 toDegrees,-0x1.3730338445bfcp7,-0x1.5b99c4527ddd1p1
 toDegrees,-0x1.022c00433be75p5,-0x1.20617946cc6bep-1
 toDegrees,0x1.12162302b21bp7,0x1.322853b853541p1
 toDegrees,0x1.b9be356b5ae5p6,0x1.ed6e81ec514c4p0
-toDegrees,0x1.9e768d7447387p3,0x1.cef5b0528789cp-3
-toDegrees,0x1.2adffc78f9eb4p6,0x1.4dd8b57b73478p0
+toDegrees,0x1.9e768d7447386p3,0x1.cef5b0528789cp-3
+toDegrees,0x1.2adffc78f9eb3p6,0x1.4dd8b57b73478p0
 toDegrees,-0x1.3f4e073a6bbd3p7,-0x1.64aab8cf9710fp1
 toDegrees,0x1.c0aa08f3f6bbap4,0x1.f529a80fabcc6p-2
 toDegrees,-0x1.e192542d3cd5ap2,-0x1.0cf5d76d8bc39p-3
 toDegrees,0x1.04ab229374e59p6,0x1.232b64afd152ep0
 toDegrees,-0x1.f9e0a77142ffdp-1,-0x1.1a890b24927f6p-6
 toDegrees,0x1.e97f3f3966932p5,0x1.116300d074b9bp0
-toDegrees,-0x1.1cec99f1b0757p6,-0x1.3e436f08204c9p0
-toDegrees,-0x1.7d252f6ad8e33p6,-0x1.a9be4b43df93ap0
+toDegrees,-0x1.1cec99f1b0756p6,-0x1.3e436f08204c9p0
+toDegrees,-0x1.7d252f6ad8e32p6,-0x1.a9be4b43df93ap0
 toDegrees,-0x1.8a42007322f05p3,-0x1.b863e4e12bc6fp-3
 toDegrees,-0x1.278d84dfc3f4cp4,-0x1.4a22ba5b4331p-2
 toDegrees,-0x1.ed3a5058db8abp3,-0x1.137869c4a0318p-2
@@ -3625,68 +3625,68 @@
 toDegrees,-0x1.4b23ed2f49fp5,-0x1.71e32698a8e3fp-1
 toDegrees,-0x1.df05855cfd054p8,-0x1.0b893eacbacc4p3
 toDegrees,-0x1.40573e27c281fp6,-0x1.65d2f82c221f3p0
-toDegrees,-0x1.68c5edf3d9b3ep5,-0x1.92fccc2599ddp-1
+toDegrees,-0x1.68c5edf3d9b3fp5,-0x1.92fccc2599ddp-1
 toDegrees,0x1.16f591c9d9791p7,0x1.3799b906d8ee9p1
 toDegrees,0x1.2c9a73b1f03b8p3,0x1.4fc6f2a6f562cp-3
 toDegrees,-0x1.98e03048c019dp4,-0x1.c8b7f4f73266cp-2
-toDegrees,-0x1.be6f129011c73p6,-0x1.f2abe2a2e1c9dp0
+toDegrees,-0x1.be6f129011c74p6,-0x1.f2abe2a2e1c9dp0
 toDegrees,0x1.e5d4c67fad724p8,0x1.0f56dc9cfb8ddp3
 toDegrees,0x1.e8a586d34db52p7,0x1.10e967bc9c3f8p2
 toDegrees,0x1.0acde77827a8ep7,0x1.2a05f5915fde8p1
-toDegrees,-0x1.66c7f595b3323p4,-0x1.90c327c09c62ap-2
-toDegrees,0x1.8dd01c0733ae9p4,0x1.bc5c7e80a714cp-2
+toDegrees,-0x1.66c7f595b3322p4,-0x1.90c327c09c62ap-2
+toDegrees,0x1.8dd01c0733aeap4,0x1.bc5c7e80a714cp-2
 toDegrees,0x1.1b088597a996dp6,0x1.3c26b633ca24fp0
 toDegrees,-0x1.cddf63ec90861p3,-0x1.01f55555933f7p-2
 toDegrees,-0x1.02b7b8b3d1fe1p6,-0x1.20fd8b01dd796p0
-toDegrees,-0x1.559d98a788a77p7,-0x1.7d969accdb9d9p1
+toDegrees,-0x1.559d98a788a76p7,-0x1.7d969accdb9d9p1
 toDegrees,-0x1.3f5f0d0bfb216p6,-0x1.64bdbc8a7e518p0
 toDegrees,0x1.16226547ded9dp4,0x1.36add6dc29f2ap-2
-toDegrees,0x1.d4d4543876317p3,0x1.05d7ff438dc75p-2
-toDegrees,0x1.1d28bbaefee9dp5,0x1.3e869a00f7c61p-1
-toDegrees,-0x1.86579970698fap5,-0x1.b404332954a1ep-1
-toDegrees,0x1.2ef51ad45c23bp9,0x1.52681e197503fp3
+toDegrees,0x1.d4d4543876316p3,0x1.05d7ff438dc75p-2
+toDegrees,0x1.1d28bbaefee9ep5,0x1.3e869a00f7c61p-1
+toDegrees,-0x1.86579970698fbp5,-0x1.b404332954a1ep-1
+toDegrees,0x1.2ef51ad45c23ap9,0x1.52681e197503fp3
 toDegrees,-0x1.3cf9701fe3407p6,-0x1.621052ffaaa7ap0
 toDegrees,0x1.e7244245038d5p3,0x1.10123b28dd00ap-2
-toDegrees,0x1.a5a03aa78362p5,0x1.d6f5ecd5f2423p-1
-toDegrees,0x1.e8f39b8a61b6ap3,0x1.1115038a9416bp-2
+toDegrees,0x1.a5a03aa78361fp5,0x1.d6f5ecd5f2423p-1
+toDegrees,0x1.e8f39b8a61b6bp3,0x1.1115038a9416bp-2
 toDegrees,0x1.3bf51b0182135p5,0x1.60ed87afc67a6p-1
-toDegrees,0x1.5b13311fb6c09p7,0x1.83afbbe37580cp1
+toDegrees,0x1.5b13311fb6c0ap7,0x1.83afbbe37580cp1
 toDegrees,0x1.395e71b8dc57ap9,0x1.5e09548dca6fap3
-toDegrees,0x1.b5fd9e97cb653p7,0x1.e93d84e4beffdp1
+toDegrees,0x1.b5fd9e97cb652p7,0x1.e93d84e4beffdp1
 toDegrees,0x1.0b5531c717ccdp10,0x1.2a9d14762cdbep4
-toDegrees,-0x1.0f7be127bd95dp6,-0x1.2f401c4c8c6dap0
+toDegrees,-0x1.0f7be127bd95cp6,-0x1.2f401c4c8c6dap0
 toDegrees,-0x1.f8a402b4d979cp5,-0x1.19d832498ae13p0
-toDegrees,-0x1.8d2c07dfa6e11p5,-0x1.bba537696177bp-1
+toDegrees,-0x1.8d2c07dfa6e12p5,-0x1.bba537696177bp-1
 toDegrees,-0x1.c154bc5577641p5,-0x1.f5e854bcf804bp-1
 toDegrees,-0x1.39d507bcace13p3,-0x1.5e8dcac830903p-3
 toDegrees,0x1.11e06dc88ae51p4,0x1.31ec55af95699p-2
-toDegrees,0x1.1ebf16f38b1d9p7,0x1.404c8193c1f93p1
+toDegrees,0x1.1ebf16f38b1d8p7,0x1.404c8193c1f93p1
 toDegrees,-0x1.708e7799a7b4ep4,-0x1.9bae7bc08c7c8p-2
-toDegrees,-0x1.5c8a2d06523abp9,-0x1.85529856c320fp3
-toDegrees,-0x1.5e2b963a36dp7,-0x1.8724d8fae038fp1
+toDegrees,-0x1.5c8a2d06523aap9,-0x1.85529856c320fp3
+toDegrees,-0x1.5e2b963a36cffp7,-0x1.8724d8fae038fp1
 toDegrees,0x1.3e13287c19be7p7,0x1.634b0236da71bp1
 toDegrees,0x1.a3ff712c87297p6,0x1.d5245e9b2e50cp0
 toDegrees,0x1.80d8a3f61cf0dp5,0x1.ade09c96d6ep-1
 toDegrees,0x1.cbaa19fa7d4edp5,0x1.00b99dd1e4d8ep0
 toDegrees,0x1.dc522f7f9e4e9p4,0x1.0a07215ac31f7p-1
 toDegrees,-0x1.2c3cf84f90a7ep3,-0x1.4f5e870a148f8p-3
-toDegrees,-0x1.1dc120f5ca3afp7,-0x1.3f30d4407fa7fp1
+toDegrees,-0x1.1dc120f5ca3aep7,-0x1.3f30d4407fa7fp1
 toDegrees,0x1.6e2c3ba13a4d3p5,0x1.9904d85ecc7c9p-1
 toDegrees,-0x1.ea38a3aa95e47p7,-0x1.11ca8bb9c2c15p2
 toDegrees,-0x1.322c04414ec4cp8,-0x1.55ff51a36f15dp2
 toDegrees,-0x1.4fe90d155a88dp6,-0x1.773728f881204p0
-toDegrees,0x1.ff58a2d82d4b1p8,0x1.1d96f0e567a04p3
+toDegrees,0x1.ff58a2d82d4bp8,0x1.1d96f0e567a04p3
 toDegrees,-0x1.a25d798c48c75p5,-0x1.d3517ee076ecap-1
 toDegrees,0x1.6c39408ca491fp4,0x1.96d77a72adc61p-2
-toDegrees,0x1.58be156cc8725p7,0x1.8114c1f74781cp1
-toDegrees,-0x1.45cb34c0c3921p3,-0x1.6bea467e50e53p-3
-toDegrees,0x1.cf7a84939fa02p6,0x1.02daf3455c5ebp1
+toDegrees,0x1.58be156cc8724p7,0x1.8114c1f74781cp1
+toDegrees,-0x1.45cb34c0c392p3,-0x1.6bea467e50e53p-3
+toDegrees,0x1.cf7a84939fa03p6,0x1.02daf3455c5ebp1
 toDegrees,0x1.c8d9ec779379ap5,0x1.fe4ec9630f342p-1
 toDegrees,0x1.40f290dd23f99p5,0x1.6680778b4b4dbp-1
 toDegrees,-0x1.d007e807137ccp5,-0x1.0329ea9fd53c5p0
 toDegrees,-0x1.0b45c757e1c32p5,-0x1.2a8bdc408764ap-1
 toDegrees,-0x1.260688556fd89p3,-0x1.486dfdea7261dp-3
-toDegrees,-0x1.e5e3535741671p8,-0x1.0f5efcf51e5d2p3
+toDegrees,-0x1.e5e3535741672p8,-0x1.0f5efcf51e5d2p3
 toDegrees,-0x1.0086c685a5706p6,-0x1.1e8af5d50c276p0
 toDegrees,0x1.57a34296c3044p7,0x1.7fd8d738cb1ddp1
 toDegrees,0x1.2a33a57def28bp7,0x1.4d18341b9bfep1
@@ -3694,13 +3694,13 @@
 toDegrees,0x1.7128c6132b42ap3,0x1.9c5ad8709aa79p-3
 toDegrees,-0x1.688e096df3805p7,-0x1.92be5d5f8c398p1
 toDegrees,0x1.000dfdac23b5cp3,0x1.1e040ae962b82p-3
-toDegrees,-0x1.1af109f43a815p5,-0x1.3c0c7b234319cp-1
+toDegrees,-0x1.1af109f43a816p5,-0x1.3c0c7b234319cp-1
 toDegrees,0x1.fc9f8ecba486dp6,0x1.1c119e785bc66p1
 toDegrees,-0x1.aab953e1cd1d7p5,-0x1.dca7bbf503ce4p-1
 toDegrees,-0x1.5e8928b59890ep7,-0x1.878d5e64a5e27p1
 toDegrees,0x1.836a93ac41202p3,0x1.b0bf889de387cp-3
 toDegrees,0x1.3b7cdcc880872p6,0x1.6067379d26339p0
-toDegrees,0x1.c8056c371e64dp5,0x1.fd616bb8f3785p-1
+toDegrees,0x1.c8056c371e64cp5,0x1.fd616bb8f3785p-1
 toDegrees,-0x1.ec401ac74c4bcp4,-0x1.12ecab81b3564p-1
 toDegrees,-0x1.ca28aa208a7a4p5,-0x1.ffc4b21fba5b4p-1
 toDegrees,-0x1.1a006a6cef937p6,-0x1.3affb3cdd2bf1p0
@@ -3716,17 +3716,17 @@
 toDegrees,-0x1.2eb22726e4acdp3,-0x1.521d54e55310dp-3
 toDegrees,-0x1.3ef9ae1414208p7,-0x1.644c8107f5d37p1
 toDegrees,-0x1.2a6babb40908dp8,-0x1.4d56c882fd11ap2
-toDegrees,-0x1.6eb2ba40302ebp4,-0x1.999b13be37afp-2
+toDegrees,-0x1.6eb2ba40302eap4,-0x1.999b13be37afp-2
 toDegrees,-0x1.1f825cdc83933p8,-0x1.4126a0d7b7ec8p2
 toDegrees,0x1.196d47a93599dp6,0x1.3a5b599fc865dp0
-toDegrees,-0x1.152cb9e9e5ap2,-0x1.359b6c87594cep-4
+toDegrees,-0x1.152cb9e9e59ffp2,-0x1.359b6c87594cep-4
 toDegrees,0x1.e8033ad066cefp5,0x1.108ec2f31fd2p0
 toDegrees,-0x1.380d292404d8ap1,-0x1.5c9094bb0fa5ap-5
 toDegrees,-0x1.e3b0170b51c6cp8,-0x1.0e246b0558ec9p3
-toDegrees,0x1.5292d8b4c17e7p7,0x1.7a30bb99bbf7ap1
+toDegrees,0x1.5292d8b4c17e6p7,0x1.7a30bb99bbf7ap1
 toDegrees,0x1.a643cd81be412p8,0x1.d7aca37eb05a1p2
-toDegrees,-0x1.6202c5199be34p2,-0x1.8b6f12d9e8ceap-4
-toDegrees,-0x1.5c58d80880982p5,-0x1.851b7d9eba78dp-1
+toDegrees,-0x1.6202c5199be33p2,-0x1.8b6f12d9e8ceap-4
+toDegrees,-0x1.5c58d80880983p5,-0x1.851b7d9eba78dp-1
 toDegrees,-0x1.43b8e1a23e479p7,-0x1.6999e5a23a02fp1
 toDegrees,-0x1.8815f8f42cea6p5,-0x1.b5f6cdab230d6p-1
 toDegrees,-0x1.4bf63f89b2173p7,-0x1.72ce151501a48p1
@@ -3735,18 +3735,18 @@
 toRadians,-0x1.269dd1f61b4dfp-6,-0x1.07c11fe9ef09ap0
 toRadians,0x1.8f9413ec4afd4p-5,0x1.65b8a1590af12p1
 toRadians,-0x1.d5e34359a07fep-6,-0x1.a4aa56bfa319bp0
-toRadians,0x1.dccef5cda0302p-8,0x1.aadc71cf68e4p-2
+toRadians,0x1.dccef5cda0303p-8,0x1.aadc71cf68e4p-2
 toRadians,-0x1.756bb6c6074bp-6,-0x1.4e4dbbcc2fa9fp0
 toRadians,0x1.bd04cff56f47ep-7,0x1.8e66cbb71fd56p-1
 toRadians,0x1.78fefa23b96fap-6,0x1.51811ea8e898bp0
 toRadians,0x1.0d4d232276716p-6,0x1.e22ea197722cep-1
 toRadians,-0x1.33d9fd2988496p-4,-0x1.139a5efe42b6ap2
-toRadians,-0x1.229982e2b8d7cp-5,-0x1.042888e04d436p1
+toRadians,-0x1.229982e2b8d7dp-5,-0x1.042888e04d436p1
 toRadians,-0x1.29c7dd4973314p-6,-0x1.0a965060eb912p0
 toRadians,0x1.20d720e76b068p-5,0x1.029554c15c57ap1
 toRadians,0x1.7f5bf73fdde9cp-7,0x1.5733775f2d25p-1
 toRadians,0x1.42170369d60b3p-4,0x1.2059910e0b436p2
-toRadians,0x1.a8b0ae7ecdabdp-3,0x1.7c33d0cf9cf15p3
+toRadians,0x1.a8b0ae7ecdabep-3,0x1.7c33d0cf9cf15p3
 toRadians,0x1.630ea69bd5949p-5,0x1.3ddd1f7cf69b8p1
 toRadians,0x1.7f69b05ec6601p-3,0x1.573fc07b23f0dp3
 toRadians,0x1.af6c649a981abp-5,0x1.823af65afedeap1
@@ -3755,7 +3755,7 @@
 toRadians,0x1.5fc99f56b4abap-7,0x1.3aefc6b5b91b7p-1
 toRadians,-0x1.517b699ec7bep-4,-0x1.2e21320a4ff91p2
 toRadians,-0x1.0bb8960d4d455p-7,-0x1.df5a490650c4ap-2
-toRadians,-0x1.ec1e137eb7753p-7,-0x1.b891050802839p-1
+toRadians,-0x1.ec1e137eb7754p-7,-0x1.b891050802839p-1
 toRadians,0x1.29829f741d254p-6,0x1.0a5853632a0e3p0
 toRadians,-0x1.4f91fde8cb341p-6,-0x1.2c6b0b115881dp0
 toRadians,0x1.33d962b6df19p-6,0x1.1399d4b969204p0
@@ -3763,7 +3763,7 @@
 toRadians,0x1.8a562054567fdp-5,0x1.610740b5d01cbp1
 toRadians,-0x1.f38e2489f0cc6p-9,-0x1.bf39a0f24dfa5p-3
 toRadians,0x1.d8241c6f90ae7p-6,0x1.a6aec291de8e6p0
-toRadians,0x1.ea933e48ad7p-7,0x1.b72f8c019babcp-1
+toRadians,0x1.ea933e48ad701p-7,0x1.b72f8c019babcp-1
 toRadians,0x1.3a4dc8294ed8p-7,0x1.1961220e2d1bfp-1
 toRadians,-0x1.19ba22c2bb686p-7,-0x1.f86e2feb49188p-2
 toRadians,-0x1.85b38b279cecbp-9,-0x1.5ce0f7fbeaff5p-3
@@ -3784,87 +3784,87 @@
 toRadians,-0x1.3a41e6900b66ep-7,-0x1.19567f131b0b3p-1
 toRadians,0x1.ef7b1d4585839p-6,0x1.bb93dc72c9319p0
 toRadians,0x1.62d84b7aedaa3p-5,0x1.3dac76046aacep1
-toRadians,-0x1.0f20042155341p-6,-0x1.e572931c5417bp-1
+toRadians,-0x1.0f2004215534p-6,-0x1.e572931c5417bp-1
 toRadians,-0x1.690ea94f0a1b8p-6,-0x1.433c3b2cb5df1p0
-toRadians,-0x1.94c307b06c295p-8,-0x1.6a5c9464a575dp-2
-toRadians,-0x1.c7c50a49a546cp-9,-0x1.9806b7e00001p-3
-toRadians,0x1.73099f305d1fep-4,0x1.4c2b8cfb45c8bp2
+toRadians,-0x1.94c307b06c294p-8,-0x1.6a5c9464a575dp-2
+toRadians,-0x1.c7c50a49a546bp-9,-0x1.9806b7e00001p-3
+toRadians,0x1.73099f305d1fdp-4,0x1.4c2b8cfb45c8bp2
 toRadians,-0x1.389be50a6fd97p-8,-0x1.17dcb26fb38ap-2
 toRadians,0x1.5fbc85728b899p-8,0x1.3ae40c2677db1p-2
 toRadians,0x1.ef94c7f3a2ecfp-6,0x1.bbaad6d3bf4d2p0
 toRadians,-0x1.8f2d697a05737p-9,-0x1.655cb8140865dp-3
-toRadians,0x1.b20508bdffb78p-5,0x1.848dfae3dca6ep1
+toRadians,0x1.b20508bdffb79p-5,0x1.848dfae3dca6ep1
 toRadians,-0x1.bdb35ca54ab81p-7,-0x1.8f030f8701c68p-1
 toRadians,0x1.0861b9ad91254p-5,0x1.d95fa9ae542c1p0
-toRadians,0x1.a76bd0ab65c66p-7,0x1.7b10fae457027p-1
+toRadians,0x1.a76bd0ab65c67p-7,0x1.7b10fae457027p-1
 toRadians,0x1.54c5e8e9a7843p-7,0x1.3113702f0fc4fp-1
 toRadians,-0x1.13d8a20895e4fp0,-0x1.ede6983faab45p5
 toRadians,-0x1.cebc35c4a890bp-8,-0x1.9e43184cdea2ap-2
-toRadians,0x1.3d3e1280d3313p-4,0x1.1c029e507fabcp2
+toRadians,0x1.3d3e1280d3314p-4,0x1.1c029e507fabcp2
 toRadians,-0x1.5f6a1f54ab2a4p-7,-0x1.3a9a47b707798p-1
 toRadians,-0x1.3c0058e027e76p-6,-0x1.1ae62d156322p0
 toRadians,0x1.91a20082f74adp-3,0x1.678f764a36e11p3
 toRadians,0x1.5a7a88c4fde6ep-8,0x1.362f0ea5bddffp-2
-toRadians,-0x1.dd2d62c4677b5p-7,-0x1.ab30fa92aab8ep-1
-toRadians,0x1.c090beda0a1ebp-4,0x1.91939eabe933ep2
-toRadians,-0x1.fa64fbfece91ep-8,-0x1.c559104a2fc35p-2
+toRadians,-0x1.dd2d62c4677b6p-7,-0x1.ab30fa92aab8ep-1
+toRadians,0x1.c090beda0a1eap-4,0x1.91939eabe933ep2
+toRadians,-0x1.fa64fbfece91fp-8,-0x1.c559104a2fc35p-2
 toRadians,0x1.03e06cf40987fp-6,0x1.d14eb0092067dp-1
 toRadians,-0x1.9e2d350067dbbp-8,-0x1.72ca4846014p-2
 toRadians,0x1.2b149a9831e4ep-2,0x1.0bc032a6985a2p4
 toRadians,-0x1.eb36b025b1424p-7,-0x1.b7c1deca5038ep-1
 toRadians,-0x1.581a480c8d362p-4,-0x1.340e855ef1af6p2
 toRadians,-0x1.0c3f652158269p-7,-0x1.e04ba8df602b9p-2
-toRadians,-0x1.309379cfc0976p-11,-0x1.10abb1f2f2834p-5
+toRadians,-0x1.309379cfc0977p-11,-0x1.10abb1f2f2834p-5
 toRadians,0x1.3f6a7aa234715p-7,0x1.1df4bd59b7a1fp-1
 toRadians,-0x1.65dd0dca04ae2p-7,-0x1.40604556bbc5ap-1
-toRadians,-0x1.69934495b8f4ap-6,-0x1.43b2f25b4c342p0
+toRadians,-0x1.69934495b8f4bp-6,-0x1.43b2f25b4c342p0
 toRadians,-0x1.f6766633423b6p-7,-0x1.c1d3ebf82f78ep-1
-toRadians,0x1.4c96adb0ee339p-6,0x1.29bfb0889a191p0
+toRadians,0x1.4c96adb0ee33ap-6,0x1.29bfb0889a191p0
 toRadians,0x1.51c4cc005ef3ep-7,0x1.2e62e47bbc5f8p-1
 toRadians,-0x1.37fcc828121c6p-8,-0x1.174e407648858p-2
 toRadians,0x1.3c85a053a8dbfp-6,0x1.1b5d7e6798ac9p0
 toRadians,0x1.2fa1707ca7533p-6,0x1.0fd303496037ep0
 toRadians,-0x1.fd30ff4ab5bb4p-8,-0x1.c7da125a7e8cp-2
-toRadians,-0x1.07053d4647f61p-3,-0x1.d6efb37cbda3cp2
+toRadians,-0x1.07053d4647f62p-3,-0x1.d6efb37cbda3cp2
 toRadians,0x1.2e6539dacf422p-6,0x1.0eb7ec831448p0
 toRadians,0x1.90768a6c41a28p-6,0x1.66835ed1b0038p0
-toRadians,-0x1.3b5f769af4c14p-9,-0x1.1a5625378947dp-3
+toRadians,-0x1.3b5f769af4c15p-9,-0x1.1a5625378947dp-3
 toRadians,0x1.5e2305a747b1bp-7,0x1.397571d90c1e8p-1
 toRadians,-0x1.9684bb151928dp-6,-0x1.6bef2c36e0214p0
 toRadians,0x1.80aab10e1f8a3p-10,0x1.585f20e007884p-4
-toRadians,-0x1.73454254905c1p-9,-0x1.4c60f0d6580cep-3
+toRadians,-0x1.73454254905cp-9,-0x1.4c60f0d6580cep-3
 toRadians,-0x1.64e173018ac6bp-6,-0x1.3f7f05ce9fc63p0
 toRadians,0x1.6c33df8f50558p-9,0x1.460d18271bc01p-3
 toRadians,-0x1.a66398e8cab57p-11,-0x1.7a2470a132a52p-5
-toRadians,-0x1.4044a3024d4c3p-6,-0x1.1eb80b6a865f3p0
+toRadians,-0x1.4044a3024d4c4p-6,-0x1.1eb80b6a865f3p0
 toRadians,0x1.a5edecbd257d2p-7,0x1.79bb1811e0512p-1
 toRadians,0x1.2f63061aafa03p-9,0x1.0f9b22b1cf7b8p-3
 toRadians,0x1.57a84a0a48335p-6,0x1.33a878472947cp0
 toRadians,-0x1.1fbadf0bde297p-5,-0x1.0196d9ca7dd6dp1
-toRadians,0x1.0579eb2f75c14p-5,0x1.d42be1d80dafap0
-toRadians,0x1.4bb08a6dddaffp-10,0x1.28f1a8d929a18p-4
+toRadians,0x1.0579eb2f75c15p-5,0x1.d42be1d80dafap0
+toRadians,0x1.4bb08a6dddafep-10,0x1.28f1a8d929a18p-4
 toRadians,-0x1.785e5c8f3248bp-6,-0x1.50f15449acabep0
 toRadians,0x1.7c2e98ed7c20ap-6,0x1.545b4d0e37923p0
 toRadians,0x1.8afa8d5f6beb4p-7,0x1.619a745cb9468p-1
 toRadians,0x1.9ab81ca6e2142p-7,0x1.6fb1e76a3d452p-1
 toRadians,0x1.5abbd97d5f2dfp-8,0x1.366987d08a848p-2
 toRadians,0x1.10b1ed52fa65ap-5,0x1.e8423180e2757p0
-toRadians,0x1.1977488637e48p-7,0x1.f7f67cff2be2p-2
+toRadians,0x1.1977488637e49p-7,0x1.f7f67cff2be2p-2
 toRadians,-0x1.ea464cd97d49ep-5,-0x1.b6eaa9f230e9ap1
-toRadians,0x1.b91c40b587a6dp-4,0x1.8ae70c5a81ab4p2
-toRadians,0x1.4437a94ee4aa7p-5,0x1.2241293012d19p1
+toRadians,0x1.b91c40b587a6ep-4,0x1.8ae70c5a81ab4p2
+toRadians,0x1.4437a94ee4aa6p-5,0x1.2241293012d19p1
 toRadians,0x1.f5efccce5b07bp-6,0x1.c15b6c1b274d9p0
-toRadians,-0x1.9f99237013c3ep-9,-0x1.741017345ad98p-3
+toRadians,-0x1.9f99237013c3dp-9,-0x1.741017345ad98p-3
 toRadians,-0x1.0973876df224ap-6,-0x1.db49e80f5e81ep-1
 toRadians,0x1.6fa164f8ba5d2p-6,0x1.491eb12f08f23p0
 toRadians,0x1.6c2759fa3c865p-9,0x1.4601e25dc1082p-3
 toRadians,-0x1.9e741ead7ac95p-9,-0x1.7309c44a173a5p-3
 toRadians,0x1.9030fa34f406dp-6,0x1.664518133618ep0
-toRadians,-0x1.efe26ef81c43dp-4,-0x1.bbf05b72f4d36p2
-toRadians,-0x1.f34430fc0f19fp-7,-0x1.bef76c89ad7e8p-1
+toRadians,-0x1.efe26ef81c43ep-4,-0x1.bbf05b72f4d36p2
+toRadians,-0x1.f34430fc0f19ep-7,-0x1.bef76c89ad7e8p-1
 toRadians,-0x1.2cbb12850bd1p-6,-0x1.0d3a694a092f3p0
-toRadians,-0x1.6ca79b649b4dap-6,-0x1.4674b45e4bef9p0
-toRadians,-0x1.5ed33ec70db7ap-6,-0x1.3a1335377f47p0
+toRadians,-0x1.6ca79b649b4dbp-6,-0x1.4674b45e4bef9p0
+toRadians,-0x1.5ed33ec70db7bp-6,-0x1.3a1335377f47p0
 toRadians,0x1.4bb2ac4bfd435p0,0x1.28f39188d12e9p6
 toRadians,-0x1.1e73f51abbca9p-5,-0x1.00722ea8a6779p1
 toRadians,0x1.50359082fe9fap-6,0x1.2cfd7b2947624p0
@@ -3880,41 +3880,41 @@
 toRadians,0x1.52c145c51ab79p0,0x1.2f44eba454717p6
 toRadians,-0x1.5082c8921deefp-5,-0x1.2d429c72b1d75p1
 toRadians,0x1.288a3adfc8e7ap-5,0x1.0979f3ee43321p1
-toRadians,-0x1.ef75b508090c5p-8,-0x1.bb8f05364e233p-2
+toRadians,-0x1.ef75b508090c6p-8,-0x1.bb8f05364e233p-2
 toRadians,0x1.365709f9e92dbp-5,0x1.15d4b01c6f66fp1
-toRadians,0x1.bd29cf8766e9fp-8,0x1.8e87eb1b1cb6ap-2
-toRadians,0x1.5705e9472736dp-10,0x1.331719fc9f227p-4
+toRadians,0x1.bd29cf8766eap-8,0x1.8e87eb1b1cb6ap-2
+toRadians,0x1.5705e9472736ep-10,0x1.331719fc9f227p-4
 toRadians,-0x1.c0f0760371a15p-5,-0x1.91e94f0aeb6fdp1
 toRadians,0x1.aa4b9d7189cd6p-11,0x1.7da3b3ce92d0bp-5
-toRadians,-0x1.e36f3239795c7p-7,-0x1.b0cafe786941p-1
+toRadians,-0x1.e36f3239795c8p-7,-0x1.b0cafe786941p-1
 toRadians,-0x1.5d453f9711567p-6,-0x1.38aee7103062ap0
-toRadians,0x1.4f5935250958dp-9,0x1.2c3835144a6c6p-3
-toRadians,-0x1.fc315cc3236d5p-5,-0x1.c6f5372783487p1
+toRadians,0x1.4f5935250958cp-9,0x1.2c3835144a6c6p-3
+toRadians,-0x1.fc315cc3236d6p-5,-0x1.c6f5372783487p1
 toRadians,-0x1.5f6f0db8815dcp-11,-0x1.3a9eb1dd80ae8p-5
 toRadians,0x1.044ced10f50cep-6,0x1.d210f4f97b4fap-1
 toRadians,-0x1.0820c249570a6p-12,-0x1.d8eb574a001cap-7
-toRadians,0x1.c0dce341827e4p-7,0x1.91d7c92de11e9p-1
-toRadians,-0x1.54c0758b47cf8p-5,-0x1.310e8efc1fc1dp1
+toRadians,0x1.c0dce341827e5p-7,0x1.91d7c92de11e9p-1
+toRadians,-0x1.54c0758b47cf9p-5,-0x1.310e8efc1fc1dp1
 toRadians,-0x1.0c22f069d37e6p-6,-0x1.e018b5a31fb2cp-1
 toRadians,-0x1.0d44d90e1098cp-6,-0x1.e21fca05c552fp-1
 toRadians,0x1.aa370725bfc19p-6,0x1.7d914597b0f93p0
-toRadians,0x1.5dae1ab6a26cap-8,0x1.390cc646e30c2p-2
+toRadians,0x1.5dae1ab6a26cbp-8,0x1.390cc646e30c2p-2
 toRadians,-0x1.93d8aca9afc4cp-6,-0x1.698ac60d72cfap0
-toRadians,0x1.dde50e06e847bp-8,0x1.abd568673b34cp-2
-toRadians,0x1.85367605681f8p-8,0x1.5c70fd2cabf94p-2
+toRadians,0x1.dde50e06e847cp-8,0x1.abd568673b34cp-2
+toRadians,0x1.85367605681f9p-8,0x1.5c70fd2cabf94p-2
 toRadians,0x1.0f398ad1f05d4p-6,0x1.e5a0476d86f65p-1
 toRadians,-0x1.d61f59195aa6cp-7,-0x1.a4e02134d1a6bp-1
 toRadians,-0x1.2ea0ffdaa0e6p-6,-0x1.0eed6f92fd525p0
-toRadians,-0x1.ed05911e835b4p-7,-0x1.b96042cbd3729p-1
+toRadians,-0x1.ed05911e835b5p-7,-0x1.b96042cbd3729p-1
 toRadians,0x1.2cd0b41a46717p-3,0x1.0d4dc6ca8eaa8p3
 toRadians,0x1.381d20fe74a4p-6,0x1.176b35da6d972p0
 toRadians,-0x1.135812197a196p-5,-0x1.ed0067a8591e4p0
-toRadians,0x1.9487e02c71b95p-4,0x1.6a279f36895cap2
-toRadians,0x1.2f1b98fbb5e38p-7,0x1.0f5b3100c4226p-1
+toRadians,0x1.9487e02c71b94p-4,0x1.6a279f36895cap2
+toRadians,0x1.2f1b98fbb5e37p-7,0x1.0f5b3100c4226p-1
 toRadians,-0x1.df126097b8ce9p-10,-0x1.ace32a703c8p-4
 toRadians,0x1.27602746d31p-5,0x1.086f19d122d5p1
 toRadians,-0x1.6f00c2e92cd4ep-7,-0x1.488ee2ccee45dp-1
-toRadians,-0x1.6d6cad1da49f1p-8,-0x1.4725214f128bcp-2
+toRadians,-0x1.6d6cad1da49f2p-8,-0x1.4725214f128bcp-2
 toRadians,0x1.53853b6e52504p-6,0x1.2ff45a46f0239p0
 toRadians,-0x1.35bedb84f10a2p-6,-0x1.154c72afce441p0
 toRadians,0x1.e5b425177b774p-8,0x1.b2d3161ad8a1fp-2
@@ -3922,14 +3922,14 @@
 toRadians,0x1.9e42202419179p-7,0x1.72dd02718e33ep-1
 toRadians,0x1.91ed5a574b502p-7,0x1.67d2eb7176ee9p-1
 toRadians,0x1.88624a4753cb9p-5,0x1.5f47c6b4bedb7p1
-toRadians,-0x1.88c9a0d956ca6p-10,-0x1.5fa44a11aabe3p-4
+toRadians,-0x1.88c9a0d956ca7p-10,-0x1.5fa44a11aabe3p-4
 toRadians,-0x1.a5a8e50293634p-8,-0x1.797d4b83f869p-2
 toRadians,-0x1.aee96588d5d7ap-6,-0x1.81c5b0333bb09p0
-toRadians,-0x1.a0bb132c02b9p-7,-0x1.7513a7bf2c3eep-1
+toRadians,-0x1.a0bb132c02b91p-7,-0x1.7513a7bf2c3eep-1
 toRadians,-0x1.159284ff767c1p-2,-0x1.f0fdca230f3f5p3
 toRadians,0x1.6ec5baab9dfccp-7,0x1.485a099e8a8a4p-1
 toRadians,-0x1.479fe44ef6b39p-4,-0x1.254e05afbfe62p2
-toRadians,0x1.0ce113f6e801dp-4,0x1.e16d26e044eb6p1
+toRadians,0x1.0ce113f6e801cp-4,0x1.e16d26e044eb6p1
 ulp,0x1.0p-52,-0x1.9369810dd84a3p0
 ulp,0x1.0p-54,-0x1.9f2928d31e01cp-2
 ulp,0x1.0p-51,-0x1.73ea1caeccadfp1
diff --git a/metrictests/memory/apps/Android.bp b/metrictests/memory/apps/Android.bp
index 480886e..4c13689 100644
--- a/metrictests/memory/apps/Android.bp
+++ b/metrictests/memory/apps/Android.bp
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_license"],
+}
+
 android_test_helper_app {
     name: "LibcoreHeapDumper",
     sdk_version: "current",
diff --git a/metrictests/memory/host/Android.bp b/metrictests/memory/host/Android.bp
index beed9e1..10ee832 100644
--- a/metrictests/memory/host/Android.bp
+++ b/metrictests/memory/host/Android.bp
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_license"],
+}
+
 java_test_host {
     name: "libcore-memory-metrics-tests",
     srcs: ["src/**/*.java"],
@@ -20,4 +28,7 @@
         "ahat",
     ],
     test_suites: ["general-tests"],
+    test_options: {
+        unit_test: false,
+    },
 }
diff --git a/mmodules/core_platform_api/Android.bp b/mmodules/core_platform_api/Android.bp
index f2a042b..7036aba 100644
--- a/mmodules/core_platform_api/Android.bp
+++ b/mmodules/core_platform_api/Android.bp
@@ -19,95 +19,41 @@
 // The API specification .txt files managed by this only contain the additional
 // classes/members that are in the intra-core API but which are not in the public
 // API.
-droidstubs {
-    name: "art-module-platform-api-stubs-source",
-    srcs: [
-        ":art_module_api_files",
-    ],
-    sdk_version: "none",
-    system_modules: "none",
-    libs: [
-        // Needed to break the cycle in the platform api caused by
-        // b/141747409.
-        "i18n.module.intra.core.api.stubs",
-    ],
-
-    installable: false,
-    args: "--hide HiddenSuperclass " +
-        "--hide-annotation libcore.api.Hide " +
-        "--show-single-annotation libcore.api.CorePlatformApi " +
-        "--skip-annotation-instance-methods=false ",
-    merge_inclusion_annotations_dirs: ["ojluni-annotated-mmodule-stubs"],
-
-    api_filename: "api.txt",
-    removed_api_filename: "removed.txt",
-    previous_api: "previous.txt",
-
-    check_api: {
-        current: {
-            api_file: "api/platform/current-api.txt",
-            removed_api_file: "api/platform/current-removed.txt",
-        },
-        last_released: {
-            api_file: "api/platform/last-api.txt",
-            removed_api_file: "api/platform/last-removed.txt",
-        },
-    },
-}
-
-// A special set of system modules that is needed to break the cycle in the
-// platform api caused by b/141747409.
-java_system_modules {
-    name: "break-cycle-in-core-platform-system-modules",
-    libs: [
-        "art.module.intra.core.api.stubs",
-        "i18n.module.intra.core.api.stubs",
-    ],
-}
-
-// A library containing the core platform API stubs of the ART module.
 //
-// Core platform APIs are only intended for use of other parts of the platform, not the
-// core library modules.
-java_library {
-    name: "art.module.platform.api.stubs",
-    srcs: [
-        ":art-module-platform-api-stubs-source",
-    ],
-    hostdex: true,
+// There are two versions of the API surface. The "stable" version contains the
+// APIs which are considered stable, and is indicated in source with the
+// annotation @CorePlatformApi(status = CorePlatformApi.Status.STABLE). The
+// "legacy" version also includes those which have not been audited for stability
+// or which are deprecated (which have the default status of LEGACY_ONLY).
+//
+// TODO(b/161973015): Remove the legacy/stable distinction once no longer useful.
 
-    sdk_version: "none",
-    system_modules: "break-cycle-in-core-platform-system-modules",
-    patch_module: "java.base",
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_license":
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-GPL
+    //   SPDX-license-identifier-GPL-2.0
+    //   SPDX-license-identifier-LGPL
+    //   SPDX-license-identifier-MIT
+    //   SPDX-license-identifier-W3C
+    default_applicable_licenses: ["libcore_license"],
 }
 
-// Used when compiling higher-level code against core.platform.api.stubs.
-java_system_modules {
-    name: "art-module-platform-api-stubs-system-modules",
-    visibility: [
-        "//art/build/sdk",
-        "//external/conscrypt",
-        "//external/icu/android_icu4j",
-        "//external/wycheproof",
-    ],
-    libs: [
-        "art.module.platform.api.stubs",
-    ],
-}
-
-// Ideally this should be a restricted whitelist but there are hundreds of modules that depend on
+// Ideally this should be a restricted allowlist but there are hundreds of modules that depend on
 // this.
 // TODO(http://b/134561230) - limit the number of dependents on this.
 core_platform_visibility = ["//visibility:public"]
 
-// A library containing the core platform API stubs for the core libraries.
+// Libraries containing the core platform API stubs for the core libraries.
 //
 // Although this stubs library is primarily used by the Java compiler / build to indicate
 // the core platform API surface area, compile_dex: true is used so that the Core Platform
 // API annotations are available to the dex tools that enable enforcement of runtime
 // accessibility. b/119068555
 java_library {
-    name: "core.platform.api.stubs",
+    name: "legacy.core.platform.api.stubs",
     visibility: core_platform_visibility,
     hostdex: true,
     compile_dex: true,
@@ -115,19 +61,52 @@
     sdk_version: "none",
     system_modules: "none",
     static_libs: [
-        "art.module.platform.api.stubs",
+        "art.module.public.api.stubs.module_lib",
         "conscrypt.module.platform.api.stubs",
-        "i18n.module.platform.api.stubs",
+        "legacy.i18n.module.platform.api.stubs",
     ],
     patch_module: "java.base",
 }
 
-// Used when compiling higher-level code against core.platform.api.stubs.
+java_library {
+    name: "stable.core.platform.api.stubs",
+    visibility: core_platform_visibility,
+    hostdex: true,
+    compile_dex: true,
+
+    sdk_version: "none",
+    system_modules: "none",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+        // conscrypt only has a stable version, so it is okay to depend on it here:
+        "conscrypt.module.platform.api.stubs",
+        "stable.i18n.module.platform.api.stubs",
+    ],
+    patch_module: "java.base",
+}
+
+// Used when compiling higher-level code against *.core.platform.api.stubs.
 java_system_modules {
-    name: "core-platform-api-stubs-system-modules",
+    name: "legacy-core-platform-api-stubs-system-modules",
     visibility: core_platform_visibility,
     libs: [
-        "core.platform.api.stubs",
+        "legacy.core.platform.api.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+}
+
+java_system_modules {
+    name: "stable-core-platform-api-stubs-system-modules",
+    visibility: core_platform_visibility,
+    libs: [
+        "stable.core.platform.api.stubs",
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
         "core-lambda-stubs-for-system-modules",
diff --git a/mmodules/core_platform_api/api/legacy_platform/current.txt b/mmodules/core_platform_api/api/legacy_platform/current.txt
new file mode 100644
index 0000000..15741a7
--- /dev/null
+++ b/mmodules/core_platform_api/api/legacy_platform/current.txt
@@ -0,0 +1,865 @@
+// Signature format: 2.0
+package android.compat {
+
+  public final class Compatibility {
+    method public static void clearBehaviorChangeDelegate();
+    method public static void clearOverrides();
+    method public static boolean isChangeEnabled(long);
+    method public static void reportUnconditionalChange(long);
+    method public static void setBehaviorChangeDelegate(android.compat.Compatibility.BehaviorChangeDelegate);
+    method public static void setOverrides(android.compat.Compatibility.ChangeConfig);
+  }
+
+  public static interface Compatibility.BehaviorChangeDelegate {
+    method public default boolean isChangeEnabled(long);
+    method public default void onChangeReported(long);
+  }
+
+  public static final class Compatibility.ChangeConfig {
+    ctor public Compatibility.ChangeConfig(@NonNull java.util.Set<java.lang.Long>, @NonNull java.util.Set<java.lang.Long>);
+    method @NonNull public long[] getDisabledChangesArray();
+    method @NonNull public java.util.Set<java.lang.Long> getDisabledSet();
+    method @NonNull public long[] getEnabledChangesArray();
+    method @NonNull public java.util.Set<java.lang.Long> getEnabledSet();
+    method public boolean isEmpty();
+    method public boolean isForceDisabled(long);
+    method public boolean isForceEnabled(long);
+  }
+
+}
+
+package android.system {
+
+  public final class NetlinkSocketAddress extends java.net.SocketAddress {
+    ctor public NetlinkSocketAddress(int, int);
+    method public int getGroupsMask();
+    method public int getPortId();
+  }
+
+  public final class Os {
+    method @Nullable public static android.system.StructCapUserData[] capget(@NonNull android.system.StructCapUserHeader) throws android.system.ErrnoException;
+    method public static void capset(@NonNull android.system.StructCapUserHeader, @NonNull android.system.StructCapUserData[]) throws android.system.ErrnoException;
+    method public static int getpgid(int) throws android.system.ErrnoException;
+    method @Nullable public static android.system.StructRlimit getrlimit(int) throws android.system.ErrnoException;
+    method public static int getsockoptInt(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method @Nullable public static android.system.StructLinger getsockoptLinger(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method public static int ioctlInt(@NonNull java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method @Nullable public static java.io.FileDescriptor[] pipe2(int) throws android.system.ErrnoException;
+    method @Nullable public static String realpath(@Nullable String) throws android.system.ErrnoException;
+    method public static void setpgid(int, int) throws android.system.ErrnoException;
+    method public static void setregid(int, int) throws android.system.ErrnoException;
+    method public static void setreuid(int, int) throws android.system.ErrnoException;
+    method public static void setsockoptIfreq(@NonNull java.io.FileDescriptor, int, int, @Nullable String) throws android.system.ErrnoException;
+    method public static void setsockoptLinger(@NonNull java.io.FileDescriptor, int, int, @NonNull android.system.StructLinger) throws android.system.ErrnoException;
+    method public static long splice(@NonNull java.io.FileDescriptor, @Nullable android.system.Int64Ref, @NonNull java.io.FileDescriptor, @Nullable android.system.Int64Ref, long, int) throws android.system.ErrnoException;
+    method public static void unlink(@Nullable String) throws android.system.ErrnoException;
+  }
+
+  public final class OsConstants {
+    method public static int CAP_TO_INDEX(int);
+    method public static int CAP_TO_MASK(int);
+    field public static final int ARPHRD_LOOPBACK;
+    field public static final int EUSERS;
+    field public static final int MAP_POPULATE;
+    field public static final int O_DIRECT;
+    field public static final int PR_CAP_AMBIENT;
+    field public static final int PR_CAP_AMBIENT_RAISE;
+    field public static final int RLIMIT_NOFILE;
+    field public static final int RTMGRP_IPV4_IFADDR;
+    field public static final int SPLICE_F_MORE;
+    field public static final int SPLICE_F_MOVE;
+    field public static final int TIOCOUTQ;
+    field public static final int UDP_ENCAP;
+    field public static final int UDP_ENCAP_ESPINUDP;
+    field public static final int XATTR_CREATE;
+    field public static final int XATTR_REPLACE;
+    field public static final int _LINUX_CAPABILITY_VERSION_3;
+  }
+
+  public final class PacketSocketAddress extends java.net.SocketAddress {
+    ctor public PacketSocketAddress(int, int, byte[]);
+  }
+
+  public final class StructCapUserData {
+    ctor public StructCapUserData(int, int, int);
+    field public final int effective;
+    field public final int inheritable;
+    field public final int permitted;
+  }
+
+  public final class StructCapUserHeader {
+    ctor public StructCapUserHeader(int, int);
+  }
+
+  public final class StructLinger {
+    ctor public StructLinger(int, int);
+    method public boolean isOn();
+    field public final int l_linger;
+  }
+
+  public final class StructRlimit {
+    field public final long rlim_cur;
+  }
+
+  public final class UnixSocketAddress extends java.net.SocketAddress {
+    method public static android.system.UnixSocketAddress createFileSystem(String);
+  }
+
+  public final class VmSocketAddress extends java.net.SocketAddress {
+    ctor public VmSocketAddress(int, int);
+    method public int getSvmCid();
+    method public int getSvmPort();
+  }
+
+}
+
+package com.android.okhttp.internalandroidapi {
+
+  public final class AndroidResponseCacheAdapter {
+    ctor public AndroidResponseCacheAdapter(@NonNull com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder);
+    method public void close() throws java.io.IOException;
+    method public void delete() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method @Nullable public java.net.CacheResponse get(@NonNull java.net.URI, @NonNull String, @Nullable java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method @NonNull public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
+    method public int getHitCount();
+    method public long getMaxSize();
+    method public int getNetworkCount();
+    method public int getRequestCount();
+    method public long getSize() throws java.io.IOException;
+    method @Nullable public java.net.CacheRequest put(@NonNull java.net.URI, @NonNull java.net.URLConnection) throws java.io.IOException;
+  }
+
+  public interface HasCacheHolder {
+    method @NonNull public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
+  }
+
+  public static final class HasCacheHolder.CacheHolder {
+    method @NonNull public static com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder create(@NonNull java.io.File, long);
+    method public boolean isEquivalent(@NonNull java.io.File, long);
+  }
+
+}
+
+package dalvik.annotation.codegen {
+
+  @java.lang.annotation.Repeatable(CovariantReturnType.CovariantReturnTypes.class) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CovariantReturnType {
+    method public abstract int presentAfter();
+    method public abstract Class<?> returnType();
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public static @interface CovariantReturnType.CovariantReturnTypes {
+    method public abstract dalvik.annotation.codegen.CovariantReturnType[] value();
+  }
+
+}
+
+package dalvik.annotation.optimization {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface CriticalNative {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface FastNative {
+  }
+
+}
+
+package dalvik.system {
+
+  public final class AnnotatedStackTraceElement {
+    method @Nullable public Object getBlockedOn();
+    method @Nullable public Object[] getHeldLocks();
+    method @NonNull public StackTraceElement getStackTraceElement();
+  }
+
+  public class AppSpecializationHooks {
+    method public static void handleCompatChangesBeforeBindingApplication();
+  }
+
+  public class BaseDexClassLoader extends java.lang.ClassLoader {
+    method public void addDexPath(@Nullable String);
+    method public void addNativePath(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public String getLdLibraryPath();
+    method public void reportClassLoaderChain();
+    method public static void setReporter(@Nullable dalvik.system.BaseDexClassLoader.Reporter);
+  }
+
+  public static interface BaseDexClassLoader.Reporter {
+    method public void report(@NonNull java.util.Map<java.lang.String,java.lang.String>);
+  }
+
+  public final class BlockGuard {
+    method @NonNull public static dalvik.system.BlockGuard.Policy getThreadPolicy();
+    method @NonNull public static dalvik.system.BlockGuard.VmPolicy getVmPolicy();
+    method public static void setThreadPolicy(@NonNull dalvik.system.BlockGuard.Policy);
+    method public static void setVmPolicy(@NonNull dalvik.system.BlockGuard.VmPolicy);
+    field public static final dalvik.system.BlockGuard.Policy LAX_POLICY;
+    field public static final dalvik.system.BlockGuard.VmPolicy LAX_VM_POLICY;
+  }
+
+  public static interface BlockGuard.Policy {
+    method public int getPolicyMask();
+    method public void onReadFromDisk();
+    method public void onUnbufferedIO();
+    method public void onWriteToDisk();
+  }
+
+  public static interface BlockGuard.VmPolicy {
+    method public void onPathAccess(@NonNull String);
+  }
+
+  public final class CloseGuard {
+    method public void close();
+    method public static dalvik.system.CloseGuard get();
+    method public static dalvik.system.CloseGuard.Reporter getReporter();
+    method public void open(String);
+    method public void openWithCallSite(String, String);
+    method public static void setEnabled(boolean);
+    method public static void setReporter(dalvik.system.CloseGuard.Reporter);
+    method public void warnIfOpen();
+  }
+
+  public static interface CloseGuard.Reporter {
+    method public void report(String, Throwable);
+    method public default void report(String);
+  }
+
+  public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
+    ctor public DelegateLastClassLoader(String, String, ClassLoader, ClassLoader[]);
+  }
+
+  @Deprecated public final class DexFile {
+    method @Deprecated @NonNull public static dalvik.system.DexFile.OptimizationInfo getDexFileOptimizationInfo(@NonNull String, @NonNull String) throws java.io.FileNotFoundException;
+    method @Deprecated @Nullable public static String[] getDexFileOutputPaths(@NonNull String, @NonNull String) throws java.io.FileNotFoundException;
+    method @Deprecated public static int getDexOptNeeded(@NonNull String, @NonNull String, @NonNull String, @Nullable String, boolean, boolean) throws java.io.FileNotFoundException, java.io.IOException;
+    method @Deprecated @NonNull public static String getSafeModeCompilerFilter(@NonNull String);
+    method @Deprecated public static boolean isProfileGuidedCompilerFilter(@NonNull String);
+    method @Deprecated public static boolean isValidCompilerFilter(@NonNull String);
+    field @Deprecated public static final int DEX2OAT_FOR_FILTER = 3; // 0x3
+    field @Deprecated public static final int NO_DEXOPT_NEEDED = 0; // 0x0
+  }
+
+  @Deprecated public static final class DexFile.OptimizationInfo {
+    method @Deprecated @NonNull public String getReason();
+    method @Deprecated @NonNull public String getStatus();
+  }
+
+  public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
+    ctor public PathClassLoader(@NonNull String, @Nullable String, @Nullable ClassLoader, @Nullable ClassLoader[]);
+  }
+
+  public final class RuntimeHooks {
+    method public static void setTimeZoneIdSupplier(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public static void setUncaughtExceptionPreHandler(@Nullable java.lang.Thread.UncaughtExceptionHandler);
+  }
+
+  public abstract class SocketTagger {
+    ctor public SocketTagger();
+    method public static dalvik.system.SocketTagger get();
+    method public static void set(dalvik.system.SocketTagger);
+    method public abstract void tag(java.io.FileDescriptor) throws java.net.SocketException;
+    method public final void tag(java.net.Socket) throws java.net.SocketException;
+    method public final void tag(java.net.DatagramSocket) throws java.net.SocketException;
+    method public abstract void untag(java.io.FileDescriptor) throws java.net.SocketException;
+    method public final void untag(java.net.Socket) throws java.net.SocketException;
+    method public final void untag(java.net.DatagramSocket) throws java.net.SocketException;
+  }
+
+  public final class VMDebug {
+    method public static void attachAgent(String, ClassLoader) throws java.io.IOException;
+    method public static long countInstancesOfClass(Class, boolean);
+    method public static long[] countInstancesOfClasses(Class[], boolean);
+    method public static void dumpHprofData(String) throws java.io.IOException;
+    method public static void dumpHprofData(String, java.io.FileDescriptor) throws java.io.IOException;
+    method public static void dumpHprofDataDdms();
+    method public static void dumpReferenceTables();
+    method public static int getAllocCount(int);
+    method @dalvik.annotation.optimization.FastNative public static int getLoadedClassCount();
+    method public static int getMethodTracingMode();
+    method public static String getRuntimeStat(String);
+    method public static java.util.Map<java.lang.String,java.lang.String> getRuntimeStats();
+    method public static String[] getVmFeatureList();
+    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggerConnected();
+    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggingEnabled();
+    method @dalvik.annotation.optimization.FastNative public static long lastDebuggerActivity();
+    method @dalvik.annotation.optimization.FastNative public static void printLoadedClasses(int);
+    method public static void resetAllocCount(int);
+    method public static void setAllocTrackerStackDepth(int);
+    method public static void startAllocCounting();
+    method public static void startMethodTracing(String, int, int, boolean, int);
+    method public static void startMethodTracing(String, java.io.FileDescriptor, int, int, boolean, int, boolean);
+    method public static void startMethodTracingDdms(int, int, boolean, int);
+    method public static void stopAllocCounting();
+    method public static void stopMethodTracing();
+    method @dalvik.annotation.optimization.FastNative public static long threadCpuTimeNanos();
+    field public static final int KIND_ALL_COUNTS = -1; // 0xffffffff
+    field public static final int KIND_GLOBAL_ALLOCATED_BYTES = 2; // 0x2
+    field public static final int KIND_GLOBAL_ALLOCATED_OBJECTS = 1; // 0x1
+    field public static final int KIND_GLOBAL_CLASS_INIT_COUNT = 32; // 0x20
+    field public static final int KIND_GLOBAL_CLASS_INIT_TIME = 64; // 0x40
+    field public static final int KIND_GLOBAL_FREED_BYTES = 8; // 0x8
+    field public static final int KIND_GLOBAL_FREED_OBJECTS = 4; // 0x4
+    field public static final int KIND_GLOBAL_GC_INVOCATIONS = 16; // 0x10
+    field public static final int KIND_THREAD_ALLOCATED_BYTES = 131072; // 0x20000
+    field public static final int KIND_THREAD_ALLOCATED_OBJECTS = 65536; // 0x10000
+    field public static final int KIND_THREAD_GC_INVOCATIONS = 1048576; // 0x100000
+    field public static final int TRACE_COUNT_ALLOCS = 1; // 0x1
+  }
+
+  public final class VMRuntime {
+    method @dalvik.annotation.optimization.FastNative public long addressOf(Object);
+    method public static void bootCompleted();
+    method public void clampGrowthLimit();
+    method public void clearGrowthLimit();
+    method public static String getCurrentInstructionSet();
+    method public static String getInstructionSet(String);
+    method public static dalvik.system.VMRuntime getRuntime();
+    method public int getTargetSdkVersion();
+    method @dalvik.annotation.optimization.FastNative public boolean is64Bit();
+    method public static boolean is64BitAbi(String);
+    method public static boolean is64BitInstructionSet(String);
+    method @dalvik.annotation.optimization.FastNative public boolean isCheckJniEnabled();
+    method @dalvik.annotation.optimization.FastNative public boolean isNativeDebuggable();
+    method public static boolean isValidClassLoaderContext(String);
+    method @dalvik.annotation.optimization.FastNative public Object newNonMovableArray(Class<?>, int);
+    method @dalvik.annotation.optimization.FastNative public Object newUnpaddedArray(Class<?>, int);
+    method public void notifyStartupCompleted();
+    method public void preloadDexCaches();
+    method public static void registerAppInfo(String, String, String, String[], int);
+    method public void registerNativeAllocation(long);
+    method @Deprecated public void registerNativeAllocation(int);
+    method public void registerNativeFree(long);
+    method @Deprecated public void registerNativeFree(int);
+    method public static void registerSensitiveThread();
+    method public void requestConcurrentGC();
+    method public static void resetJitCounters();
+    method public static void setDedupeHiddenApiWarnings(boolean);
+    method public void setDisabledCompatChanges(long[]);
+    method public void setHiddenApiAccessLogSamplingRate(int);
+    method public void setHiddenApiExemptions(String[]);
+    method public static void setHiddenApiUsageLogger(dalvik.system.VMRuntime.HiddenApiUsageLogger);
+    method public static void setNonSdkApiUsageConsumer(java.util.function.Consumer<java.lang.String>);
+    method public static void setProcessDataDirectory(String);
+    method public static void setProcessPackageName(String);
+    method public void setTargetSdkVersion(int);
+    method public void updateProcessState(int);
+    method public String vmInstructionSet();
+    method public String vmLibrary();
+    field public static final int CODE_PATH_TYPE_PRIMARY_APK = 1; // 0x1
+    field public static final int CODE_PATH_TYPE_SECONDARY_DEX = 4; // 0x4
+    field public static final int CODE_PATH_TYPE_SPLIT_APK = 2; // 0x2
+    field public static final int SDK_VERSION_CUR_DEVELOPMENT = 10000; // 0x2710
+  }
+
+  public static interface VMRuntime.HiddenApiUsageLogger {
+    method public void hiddenApiUsed(int, String, String, int, boolean);
+    field public static final int ACCESS_METHOD_JNI = 2; // 0x2
+    field public static final int ACCESS_METHOD_LINKING = 3; // 0x3
+    field public static final int ACCESS_METHOD_NONE = 0; // 0x0
+    field public static final int ACCESS_METHOD_REFLECTION = 1; // 0x1
+  }
+
+  public final class VMStack {
+    method @Nullable @dalvik.annotation.optimization.FastNative public static dalvik.system.AnnotatedStackTraceElement[] getAnnotatedThreadStackTrace(Thread);
+  }
+
+  public final class ZygoteHooks {
+    method public static void gcAndFinalize();
+    method public static boolean isIndefiniteThreadSuspensionSafe();
+    method public static void onBeginPreload();
+    method public static void onEndPreload();
+    method public static void postForkChild(int, boolean, boolean, String);
+    method public static void postForkCommon();
+    method public static void postForkSystemServer(int);
+    method public static void preFork();
+    method public static void startZygoteNoThreadCreation();
+    method public static void stopZygoteNoThreadCreation();
+  }
+
+}
+
+package java.io {
+
+  public final class FileDescriptor {
+    method public int getInt$();
+    method public void setInt$(int);
+  }
+
+  public class FileInputStream extends java.io.InputStream {
+    ctor public FileInputStream(java.io.FileDescriptor, boolean);
+  }
+
+}
+
+package java.lang {
+
+  public class Thread implements java.lang.Runnable {
+    method public static java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionPreHandler();
+    method public static void setUncaughtExceptionPreHandler(java.lang.Thread.UncaughtExceptionHandler);
+  }
+
+}
+
+package java.net {
+
+  public class DatagramSocket implements java.io.Closeable {
+    method public java.io.FileDescriptor getFileDescriptor$();
+  }
+
+  public final class Inet4Address extends java.net.InetAddress {
+    field public static final java.net.InetAddress ALL;
+    field public static final java.net.InetAddress ANY;
+    field public static final java.net.InetAddress LOOPBACK;
+  }
+
+  public final class Inet6Address extends java.net.InetAddress {
+    field public static final java.net.InetAddress ANY;
+    field public static final java.net.InetAddress LOOPBACK;
+  }
+
+  public class InetAddress implements java.io.Serializable {
+    method public static void clearDnsCache();
+    method @NonNull public static java.net.InetAddress[] getAllByNameOnNet(@Nullable String, int) throws java.net.UnknownHostException;
+    method @NonNull public static java.net.InetAddress getByNameOnNet(@Nullable String, int) throws java.net.UnknownHostException;
+    method @Deprecated public static boolean isNumeric(String);
+    method @Deprecated public static java.net.InetAddress parseNumericAddress(String);
+  }
+
+  public class InetSocketAddress extends java.net.SocketAddress {
+    ctor public InetSocketAddress();
+  }
+
+  public class ServerSocket implements java.io.Closeable {
+    method public java.net.SocketImpl getImpl() throws java.net.SocketException;
+  }
+
+  public class Socket implements java.io.Closeable {
+    method public java.io.FileDescriptor getFileDescriptor$();
+  }
+
+  public abstract class SocketImpl implements java.net.SocketOptions {
+    method public java.io.FileDescriptor getFD$();
+  }
+
+}
+
+package java.nio {
+
+  public abstract class ByteBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.ByteBuffer> {
+    method public void setAccessible(boolean);
+  }
+
+  public class DirectByteBuffer extends java.nio.MappedByteBuffer {
+    ctor public DirectByteBuffer(int, long, java.io.FileDescriptor, Runnable, boolean);
+    method public final long address();
+    method public final void setAccessible(boolean);
+  }
+
+  public final class NIOAccess {
+    method public static Object getBaseArray(java.nio.Buffer);
+    method public static int getBaseArrayOffset(java.nio.Buffer);
+  }
+
+  public final class NioUtils {
+    method public static void freeDirectBuffer(java.nio.ByteBuffer);
+    method public static byte[] unsafeArray(java.nio.ByteBuffer);
+    method public static int unsafeArrayOffset(java.nio.ByteBuffer);
+  }
+
+}
+
+package java.security {
+
+  public abstract class Provider extends java.util.Properties {
+    method public void warmUpServiceProvision();
+  }
+
+  public abstract class Signature extends java.security.SignatureSpi {
+    method public java.security.SignatureSpi getCurrentSpi();
+  }
+
+}
+
+package java.text {
+
+  public abstract class DateFormat extends java.text.Format {
+    method public static final void set24HourTimePref(Boolean);
+  }
+
+}
+
+package java.util {
+
+  public class LinkedHashMap<K, V> extends java.util.HashMap<K,V> implements java.util.Map<K,V> {
+    method public java.util.Map.Entry<K,V> eldest();
+  }
+
+}
+
+package java.util.zip {
+
+  public class ZipEntry implements java.lang.Cloneable {
+    method public long getDataOffset();
+  }
+
+}
+
+package javax.crypto {
+
+  public class Cipher {
+    method public javax.crypto.CipherSpi getCurrentSpi();
+  }
+
+  public class Mac implements java.lang.Cloneable {
+    method public javax.crypto.MacSpi getCurrentSpi();
+  }
+
+}
+
+package javax.net.ssl {
+
+  public abstract class HttpsURLConnection extends java.net.HttpURLConnection {
+    method public static javax.net.ssl.HostnameVerifier getStrictHostnameVerifier();
+  }
+
+}
+
+package libcore.content.type {
+
+  public final class MimeMap {
+    method @NonNull public libcore.content.type.MimeMap.Builder buildUpon();
+    method @NonNull public static libcore.content.type.MimeMap.Builder builder();
+    method @NonNull public java.util.Set<java.lang.String> extensions();
+    method @NonNull public static libcore.content.type.MimeMap getDefault();
+    method @Nullable public String guessExtensionFromMimeType(@Nullable String);
+    method @Nullable public String guessMimeTypeFromExtension(@Nullable String);
+    method public boolean hasExtension(@Nullable String);
+    method public boolean hasMimeType(@Nullable String);
+    method @NonNull public java.util.Set<java.lang.String> mimeTypes();
+    method public static void setDefaultSupplier(@NonNull java.util.function.Supplier<libcore.content.type.MimeMap>);
+  }
+
+  public static final class MimeMap.Builder {
+    method @NonNull public libcore.content.type.MimeMap.Builder addMimeMapping(@NonNull String, @NonNull java.util.List<java.lang.String>);
+    method @NonNull public libcore.content.type.MimeMap build();
+  }
+
+}
+
+package libcore.io {
+
+  public class ForwardingOs implements libcore.io.Os {
+    ctor protected ForwardingOs(@NonNull libcore.io.Os);
+    method public boolean access(@Nullable String, int) throws android.system.ErrnoException;
+    method public java.io.FileDescriptor open(@Nullable String, int, int) throws android.system.ErrnoException;
+    method public void remove(@Nullable String) throws android.system.ErrnoException;
+    method public void rename(@Nullable String, @Nullable String) throws android.system.ErrnoException;
+    method @Nullable public android.system.StructStat stat(@Nullable String) throws android.system.ErrnoException;
+    method public void unlink(@Nullable String) throws android.system.ErrnoException;
+  }
+
+  public final class IoBridge {
+    method public static void closeAndSignalBlockedThreads(@NonNull java.io.FileDescriptor) throws java.io.IOException;
+    method @NonNull public static java.io.FileDescriptor open(@NonNull String, int) throws java.io.FileNotFoundException;
+    method public static int read(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int) throws java.io.IOException;
+    method public static void write(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int) throws java.io.IOException;
+  }
+
+  public final class IoUtils {
+    method public static int acquireRawFd(@NonNull java.io.FileDescriptor);
+    method public static void close(@Nullable java.io.FileDescriptor) throws java.io.IOException;
+    method public static void closeQuietly(@Nullable AutoCloseable);
+    method public static void closeQuietly(@Nullable java.io.FileDescriptor);
+    method public static void closeQuietly(@Nullable java.net.Socket);
+    method @NonNull public static byte[] readFileAsByteArray(@NonNull String) throws java.io.IOException;
+    method @NonNull public static String readFileAsString(@NonNull String) throws java.io.IOException;
+    method public static void setBlocking(@NonNull java.io.FileDescriptor, boolean) throws java.io.IOException;
+    method public static void setFdOwner(@NonNull java.io.FileDescriptor, @NonNull Object);
+  }
+
+  public final class Memory {
+    method public static void memmove(@NonNull Object, int, @NonNull Object, int, long);
+    method public static int peekInt(@NonNull byte[], int, @NonNull java.nio.ByteOrder);
+    method public static short peekShort(@NonNull byte[], int, @NonNull java.nio.ByteOrder);
+    method public static void pokeInt(@NonNull byte[], int, int, @NonNull java.nio.ByteOrder);
+    method public static void pokeLong(@NonNull byte[], int, long, @NonNull java.nio.ByteOrder);
+    method public static void pokeShort(@NonNull byte[], int, short, @NonNull java.nio.ByteOrder);
+  }
+
+  public interface Os {
+    method public static boolean compareAndSetDefault(libcore.io.Os, libcore.io.Os);
+    method public static libcore.io.Os getDefault();
+  }
+
+  public final class Streams {
+    method public static int copy(@NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws java.io.IOException;
+    method public static void readFully(@NonNull java.io.InputStream, @NonNull byte[]) throws java.io.IOException;
+    method @NonNull public static byte[] readFully(@NonNull java.io.InputStream) throws java.io.IOException;
+    method @NonNull public static String readFully(@NonNull java.io.Reader) throws java.io.IOException;
+    method @NonNull public static byte[] readFullyNoClose(@NonNull java.io.InputStream) throws java.io.IOException;
+    method public static int readSingleByte(@NonNull java.io.InputStream) throws java.io.IOException;
+    method public static long skipByReading(@NonNull java.io.InputStream, long) throws java.io.IOException;
+    method public static void writeSingleByte(@NonNull java.io.OutputStream, int) throws java.io.IOException;
+  }
+
+}
+
+package libcore.net {
+
+  public class InetAddressUtils {
+    method public static boolean isNumericAddress(String);
+    method public static java.net.InetAddress parseNumericAddress(String);
+  }
+
+  public abstract class NetworkSecurityPolicy {
+    ctor public NetworkSecurityPolicy();
+    method public static libcore.net.NetworkSecurityPolicy getInstance();
+    method public abstract boolean isCertificateTransparencyVerificationRequired(String);
+    method public abstract boolean isCleartextTrafficPermitted();
+    method public abstract boolean isCleartextTrafficPermitted(String);
+    method public static void setInstance(libcore.net.NetworkSecurityPolicy);
+  }
+
+}
+
+package libcore.net.event {
+
+  public final class NetworkEventDispatcher {
+    method public void dispatchNetworkConfigurationChange();
+    method public static libcore.net.event.NetworkEventDispatcher getInstance();
+  }
+
+}
+
+package libcore.net.http {
+
+  public interface Dns {
+    method @NonNull public java.util.List<java.net.InetAddress> lookup(@Nullable String) throws java.net.UnknownHostException;
+  }
+
+  public class HttpURLConnectionFactory {
+    method @NonNull public static libcore.net.http.HttpURLConnectionFactory createInstance();
+    method public java.net.URLConnection openConnection(@NonNull java.net.URL, @NonNull javax.net.SocketFactory, @NonNull java.net.Proxy) throws java.io.IOException;
+    method public void setDns(@NonNull libcore.net.http.Dns);
+    method public void setNewConnectionPool(int, long, @NonNull java.util.concurrent.TimeUnit);
+  }
+
+}
+
+package libcore.util {
+
+  public final class EmptyArray {
+    field @NonNull public static final boolean[] BOOLEAN;
+    field @NonNull public static final byte[] BYTE;
+    field @NonNull public static final float[] FLOAT;
+    field @NonNull public static final int[] INT;
+    field @NonNull public static final long[] LONG;
+    field @NonNull public static final Object[] OBJECT;
+    field @NonNull public static final String[] STRING;
+  }
+
+  public final class FP16 {
+    method public static short ceil(short);
+    method public static int compare(short, short);
+    method public static boolean equals(short, short);
+    method public static short floor(short);
+    method public static boolean greater(short, short);
+    method public static boolean greaterEquals(short, short);
+    method public static boolean isInfinite(short);
+    method public static boolean isNaN(short);
+    method public static boolean isNormalized(short);
+    method public static boolean less(short, short);
+    method public static boolean lessEquals(short, short);
+    method public static short max(short, short);
+    method public static short min(short, short);
+    method public static short rint(short);
+    method public static float toFloat(short);
+    method public static short toHalf(float);
+    method public static String toHexString(short);
+    method public static short trunc(short);
+    field public static final short EPSILON = 5120; // 0x1400
+    field public static final int EXPONENT_BIAS = 15; // 0xf
+    field public static final int EXPONENT_SHIFT = 10; // 0xa
+    field public static final int EXPONENT_SIGNIFICAND_MASK = 32767; // 0x7fff
+    field public static final short LOWEST_VALUE = -1025; // 0xfffffbff
+    field public static final int MAX_EXPONENT = 15; // 0xf
+    field public static final short MAX_VALUE = 31743; // 0x7bff
+    field public static final int MIN_EXPONENT = -14; // 0xfffffff2
+    field public static final short MIN_NORMAL = 1024; // 0x400
+    field public static final short MIN_VALUE = 1; // 0x1
+    field public static final short NEGATIVE_INFINITY = -1024; // 0xfffffc00
+    field public static final short NEGATIVE_ZERO = -32768; // 0xffff8000
+    field public static final short NaN = 32256; // 0x7e00
+    field public static final short POSITIVE_INFINITY = 31744; // 0x7c00
+    field public static final short POSITIVE_ZERO = 0; // 0x0
+    field public static final int SHIFTED_EXPONENT_MASK = 31; // 0x1f
+    field public static final int SIGNIFICAND_MASK = 1023; // 0x3ff
+    field public static final int SIGN_MASK = 32768; // 0x8000
+    field public static final int SIGN_SHIFT = 15; // 0xf
+    field public static final int SIZE = 16; // 0x10
+  }
+
+  public class HexEncoding {
+    method public static byte[] decode(String) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(String, boolean) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(char[]) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(char[], boolean) throws java.lang.IllegalArgumentException;
+    method public static char[] encode(byte[]);
+    method public static char[] encode(byte[], boolean);
+    method public static char[] encode(byte[], int, int);
+    method public static String encodeToString(byte, boolean);
+    method public static String encodeToString(byte[]);
+    method public static String encodeToString(byte[], boolean);
+  }
+
+  public class NativeAllocationRegistry {
+    ctor public NativeAllocationRegistry(@NonNull ClassLoader, long, long);
+    method public static void applyFreeFunction(long, long);
+    method public static libcore.util.NativeAllocationRegistry createMalloced(@NonNull ClassLoader, long, long);
+    method public static libcore.util.NativeAllocationRegistry createMalloced(@NonNull ClassLoader, long);
+    method public static libcore.util.NativeAllocationRegistry createNonmalloced(@NonNull ClassLoader, long, long);
+    method @NonNull public Runnable registerNativeAllocation(@NonNull Object, long);
+  }
+
+  public class SneakyThrow {
+    method public static void sneakyThrow(@NonNull Throwable);
+  }
+
+  public class XmlObjectFactory {
+    method @NonNull public static org.xml.sax.XMLReader newXMLReader();
+    method @NonNull public static org.xmlpull.v1.XmlPullParser newXmlPullParser();
+    method @NonNull public static org.xmlpull.v1.XmlSerializer newXmlSerializer();
+  }
+
+}
+
+package org.apache.harmony.dalvik.ddmc {
+
+  public class Chunk {
+    ctor public Chunk(int, byte[], int, int);
+    ctor public Chunk(int, java.nio.ByteBuffer);
+    field public int type;
+  }
+
+  public abstract class ChunkHandler {
+    ctor public ChunkHandler();
+    method public static org.apache.harmony.dalvik.ddmc.Chunk createFailChunk(int, String);
+    method public abstract org.apache.harmony.dalvik.ddmc.Chunk handleChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    method public static String name(int);
+    method public abstract void onConnected();
+    method public abstract void onDisconnected();
+    method public static int type(String);
+    method public static java.nio.ByteBuffer wrapChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    field public static final java.nio.ByteOrder CHUNK_ORDER;
+  }
+
+  public final class DdmServer {
+    method public static void registerHandler(int, org.apache.harmony.dalvik.ddmc.ChunkHandler);
+    method public static void registrationComplete();
+    method public static void sendChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    method public static org.apache.harmony.dalvik.ddmc.ChunkHandler unregisterHandler(int);
+  }
+
+  public final class DdmVmInternal {
+    method public static void setRecentAllocationsTrackingEnabled(boolean);
+    method public static void setThreadNotifyEnabled(boolean);
+  }
+
+}
+
+package org.json {
+
+  public class JSONObject {
+    method @NonNull public java.util.Set<java.lang.String> keySet();
+  }
+
+}
+
+package sun.misc {
+
+  public class Cleaner extends java.lang.ref.PhantomReference<java.lang.Object> {
+    method public void clean();
+    method public static sun.misc.Cleaner create(Object, Runnable);
+  }
+
+  public final class Unsafe {
+    method public int arrayBaseOffset(Class);
+    method public int arrayIndexScale(Class);
+    method @dalvik.annotation.optimization.FastNative public void copyMemory(long, long, long);
+    method @dalvik.annotation.optimization.FastNative public boolean getBoolean(Object, long);
+    method @dalvik.annotation.optimization.FastNative public byte getByte(Object, long);
+    method @dalvik.annotation.optimization.FastNative public byte getByte(long);
+    method @dalvik.annotation.optimization.FastNative public double getDouble(Object, long);
+    method @dalvik.annotation.optimization.FastNative public double getDouble(long);
+    method @dalvik.annotation.optimization.FastNative public float getFloat(Object, long);
+    method @dalvik.annotation.optimization.FastNative public float getFloat(long);
+    method @dalvik.annotation.optimization.FastNative public int getInt(Object, long);
+    method @dalvik.annotation.optimization.FastNative public int getInt(long);
+    method @dalvik.annotation.optimization.FastNative public long getLong(Object, long);
+    method @dalvik.annotation.optimization.FastNative public long getLong(long);
+    method @dalvik.annotation.optimization.FastNative public Object getObject(Object, long);
+    method public static sun.misc.Unsafe getUnsafe();
+    method public long objectFieldOffset(java.lang.reflect.Field);
+    method @dalvik.annotation.optimization.FastNative public void putBoolean(Object, long, boolean);
+    method @dalvik.annotation.optimization.FastNative public void putByte(Object, long, byte);
+    method @dalvik.annotation.optimization.FastNative public void putByte(long, byte);
+    method @dalvik.annotation.optimization.FastNative public void putDouble(Object, long, double);
+    method @dalvik.annotation.optimization.FastNative public void putDouble(long, double);
+    method @dalvik.annotation.optimization.FastNative public void putFloat(Object, long, float);
+    method @dalvik.annotation.optimization.FastNative public void putFloat(long, float);
+    method @dalvik.annotation.optimization.FastNative public void putInt(Object, long, int);
+    method @dalvik.annotation.optimization.FastNative public void putInt(long, int);
+    method @dalvik.annotation.optimization.FastNative public void putLong(Object, long, long);
+    method @dalvik.annotation.optimization.FastNative public void putLong(long, long);
+    method @dalvik.annotation.optimization.FastNative public void putObject(Object, long, Object);
+  }
+
+}
+
+package sun.security.jca {
+
+  public class Providers {
+    method public static Object startJarVerification();
+    method public static void stopJarVerification(Object);
+  }
+
+}
+
+package sun.security.pkcs {
+
+  public class PKCS7 {
+    ctor public PKCS7(java.io.InputStream) throws java.io.IOException, sun.security.pkcs.ParsingException;
+    ctor public PKCS7(byte[]) throws sun.security.pkcs.ParsingException;
+    method public java.security.cert.X509Certificate[] getCertificates();
+    method public sun.security.pkcs.SignerInfo[] getSignerInfos();
+    method public sun.security.pkcs.SignerInfo verify(sun.security.pkcs.SignerInfo, java.io.InputStream) throws java.io.IOException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
+    method public sun.security.pkcs.SignerInfo[] verify(byte[]) throws java.security.NoSuchAlgorithmException, java.security.SignatureException;
+  }
+
+  public class ParsingException extends java.io.IOException {
+  }
+
+  public class SignerInfo {
+    ctor public SignerInfo();
+    method public java.util.ArrayList<java.security.cert.X509Certificate> getCertificateChain(sun.security.pkcs.PKCS7) throws java.io.IOException;
+  }
+
+}
+
+package sun.security.util {
+
+  public final class ObjectIdentifier implements java.io.Serializable {
+    ctor public ObjectIdentifier(String) throws java.io.IOException;
+  }
+
+}
+
+package sun.security.x509 {
+
+  public class AlgorithmId implements java.io.Serializable {
+    ctor public AlgorithmId(sun.security.util.ObjectIdentifier);
+    method public String getName();
+  }
+
+}
+
diff --git a/mmodules/core_platform_api/api/platform/last-api.txt b/mmodules/core_platform_api/api/legacy_platform/last-api.txt
similarity index 100%
rename from mmodules/core_platform_api/api/platform/last-api.txt
rename to mmodules/core_platform_api/api/legacy_platform/last-api.txt
diff --git a/mmodules/core_platform_api/api/platform/last-api.txt b/mmodules/core_platform_api/api/legacy_platform/last-incompatibilities.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/last-api.txt
copy to mmodules/core_platform_api/api/legacy_platform/last-incompatibilities.txt
diff --git a/mmodules/core_platform_api/api/platform/last-removed.txt b/mmodules/core_platform_api/api/legacy_platform/last-removed.txt
similarity index 100%
rename from mmodules/core_platform_api/api/platform/last-removed.txt
rename to mmodules/core_platform_api/api/legacy_platform/last-removed.txt
diff --git a/mmodules/core_platform_api/api/platform/current-removed.txt b/mmodules/core_platform_api/api/legacy_platform/removed.txt
similarity index 100%
rename from mmodules/core_platform_api/api/platform/current-removed.txt
rename to mmodules/core_platform_api/api/legacy_platform/removed.txt
diff --git a/mmodules/core_platform_api/api/platform/current-api.txt b/mmodules/core_platform_api/api/platform/current-api.txt
deleted file mode 100644
index 15a0a48..0000000
--- a/mmodules/core_platform_api/api/platform/current-api.txt
+++ /dev/null
@@ -1,1510 +0,0 @@
-// Signature format: 2.0
-package android.compat {
-
-  public final class Compatibility {
-    method public static void clearOverrides();
-    method public static boolean isChangeEnabled(long);
-    method public static void reportChange(long);
-    method public static void setCallbacks(android.compat.Compatibility.Callbacks);
-    method public static void setOverrides(android.compat.Compatibility.ChangeConfig);
-  }
-
-  public static class Compatibility.Callbacks {
-    ctor protected Compatibility.Callbacks();
-    method protected boolean isChangeEnabled(long);
-    method protected void reportChange(long);
-  }
-
-  public static final class Compatibility.ChangeConfig {
-    ctor public Compatibility.ChangeConfig(java.util.Set<java.lang.Long>, java.util.Set<java.lang.Long>);
-    method public long[] forceDisabledChangesArray();
-    method public java.util.Set<java.lang.Long> forceDisabledSet();
-    method public long[] forceEnabledChangesArray();
-    method public java.util.Set<java.lang.Long> forceEnabledSet();
-    method public boolean isEmpty();
-    method public boolean isForceDisabled(long);
-    method public boolean isForceEnabled(long);
-  }
-
-}
-
-package android.system {
-
-  public class Int32Ref {
-    ctor public Int32Ref(int);
-    field public int value;
-  }
-
-  public final class NetlinkSocketAddress extends java.net.SocketAddress {
-    ctor public NetlinkSocketAddress(int, int);
-    method public int getGroupsMask();
-    method public int getPortId();
-  }
-
-  public final class Os {
-    method public static android.system.StructCapUserData[] capget(android.system.StructCapUserHeader) throws android.system.ErrnoException;
-    method public static void capset(android.system.StructCapUserHeader, android.system.StructCapUserData[]) throws android.system.ErrnoException;
-    method public static int getpgid(int) throws android.system.ErrnoException;
-    method public static android.system.StructRlimit getrlimit(int) throws android.system.ErrnoException;
-    method public static int getsockoptInt(java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
-    method public static android.system.StructLinger getsockoptLinger(java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
-    method public static int ioctlInt(java.io.FileDescriptor, int, android.system.Int32Ref) throws android.system.ErrnoException;
-    method public static java.io.FileDescriptor[] pipe2(int) throws android.system.ErrnoException;
-    method public static String realpath(String) throws android.system.ErrnoException;
-    method public static void setpgid(int, int) throws android.system.ErrnoException;
-    method public static void setregid(int, int) throws android.system.ErrnoException;
-    method public static void setreuid(int, int) throws android.system.ErrnoException;
-    method public static void setsockoptIfreq(java.io.FileDescriptor, int, int, String) throws android.system.ErrnoException;
-    method public static void setsockoptLinger(java.io.FileDescriptor, int, int, android.system.StructLinger) throws android.system.ErrnoException;
-    method public static long splice(java.io.FileDescriptor, android.system.Int64Ref, java.io.FileDescriptor, android.system.Int64Ref, long, int) throws android.system.ErrnoException;
-    method public static void unlink(String) throws android.system.ErrnoException;
-  }
-
-  public final class OsConstants {
-    method public static int CAP_TO_INDEX(int);
-    method public static int CAP_TO_MASK(int);
-    field public static final int ARPHRD_LOOPBACK;
-    field public static final int ENONET;
-    field public static final int EUSERS;
-    field public static final int MAP_POPULATE;
-    field public static final int O_DIRECT;
-    field public static final int PR_CAP_AMBIENT;
-    field public static final int PR_CAP_AMBIENT_RAISE;
-    field public static final int RLIMIT_NOFILE;
-    field public static final int RTMGRP_IPV4_IFADDR;
-    field public static final int SPLICE_F_MORE;
-    field public static final int SPLICE_F_MOVE;
-    field public static final int TIOCOUTQ;
-    field public static final int UDP_ENCAP;
-    field public static final int UDP_ENCAP_ESPINUDP;
-    field public static final int XATTR_CREATE;
-    field public static final int _LINUX_CAPABILITY_VERSION_3;
-  }
-
-  public final class PacketSocketAddress extends java.net.SocketAddress {
-    ctor public PacketSocketAddress(int, int, byte[]);
-  }
-
-  public final class StructCapUserData {
-    ctor public StructCapUserData(int, int, int);
-    field public final int effective;
-    field public final int inheritable;
-    field public final int permitted;
-  }
-
-  public final class StructCapUserHeader {
-    ctor public StructCapUserHeader(int, int);
-  }
-
-  public final class StructLinger {
-    ctor public StructLinger(int, int);
-    method public boolean isOn();
-    field public final int l_linger;
-  }
-
-  public final class StructRlimit {
-    field public final long rlim_cur;
-  }
-
-  public final class UnixSocketAddress extends java.net.SocketAddress {
-    method public static android.system.UnixSocketAddress createFileSystem(String);
-  }
-
-}
-
-package com.android.okhttp.internalandroidapi {
-
-  public final class AndroidResponseCacheAdapter {
-    ctor public AndroidResponseCacheAdapter(com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder);
-    method public void close() throws java.io.IOException;
-    method public void delete() throws java.io.IOException;
-    method public void flush() throws java.io.IOException;
-    method public java.net.CacheResponse get(java.net.URI, String, java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
-    method public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
-    method public int getHitCount();
-    method public long getMaxSize();
-    method public int getNetworkCount();
-    method public int getRequestCount();
-    method public long getSize() throws java.io.IOException;
-    method public java.net.CacheRequest put(java.net.URI, java.net.URLConnection) throws java.io.IOException;
-  }
-
-  public interface Dns {
-    method public java.util.List<java.net.InetAddress> lookup(String) throws java.net.UnknownHostException;
-  }
-
-  public interface HasCacheHolder {
-    method public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
-  }
-
-  public static final class HasCacheHolder.CacheHolder {
-    method public static com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder create(java.io.File, long);
-    method public boolean isEquivalent(java.io.File, long);
-  }
-
-  public final class HttpURLConnectionFactory {
-    ctor public HttpURLConnectionFactory();
-    method public java.net.URLConnection openConnection(java.net.URL, javax.net.SocketFactory, java.net.Proxy) throws java.io.IOException;
-    method public void setDns(com.android.okhttp.internalandroidapi.Dns);
-    method public void setNewConnectionPool(int, long, java.util.concurrent.TimeUnit);
-  }
-
-}
-
-package com.android.org.bouncycastle.asn1 {
-
-  public abstract class ASN1BitString extends com.android.org.bouncycastle.asn1.ASN1Primitive {
-  }
-
-  public interface ASN1Encodable {
-  }
-
-  public class ASN1EncodableVector {
-    ctor public ASN1EncodableVector();
-    method public void add(com.android.org.bouncycastle.asn1.ASN1Encodable);
-  }
-
-  public class ASN1InputStream extends java.io.FilterInputStream {
-    ctor public ASN1InputStream(java.io.InputStream);
-    ctor public ASN1InputStream(byte[]);
-    method public com.android.org.bouncycastle.asn1.ASN1Primitive readObject() throws java.io.IOException;
-  }
-
-  public class ASN1Integer extends com.android.org.bouncycastle.asn1.ASN1Primitive {
-    ctor public ASN1Integer(java.math.BigInteger);
-  }
-
-  public abstract class ASN1Null extends com.android.org.bouncycastle.asn1.ASN1Primitive {
-  }
-
-  public abstract class ASN1Object implements com.android.org.bouncycastle.asn1.ASN1Encodable {
-    ctor public ASN1Object();
-    method public byte[] getEncoded() throws java.io.IOException;
-    method public byte[] getEncoded(String) throws java.io.IOException;
-  }
-
-  public class ASN1ObjectIdentifier extends com.android.org.bouncycastle.asn1.ASN1Primitive {
-    ctor public ASN1ObjectIdentifier(String);
-    method public String getId();
-    method public static com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier getInstance(Object);
-  }
-
-  public abstract class ASN1OctetString extends com.android.org.bouncycastle.asn1.ASN1Primitive implements com.android.org.bouncycastle.asn1.ASN1Encodable {
-    method public byte[] getOctets();
-  }
-
-  public abstract class ASN1Primitive extends com.android.org.bouncycastle.asn1.ASN1Object {
-    method public static com.android.org.bouncycastle.asn1.ASN1Primitive fromByteArray(byte[]) throws java.io.IOException;
-    method public com.android.org.bouncycastle.asn1.ASN1Primitive toASN1Primitive();
-  }
-
-  public abstract class ASN1Sequence extends com.android.org.bouncycastle.asn1.ASN1Primitive implements com.android.org.bouncycastle.util.Iterable<com.android.org.bouncycastle.asn1.ASN1Encodable> {
-    method public com.android.org.bouncycastle.asn1.ASN1Encodable getObjectAt(int);
-    method public int size();
-  }
-
-  public abstract class ASN1TaggedObject extends com.android.org.bouncycastle.asn1.ASN1Primitive implements com.android.org.bouncycastle.asn1.ASN1Encodable {
-    method public com.android.org.bouncycastle.asn1.ASN1Primitive getObject();
-  }
-
-  public class DERBitString extends com.android.org.bouncycastle.asn1.ASN1BitString {
-    ctor public DERBitString(byte[]);
-  }
-
-  @Deprecated public class DERInteger extends com.android.org.bouncycastle.asn1.ASN1Integer {
-    ctor @Deprecated public DERInteger(long);
-  }
-
-  public class DERNull extends com.android.org.bouncycastle.asn1.ASN1Null {
-    field public static final com.android.org.bouncycastle.asn1.DERNull INSTANCE;
-  }
-
-  public class DEROctetString extends com.android.org.bouncycastle.asn1.ASN1OctetString {
-    ctor public DEROctetString(byte[]);
-  }
-
-  public class DERSequence extends com.android.org.bouncycastle.asn1.ASN1Sequence {
-    ctor public DERSequence();
-    ctor public DERSequence(com.android.org.bouncycastle.asn1.ASN1EncodableVector);
-  }
-
-  public class DERTaggedObject extends com.android.org.bouncycastle.asn1.ASN1TaggedObject {
-    ctor public DERTaggedObject(int, com.android.org.bouncycastle.asn1.ASN1Encodable);
-  }
-
-  public class DERUTF8String extends com.android.org.bouncycastle.asn1.ASN1Primitive {
-    ctor public DERUTF8String(String);
-    method public String getString();
-  }
-
-}
-
-package com.android.org.bouncycastle.asn1.pkcs {
-
-  public interface PKCSObjectIdentifiers {
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier sha256WithRSAEncryption;
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier sha512WithRSAEncryption;
-  }
-
-  public class PrivateKeyInfo extends com.android.org.bouncycastle.asn1.ASN1Object {
-    method public static com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo getInstance(Object);
-    method public com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier getPrivateKeyAlgorithm();
-    method public com.android.org.bouncycastle.asn1.ASN1Encodable parsePrivateKey() throws java.io.IOException;
-  }
-
-}
-
-package com.android.org.bouncycastle.asn1.x500.style {
-
-  public class BCStyle {
-    method public static java.util.Hashtable copyHashTable(java.util.Hashtable);
-    method public com.android.org.bouncycastle.asn1.ASN1Encodable stringToValue(com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier, String);
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier C;
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier CN;
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier E;
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier L;
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier O;
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier OU;
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier ST;
-  }
-
-}
-
-package com.android.org.bouncycastle.asn1.x509 {
-
-  public class AlgorithmIdentifier extends com.android.org.bouncycastle.asn1.ASN1Object {
-    ctor public AlgorithmIdentifier(com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier);
-    ctor public AlgorithmIdentifier(com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier, com.android.org.bouncycastle.asn1.ASN1Encodable);
-    method public com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier getAlgorithm();
-  }
-
-  public class BasicConstraints extends com.android.org.bouncycastle.asn1.ASN1Object {
-    method public static com.android.org.bouncycastle.asn1.x509.BasicConstraints getInstance(Object);
-    method public boolean isCA();
-  }
-
-  public class Certificate extends com.android.org.bouncycastle.asn1.ASN1Object {
-    method public static com.android.org.bouncycastle.asn1.x509.Certificate getInstance(Object);
-  }
-
-  public class GeneralName extends com.android.org.bouncycastle.asn1.ASN1Object {
-    ctor public GeneralName(int, com.android.org.bouncycastle.asn1.ASN1Encodable);
-    ctor public GeneralName(int, String);
-    method public int getTagNo();
-    field public static final int dNSName = 2; // 0x2
-    field public static final int iPAddress = 7; // 0x7
-    field public static final int otherName = 0; // 0x0
-  }
-
-  public class SubjectPublicKeyInfo extends com.android.org.bouncycastle.asn1.ASN1Object {
-    method public static com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo getInstance(Object);
-  }
-
-  public class TBSCertificate extends com.android.org.bouncycastle.asn1.ASN1Object {
-  }
-
-  public class Time extends com.android.org.bouncycastle.asn1.ASN1Object {
-    ctor public Time(java.util.Date);
-  }
-
-  public class V3TBSCertificateGenerator {
-    ctor public V3TBSCertificateGenerator();
-    method public com.android.org.bouncycastle.asn1.x509.TBSCertificate generateTBSCertificate();
-    method public void setEndDate(com.android.org.bouncycastle.asn1.x509.Time);
-    method @Deprecated public void setIssuer(com.android.org.bouncycastle.asn1.x509.X509Name);
-    method public void setSerialNumber(com.android.org.bouncycastle.asn1.ASN1Integer);
-    method public void setSignature(com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier);
-    method public void setStartDate(com.android.org.bouncycastle.asn1.x509.Time);
-    method @Deprecated public void setSubject(com.android.org.bouncycastle.asn1.x509.X509Name);
-    method public void setSubjectPublicKeyInfo(com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo);
-  }
-
-  @Deprecated public class X509Name extends com.android.org.bouncycastle.asn1.ASN1Object {
-    ctor @Deprecated public X509Name(String);
-    method @Deprecated public static com.android.org.bouncycastle.asn1.x509.X509Name getInstance(Object);
-    method @Deprecated public java.util.Vector getOIDs();
-    method @Deprecated public java.util.Vector getValues();
-    method @Deprecated public String toString(boolean, java.util.Hashtable);
-    field @Deprecated public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier CN;
-    field @Deprecated public static final java.util.Hashtable DefaultSymbols;
-    field @Deprecated public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier O;
-    field @Deprecated public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier OU;
-  }
-
-}
-
-package com.android.org.bouncycastle.asn1.x9 {
-
-  public interface X9ObjectIdentifiers {
-    field public static final com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier ecdsa_with_SHA256;
-  }
-
-}
-
-package com.android.org.bouncycastle.crypto {
-
-  public interface CipherParameters {
-  }
-
-  public abstract class PBEParametersGenerator {
-    ctor protected PBEParametersGenerator();
-    method public static byte[] PKCS5PasswordToBytes(char[]);
-  }
-
-}
-
-package com.android.org.bouncycastle.crypto.digests {
-
-  public abstract class GeneralDigest {
-    method public void finish();
-    method protected abstract void processBlock();
-    method public void update(byte[], int, int);
-  }
-
-  public class MD4Digest extends com.android.org.bouncycastle.crypto.digests.GeneralDigest {
-    ctor public MD4Digest();
-    method public int doFinal(byte[], int);
-    method protected void processBlock();
-  }
-
-  public class SHA1Digest extends com.android.org.bouncycastle.crypto.digests.GeneralDigest {
-    ctor public SHA1Digest();
-    method public int doFinal(byte[], int);
-    method protected void processBlock();
-  }
-
-}
-
-package com.android.org.bouncycastle.crypto.generators {
-
-  public class OpenSSLPBEParametersGenerator extends com.android.org.bouncycastle.crypto.PBEParametersGenerator {
-    ctor public OpenSSLPBEParametersGenerator();
-    method public com.android.org.bouncycastle.crypto.CipherParameters generateDerivedParameters(int);
-    method public void init(byte[], byte[]);
-  }
-
-}
-
-package com.android.org.bouncycastle.crypto.params {
-
-  public class KeyParameter implements com.android.org.bouncycastle.crypto.CipherParameters {
-    method public byte[] getKey();
-  }
-
-}
-
-package com.android.org.bouncycastle.jcajce.util {
-
-  public class DefaultJcaJceHelper {
-    method public javax.crypto.Cipher createCipher(String) throws java.security.NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException;
-  }
-
-}
-
-package com.android.org.bouncycastle.jce {
-
-  @Deprecated public class X509Principal extends com.android.org.bouncycastle.asn1.x509.X509Name implements java.security.Principal {
-    ctor @Deprecated public X509Principal(byte[]) throws java.io.IOException;
-    ctor @Deprecated public X509Principal(java.util.Vector, java.util.Hashtable);
-    method public byte[] getEncoded();
-  }
-
-}
-
-package com.android.org.bouncycastle.jce.provider {
-
-  public final class BouncyCastleProvider extends java.security.Provider {
-    ctor public BouncyCastleProvider();
-  }
-
-  @Deprecated public class X509CertificateObject extends java.security.cert.X509Certificate {
-    ctor @Deprecated public X509CertificateObject(com.android.org.bouncycastle.asn1.x509.Certificate) throws java.security.cert.CertificateParsingException;
-  }
-
-}
-
-package com.android.org.bouncycastle.util {
-
-  public interface Iterable<T> extends java.lang.Iterable<T> {
-  }
-
-}
-
-package com.android.org.bouncycastle.util.io.pem {
-
-  public class PemHeader {
-    ctor public PemHeader(String, String);
-  }
-
-  public class PemObject implements com.android.org.bouncycastle.util.io.pem.PemObjectGenerator {
-    ctor public PemObject(String, byte[]);
-    ctor public PemObject(String, java.util.List, byte[]);
-    method public byte[] getContent();
-    method public String getType();
-  }
-
-  public interface PemObjectGenerator {
-  }
-
-  public class PemReader extends java.io.BufferedReader {
-    ctor public PemReader(java.io.Reader);
-    method public com.android.org.bouncycastle.util.io.pem.PemObject readPemObject() throws java.io.IOException;
-  }
-
-  public class PemWriter extends java.io.BufferedWriter {
-    ctor public PemWriter(java.io.Writer);
-    method public void writeObject(com.android.org.bouncycastle.util.io.pem.PemObjectGenerator) throws java.io.IOException;
-  }
-
-}
-
-package com.android.org.bouncycastle.x509 {
-
-  @Deprecated public class X509V1CertificateGenerator {
-    ctor @Deprecated public X509V1CertificateGenerator();
-    method @Deprecated public java.security.cert.X509Certificate generate(java.security.PrivateKey, String) throws java.security.cert.CertificateEncodingException, java.lang.IllegalStateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException, java.security.SignatureException;
-    method @Deprecated public void setIssuerDN(javax.security.auth.x500.X500Principal);
-    method @Deprecated public void setNotAfter(java.util.Date);
-    method @Deprecated public void setNotBefore(java.util.Date);
-    method @Deprecated public void setPublicKey(java.security.PublicKey);
-    method @Deprecated public void setSerialNumber(java.math.BigInteger);
-    method @Deprecated public void setSignatureAlgorithm(String);
-    method @Deprecated public void setSubjectDN(javax.security.auth.x500.X500Principal);
-  }
-
-  @Deprecated public class X509V3CertificateGenerator {
-    ctor @Deprecated public X509V3CertificateGenerator();
-    method @Deprecated public java.security.cert.X509Certificate generate(java.security.PrivateKey) throws java.security.cert.CertificateEncodingException, java.lang.IllegalStateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
-    method @Deprecated public void setIssuerDN(javax.security.auth.x500.X500Principal);
-    method @Deprecated public void setNotAfter(java.util.Date);
-    method @Deprecated public void setNotBefore(java.util.Date);
-    method @Deprecated public void setPublicKey(java.security.PublicKey) throws java.lang.IllegalArgumentException;
-    method @Deprecated public void setSerialNumber(java.math.BigInteger);
-    method @Deprecated public void setSignatureAlgorithm(String);
-    method @Deprecated public void setSubjectDN(javax.security.auth.x500.X500Principal);
-  }
-
-}
-
-package dalvik.annotation.codegen {
-
-  @java.lang.annotation.Repeatable(CovariantReturnType.CovariantReturnTypes.class) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CovariantReturnType {
-    method public abstract int presentAfter();
-    method public abstract Class<?> returnType();
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public static @interface CovariantReturnType.CovariantReturnTypes {
-    method public abstract dalvik.annotation.codegen.CovariantReturnType[] value();
-  }
-
-}
-
-package dalvik.annotation.compat {
-
-  public class VersionCodes {
-    field public static final int O = 26; // 0x1a
-    field public static final int P = 28; // 0x1c
-    field public static final int Q = 29; // 0x1d
-  }
-
-}
-
-package dalvik.annotation.optimization {
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface CriticalNative {
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface FastNative {
-  }
-
-}
-
-package dalvik.system {
-
-  public class AnnotatedStackTraceElement {
-    method public Object getBlockedOn();
-    method public Object[] getHeldLocks();
-    method public StackTraceElement getStackTraceElement();
-  }
-
-  public class BaseDexClassLoader extends java.lang.ClassLoader {
-    method public void addDexPath(String);
-    method public void addNativePath(java.util.Collection<java.lang.String>);
-    method public String getLdLibraryPath();
-    method public void reportClassLoaderChain();
-    method public static void setReporter(dalvik.system.BaseDexClassLoader.Reporter);
-  }
-
-  public static interface BaseDexClassLoader.Reporter {
-    method public void report(java.util.Map<java.lang.String,java.lang.String>);
-  }
-
-  public final class BlockGuard {
-    method @NonNull public static dalvik.system.BlockGuard.Policy getThreadPolicy();
-    method @NonNull public static dalvik.system.BlockGuard.VmPolicy getVmPolicy();
-    method public static void setThreadPolicy(@NonNull dalvik.system.BlockGuard.Policy);
-    method public static void setVmPolicy(@NonNull dalvik.system.BlockGuard.VmPolicy);
-    field public static final dalvik.system.BlockGuard.Policy LAX_POLICY;
-    field public static final dalvik.system.BlockGuard.VmPolicy LAX_VM_POLICY;
-  }
-
-  public static interface BlockGuard.Policy {
-    method public int getPolicyMask();
-    method public void onReadFromDisk();
-    method public void onUnbufferedIO();
-    method public void onWriteToDisk();
-  }
-
-  public static interface BlockGuard.VmPolicy {
-    method public void onPathAccess(String);
-  }
-
-  public final class CloseGuard {
-    method public void close();
-    method public static dalvik.system.CloseGuard get();
-    method public static dalvik.system.CloseGuard.Reporter getReporter();
-    method public void open(String);
-    method public void openWithCallSite(String, String);
-    method public static void setEnabled(boolean);
-    method public static void setReporter(dalvik.system.CloseGuard.Reporter);
-    method public void warnIfOpen();
-  }
-
-  public static interface CloseGuard.Reporter {
-    method public void report(String, Throwable);
-    method public default void report(String);
-  }
-
-  public interface DalvikLogHandler {
-    method public void publish(java.util.logging.Logger, String, java.util.logging.Level, String);
-  }
-
-  public final class DalvikLogging {
-    method public static String loggerNameToTag(String);
-  }
-
-  public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
-    ctor public DelegateLastClassLoader(String, String, ClassLoader, ClassLoader[]);
-  }
-
-  @Deprecated public final class DexFile {
-    ctor @Deprecated public DexFile(java.io.File) throws java.io.IOException;
-    ctor @Deprecated public DexFile(String) throws java.io.IOException;
-    method @Deprecated public void close() throws java.io.IOException;
-    method @Deprecated public java.util.Enumeration<java.lang.String> entries();
-    method @Deprecated public static dalvik.system.DexFile.OptimizationInfo getDexFileOptimizationInfo(String, String) throws java.io.FileNotFoundException;
-    method @Deprecated public static String[] getDexFileOutputPaths(String, String) throws java.io.FileNotFoundException;
-    method @Deprecated public static int getDexOptNeeded(String, String, String, String, boolean, boolean) throws java.io.FileNotFoundException, java.io.IOException;
-    method @Deprecated public String getName();
-    method @Deprecated public static String getSafeModeCompilerFilter(String);
-    method @Deprecated public static boolean isDexOptNeeded(String) throws java.io.FileNotFoundException, java.io.IOException;
-    method @Deprecated public static boolean isProfileGuidedCompilerFilter(String);
-    method @Deprecated public static boolean isValidCompilerFilter(String);
-    method @Deprecated public Class loadClass(String, ClassLoader);
-    method @Deprecated public static dalvik.system.DexFile loadDex(String, String, int) throws java.io.IOException;
-    field @Deprecated public static final int DEX2OAT_FOR_FILTER = 3; // 0x3
-    field @Deprecated public static final int NO_DEXOPT_NEEDED = 0; // 0x0
-  }
-
-  @Deprecated public static final class DexFile.OptimizationInfo {
-    method @Deprecated public String getReason();
-    method @Deprecated public String getStatus();
-  }
-
-  public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
-    ctor public PathClassLoader(String, String, ClassLoader, ClassLoader[]);
-  }
-
-  public final class RuntimeHooks {
-    method @Nullable public static dalvik.system.ThreadPrioritySetter getThreadPrioritySetter();
-    method public static void setThreadPrioritySetter(@NonNull dalvik.system.ThreadPrioritySetter);
-    method public static void setTimeZoneIdSupplier(java.util.function.Supplier<java.lang.String>);
-    method public static void setUncaughtExceptionPreHandler(java.lang.Thread.UncaughtExceptionHandler);
-  }
-
-  public abstract class SocketTagger {
-    ctor public SocketTagger();
-    method public static dalvik.system.SocketTagger get();
-    method public static void set(dalvik.system.SocketTagger);
-    method public abstract void tag(java.io.FileDescriptor) throws java.net.SocketException;
-    method public final void tag(java.net.Socket) throws java.net.SocketException;
-    method public final void tag(java.net.DatagramSocket) throws java.net.SocketException;
-    method public abstract void untag(java.io.FileDescriptor) throws java.net.SocketException;
-    method public final void untag(java.net.Socket) throws java.net.SocketException;
-    method public final void untag(java.net.DatagramSocket) throws java.net.SocketException;
-  }
-
-  @java.lang.FunctionalInterface public interface ThreadPrioritySetter {
-    method public void setPriority(int, int);
-  }
-
-  public final class VMDebug {
-    method public static void attachAgent(String, ClassLoader) throws java.io.IOException;
-    method public static boolean cacheRegisterMap(String);
-    method public static long countInstancesOfClass(Class, boolean);
-    method public static long[] countInstancesOfClasses(Class[], boolean);
-    method public static void dumpHprofData(String) throws java.io.IOException;
-    method public static void dumpHprofData(String, java.io.FileDescriptor) throws java.io.IOException;
-    method public static void dumpHprofDataDdms();
-    method public static void dumpReferenceTables();
-    method public static int getAllocCount(int);
-    method @dalvik.annotation.optimization.FastNative public static int getLoadedClassCount();
-    method public static int getMethodTracingMode();
-    method public static String getRuntimeStat(String);
-    method public static java.util.Map<java.lang.String,java.lang.String> getRuntimeStats();
-    method public static String[] getVmFeatureList();
-    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggerConnected();
-    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggingEnabled();
-    method @dalvik.annotation.optimization.FastNative public static long lastDebuggerActivity();
-    method @dalvik.annotation.optimization.FastNative public static void printLoadedClasses(int);
-    method public static void resetAllocCount(int);
-    method public static void setAllocTrackerStackDepth(int);
-    method public static void startAllocCounting();
-    method public static void startEmulatorTracing();
-    method public static void startMethodTracing(String, int, int, boolean, int);
-    method public static void startMethodTracing(String, java.io.FileDescriptor, int, int, boolean, int, boolean);
-    method public static void startMethodTracingDdms(int, int, boolean, int);
-    method public static void stopAllocCounting();
-    method public static void stopEmulatorTracing();
-    method public static void stopMethodTracing();
-    method @dalvik.annotation.optimization.FastNative public static long threadCpuTimeNanos();
-    field public static final int KIND_ALL_COUNTS = -1; // 0xffffffff
-    field public static final int KIND_GLOBAL_ALLOCATED_BYTES = 2; // 0x2
-    field public static final int KIND_GLOBAL_ALLOCATED_OBJECTS = 1; // 0x1
-    field public static final int KIND_GLOBAL_CLASS_INIT_COUNT = 32; // 0x20
-    field public static final int KIND_GLOBAL_CLASS_INIT_TIME = 64; // 0x40
-    field public static final int KIND_GLOBAL_FREED_BYTES = 8; // 0x8
-    field public static final int KIND_GLOBAL_FREED_OBJECTS = 4; // 0x4
-    field public static final int KIND_GLOBAL_GC_INVOCATIONS = 16; // 0x10
-    field public static final int KIND_THREAD_ALLOCATED_BYTES = 131072; // 0x20000
-    field public static final int KIND_THREAD_ALLOCATED_OBJECTS = 65536; // 0x10000
-    field public static final int KIND_THREAD_GC_INVOCATIONS = 1048576; // 0x100000
-    field public static final int TRACE_COUNT_ALLOCS = 1; // 0x1
-  }
-
-  public final class VMRuntime {
-    method @dalvik.annotation.optimization.FastNative public long addressOf(Object);
-    method public static void bootCompleted();
-    method public void clampGrowthLimit();
-    method public void clearGrowthLimit();
-    method public static boolean didPruneDalvikCache();
-    method public void disableJitCompilation();
-    method public static String getCurrentInstructionSet();
-    method public static String getInstructionSet(String);
-    method public static dalvik.system.VMRuntime getRuntime();
-    method public float getTargetHeapUtilization();
-    method public int getTargetSdkVersion();
-    method @dalvik.annotation.optimization.FastNative public static boolean hasBootImageSpaces();
-    method @dalvik.annotation.optimization.FastNative public boolean is64Bit();
-    method public static boolean is64BitAbi(String);
-    method public static boolean is64BitInstructionSet(String);
-    method public static boolean isBootClassPathOnDisk(String);
-    method @dalvik.annotation.optimization.FastNative public boolean isCheckJniEnabled();
-    method @dalvik.annotation.optimization.FastNative public boolean isNativeDebuggable();
-    method public static boolean isValidClassLoaderContext(String);
-    method @dalvik.annotation.optimization.FastNative public Object newNonMovableArray(Class<?>, int);
-    method @dalvik.annotation.optimization.FastNative public Object newUnpaddedArray(Class<?>, int);
-    method public void notifyStartupCompleted();
-    method public void preloadDexCaches();
-    method public static void registerAppInfo(String, String[]);
-    method public void registerNativeAllocation(long);
-    method @Deprecated public void registerNativeAllocation(int);
-    method public void registerNativeFree(long);
-    method @Deprecated public void registerNativeFree(int);
-    method public static void registerSensitiveThread();
-    method public void requestConcurrentGC();
-    method public static void resetJitCounters();
-    method public static void setDedupeHiddenApiWarnings(boolean);
-    method public void setDisabledCompatChanges(long[]);
-    method public void setHiddenApiAccessLogSamplingRate(int);
-    method public void setHiddenApiExemptions(String[]);
-    method public static void setHiddenApiUsageLogger(dalvik.system.VMRuntime.HiddenApiUsageLogger);
-    method public static void setNonSdkApiUsageConsumer(java.util.function.Consumer<java.lang.String>);
-    method public static void setProcessDataDirectory(String);
-    method public static void setProcessPackageName(String);
-    method public float setTargetHeapUtilization(float);
-    method public void setTargetSdkVersion(int);
-    method public void startJitCompilation();
-    method public void updateProcessState(int);
-    method public String vmInstructionSet();
-    method public String vmLibrary();
-    field public static final int SDK_VERSION_CUR_DEVELOPMENT = 10000; // 0x2710
-  }
-
-  public static interface VMRuntime.HiddenApiUsageLogger {
-    method public void hiddenApiUsed(int, String, String, int, boolean);
-    field public static final int ACCESS_METHOD_JNI = 2; // 0x2
-    field public static final int ACCESS_METHOD_LINKING = 3; // 0x3
-    field public static final int ACCESS_METHOD_NONE = 0; // 0x0
-    field public static final int ACCESS_METHOD_REFLECTION = 1; // 0x1
-  }
-
-  public final class VMStack {
-    method @dalvik.annotation.optimization.FastNative public static dalvik.system.AnnotatedStackTraceElement[] getAnnotatedThreadStackTrace(Thread);
-  }
-
-  public final class ZygoteHooks {
-    method public static void gcAndFinalize();
-    method public static void onBeginPreload();
-    method public static void onEndPreload();
-    method public static void postForkChild(int, boolean, boolean, String);
-    method public static void postForkCommon();
-    method public static void postForkSystemServer(int);
-    method public static void preFork();
-    method public static void startZygoteNoThreadCreation();
-    method public static void stopZygoteNoThreadCreation();
-  }
-
-}
-
-package java.io {
-
-  public final class FileDescriptor {
-    method public int getInt$();
-    method public void setInt$(int);
-  }
-
-  public class FileInputStream extends java.io.InputStream {
-    ctor public FileInputStream(java.io.FileDescriptor, boolean);
-  }
-
-}
-
-package java.lang {
-
-  public final class Class<T> implements java.lang.reflect.AnnotatedElement java.lang.reflect.GenericDeclaration java.io.Serializable java.lang.reflect.Type {
-    method @dalvik.annotation.optimization.FastNative public java.lang.reflect.Field[] getDeclaredFieldsUnchecked(boolean);
-    method @dalvik.annotation.optimization.FastNative public java.lang.reflect.Method[] getDeclaredMethodsUnchecked(boolean);
-    method public String getPackageName$();
-  }
-
-  public final class Math {
-    method public static long randomLongInternal();
-  }
-
-  public final class System {
-    method public static void logE(String, Throwable);
-  }
-
-  public class Thread implements java.lang.Runnable {
-    method public static java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionPreHandler();
-    method public static void setUncaughtExceptionPreHandler(java.lang.Thread.UncaughtExceptionHandler);
-  }
-
-}
-
-package java.net {
-
-  public class DatagramSocket implements java.io.Closeable {
-    method public java.io.FileDescriptor getFileDescriptor$();
-  }
-
-  public final class Inet4Address extends java.net.InetAddress {
-    field public static final java.net.InetAddress ALL;
-    field public static final java.net.InetAddress ANY;
-    field public static final java.net.InetAddress LOOPBACK;
-  }
-
-  public final class Inet6Address extends java.net.InetAddress {
-    method public static java.net.Inet6Address getByAddress(String, byte[], java.net.NetworkInterface) throws java.net.UnknownHostException;
-    method public static java.net.Inet6Address getByAddress(String, byte[], int) throws java.net.UnknownHostException;
-    method public int getScopeId();
-    method public java.net.NetworkInterface getScopedInterface();
-    method public boolean isIPv4CompatibleAddress();
-    field public static final java.net.InetAddress ANY;
-    field public static final java.net.InetAddress LOOPBACK;
-  }
-
-  public class InetAddress implements java.io.Serializable {
-    method public static void clearDnsCache();
-    method public static java.net.InetAddress[] getAllByNameOnNet(String, int) throws java.net.UnknownHostException;
-    method public static java.net.InetAddress getByNameOnNet(String, int) throws java.net.UnknownHostException;
-    method @Deprecated public static boolean isNumeric(String);
-    method @Deprecated public static java.net.InetAddress parseNumericAddress(String);
-  }
-
-  public class InetSocketAddress extends java.net.SocketAddress {
-    ctor public InetSocketAddress();
-  }
-
-  public class ServerSocket implements java.io.Closeable {
-    method public java.net.SocketImpl getImpl() throws java.net.SocketException;
-  }
-
-  public class Socket implements java.io.Closeable {
-    method public java.io.FileDescriptor getFileDescriptor$();
-  }
-
-  public abstract class SocketImpl implements java.net.SocketOptions {
-    method public java.io.FileDescriptor getFD$();
-  }
-
-}
-
-package java.nio {
-
-  public abstract class ByteBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.ByteBuffer> {
-    method public void setAccessible(boolean);
-  }
-
-  public class DirectByteBuffer extends java.nio.MappedByteBuffer {
-    ctor public DirectByteBuffer(int, long, java.io.FileDescriptor, Runnable, boolean);
-    method public final long address();
-    method public final void setAccessible(boolean);
-  }
-
-  public final class NIOAccess {
-    method public static Object getBaseArray(java.nio.Buffer);
-    method public static int getBaseArrayOffset(java.nio.Buffer);
-  }
-
-  public final class NioUtils {
-    method public static void freeDirectBuffer(java.nio.ByteBuffer);
-    method public static byte[] unsafeArray(java.nio.ByteBuffer);
-    method public static int unsafeArrayOffset(java.nio.ByteBuffer);
-  }
-
-}
-
-package java.security {
-
-  public abstract class Provider extends java.util.Properties {
-    method public void warmUpServiceProvision();
-  }
-
-  public abstract class Signature extends java.security.SignatureSpi {
-    method public java.security.SignatureSpi getCurrentSpi();
-  }
-
-}
-
-package java.text {
-
-  public abstract class DateFormat extends java.text.Format {
-    method public static final void set24HourTimePref(Boolean);
-  }
-
-}
-
-package java.util {
-
-  public class LinkedHashMap<K, V> extends java.util.HashMap<K,V> implements java.util.Map<K,V> {
-    method public java.util.Map.Entry<K,V> eldest();
-  }
-
-  public final class Locale implements java.lang.Cloneable java.io.Serializable {
-    method public static String adjustLanguageCode(String);
-  }
-
-}
-
-package java.util.concurrent {
-
-  public class CompletableFuture<T> implements java.util.concurrent.CompletionStage<T> java.util.concurrent.Future<T> {
-    method public java.util.concurrent.CompletableFuture<T> completeOnTimeout(T, long, java.util.concurrent.TimeUnit);
-    method public static <U> java.util.concurrent.CompletableFuture<U> failedFuture(Throwable);
-  }
-
-}
-
-package java.util.zip {
-
-  public class ZipEntry implements java.lang.Cloneable {
-    method public long getDataOffset();
-  }
-
-}
-
-package javax.crypto {
-
-  public class Cipher {
-    method public javax.crypto.CipherSpi getCurrentSpi();
-  }
-
-  public class Mac implements java.lang.Cloneable {
-    method public javax.crypto.MacSpi getCurrentSpi();
-  }
-
-}
-
-package javax.net.ssl {
-
-  public abstract class HttpsURLConnection extends java.net.HttpURLConnection {
-    method public static javax.net.ssl.HostnameVerifier getStrictHostnameVerifier();
-  }
-
-}
-
-package libcore.content.type {
-
-  public final class MimeMap {
-    method public libcore.content.type.MimeMap.Builder buildUpon();
-    method public static libcore.content.type.MimeMap.Builder builder();
-    method @NonNull public java.util.Set<java.lang.String> extensions();
-    method @NonNull public static libcore.content.type.MimeMap getDefault();
-    method @Nullable public String guessExtensionFromMimeType(@Nullable String);
-    method @Nullable public String guessMimeTypeFromExtension(@Nullable String);
-    method public boolean hasExtension(@Nullable String);
-    method public boolean hasMimeType(@Nullable String);
-    method @NonNull public java.util.Set<java.lang.String> mimeTypes();
-    method public static void setDefaultSupplier(@NonNull java.util.function.Supplier<libcore.content.type.MimeMap>);
-  }
-
-  public static final class MimeMap.Builder {
-    method public libcore.content.type.MimeMap build();
-    method public libcore.content.type.MimeMap.Builder put(@NonNull String, @NonNull java.util.List<java.lang.String>);
-  }
-
-}
-
-package libcore.icu {
-
-  public final class DateIntervalFormat {
-    method public static String formatDateRange(long, long, int, String);
-  }
-
-  public final class ICU {
-    method public static String getBestDateTimePattern(String, java.util.Locale);
-    method public static char[] getDateFormatOrder(String);
-  }
-
-  public final class LocaleData {
-    method public static libcore.icu.LocaleData get(java.util.Locale);
-    method public String getDateFormat(int);
-    field public String[] amPm;
-    field public Integer firstDayOfWeek;
-    field public String[] longMonthNames;
-    field public String[] longStandAloneMonthNames;
-    field public String[] longStandAloneWeekdayNames;
-    field public String[] longWeekdayNames;
-    field public String narrowAm;
-    field public String narrowPm;
-    field public String[] shortMonthNames;
-    field public String[] shortStandAloneMonthNames;
-    field public String[] shortStandAloneWeekdayNames;
-    field public String[] shortWeekdayNames;
-    field public String timeFormat_Hm;
-    field public String timeFormat_Hms;
-    field public String timeFormat_hm;
-    field public String timeFormat_hms;
-    field public String[] tinyMonthNames;
-    field public String[] tinyStandAloneMonthNames;
-    field public String[] tinyStandAloneWeekdayNames;
-    field public String[] tinyWeekdayNames;
-    field public String today;
-    field public String yesterday;
-    field public char zeroDigit;
-  }
-
-  public final class RelativeDateTimeFormatter {
-    method public static String getRelativeDateTimeString(java.util.Locale, java.util.TimeZone, long, long, long, long, int);
-    method public static String getRelativeTimeSpanString(java.util.Locale, java.util.TimeZone, long, long, long, int);
-  }
-
-}
-
-package libcore.internal {
-
-  public final class StringPool {
-    ctor public StringPool();
-    method public String get(char[], int, int);
-  }
-
-}
-
-package libcore.io {
-
-  public class ForwardingOs implements libcore.io.Os {
-    ctor protected ForwardingOs(libcore.io.Os);
-    method public boolean access(String, int) throws android.system.ErrnoException;
-    method public java.io.FileDescriptor open(String, int, int) throws android.system.ErrnoException;
-    method public void remove(String) throws android.system.ErrnoException;
-    method public void rename(String, String) throws android.system.ErrnoException;
-    method public android.system.StructStat stat(String) throws android.system.ErrnoException;
-    method public void unlink(String) throws android.system.ErrnoException;
-  }
-
-  public final class IoBridge {
-    method public static void closeAndSignalBlockedThreads(java.io.FileDescriptor) throws java.io.IOException;
-    method public static java.net.InetSocketAddress getLocalInetSocketAddress(java.io.FileDescriptor) throws java.net.SocketException;
-    method public static java.io.FileDescriptor open(String, int) throws java.io.FileNotFoundException;
-    method public static int read(java.io.FileDescriptor, byte[], int, int) throws java.io.IOException;
-    method public static int recvfrom(boolean, java.io.FileDescriptor, byte[], int, int, int, java.net.DatagramPacket, boolean) throws java.io.IOException;
-    method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws java.io.IOException;
-    method public static java.io.FileDescriptor socket(int, int, int) throws java.net.SocketException;
-    method public static void write(java.io.FileDescriptor, byte[], int, int) throws java.io.IOException;
-  }
-
-  public final class IoUtils {
-    method public static int acquireRawFd(@NonNull java.io.FileDescriptor);
-    method public static void close(java.io.FileDescriptor) throws java.io.IOException;
-    method public static void closeQuietly(AutoCloseable);
-    method public static void closeQuietly(java.io.FileDescriptor);
-    method public static void closeQuietly(java.net.Socket);
-    method @Deprecated public static void deleteContents(java.io.File) throws java.io.IOException;
-    method public static byte[] readFileAsByteArray(String) throws java.io.IOException;
-    method public static String readFileAsString(String) throws java.io.IOException;
-    method public static void setBlocking(java.io.FileDescriptor, boolean) throws java.io.IOException;
-    method public static void setFdOwner(@NonNull java.io.FileDescriptor, @NonNull Object);
-  }
-
-  public final class Memory {
-    method public static void memmove(Object, int, Object, int, long);
-    method public static int peekInt(byte[], int, java.nio.ByteOrder);
-    method public static short peekShort(byte[], int, java.nio.ByteOrder);
-    method public static void pokeInt(byte[], int, int, java.nio.ByteOrder);
-    method public static void pokeLong(byte[], int, long, java.nio.ByteOrder);
-    method public static void pokeShort(byte[], int, short, java.nio.ByteOrder);
-  }
-
-  public interface Os {
-    method public static boolean compareAndSetDefault(libcore.io.Os, libcore.io.Os);
-    method public static libcore.io.Os getDefault();
-  }
-
-  public final class Streams {
-    method public static int copy(java.io.InputStream, java.io.OutputStream) throws java.io.IOException;
-    method public static void readFully(java.io.InputStream, byte[]) throws java.io.IOException;
-    method public static byte[] readFully(java.io.InputStream) throws java.io.IOException;
-    method public static String readFully(java.io.Reader) throws java.io.IOException;
-    method public static byte[] readFullyNoClose(java.io.InputStream) throws java.io.IOException;
-    method public static int readSingleByte(java.io.InputStream) throws java.io.IOException;
-    method public static long skipByReading(java.io.InputStream, long) throws java.io.IOException;
-    method public static void writeSingleByte(java.io.OutputStream, int) throws java.io.IOException;
-  }
-
-}
-
-package libcore.net {
-
-  public class InetAddressUtils {
-    method public static boolean isNumericAddress(String);
-    method public static java.net.InetAddress parseNumericAddress(String);
-  }
-
-  public abstract class NetworkSecurityPolicy {
-    ctor public NetworkSecurityPolicy();
-    method public static libcore.net.NetworkSecurityPolicy getInstance();
-    method public abstract boolean isCertificateTransparencyVerificationRequired(String);
-    method public abstract boolean isCleartextTrafficPermitted();
-    method public abstract boolean isCleartextTrafficPermitted(String);
-    method public static void setInstance(libcore.net.NetworkSecurityPolicy);
-  }
-
-}
-
-package libcore.net.event {
-
-  public class NetworkEventDispatcher {
-    method public static libcore.net.event.NetworkEventDispatcher getInstance();
-    method public void onNetworkConfigurationChanged();
-  }
-
-}
-
-package libcore.timezone {
-
-  public final class CountryTimeZones {
-    method public String getCountryIso();
-    method public android.icu.util.TimeZone getDefaultTimeZone();
-    method public String getDefaultTimeZoneId();
-    method public java.util.List<libcore.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long);
-    method public java.util.List<libcore.timezone.CountryTimeZones.TimeZoneMapping> getTimeZoneMappings();
-    method public boolean hasUtcZone(long);
-    method public boolean isDefaultTimeZoneBoosted();
-    method public boolean isForCountryCode(String);
-    method public libcore.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(long, android.icu.util.TimeZone, int, boolean);
-    method public libcore.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(long, android.icu.util.TimeZone, int);
-  }
-
-  public static final class CountryTimeZones.OffsetResult {
-    method public android.icu.util.TimeZone getTimeZone();
-    method public boolean isOnlyMatch();
-  }
-
-  public static final class CountryTimeZones.TimeZoneMapping {
-    method public static libcore.timezone.CountryTimeZones.TimeZoneMapping createForTests(String, boolean, Long);
-    method public Long getNotUsedAfter();
-    method public android.icu.util.TimeZone getTimeZone();
-    method public String getTimeZoneId();
-    method public boolean isShownInPicker();
-  }
-
-  public final class CountryZonesFinder {
-    method public java.util.List<java.lang.String> lookupAllCountryIsoCodes();
-    method public libcore.timezone.CountryTimeZones lookupCountryTimeZones(String);
-    method public java.util.List<libcore.timezone.CountryTimeZones> lookupCountryTimeZonesForZoneId(String);
-  }
-
-  public final class TelephonyLookup {
-    method public static libcore.timezone.TelephonyLookup createInstance(String) throws java.io.IOException;
-    method public static libcore.timezone.TelephonyLookup getInstance();
-    method public libcore.timezone.TelephonyNetworkFinder getTelephonyNetworkFinder();
-    method public void validate() throws java.io.IOException;
-  }
-
-  public final class TelephonyNetwork {
-    method public String getCountryIsoCode();
-    method public String getMcc();
-    method public String getMnc();
-  }
-
-  public final class TelephonyNetworkFinder {
-    method public libcore.timezone.TelephonyNetwork findNetworkByMccMnc(String, String);
-  }
-
-  public final class TimeZoneDataFiles {
-    method public static String getDataTimeZoneFile(String);
-    method public static String getDataTimeZoneRootDir();
-    method public static String getTimeZoneModuleTzVersionFile();
-  }
-
-  public final class TimeZoneFinder {
-    method public static libcore.timezone.TimeZoneFinder createInstance(String) throws java.io.IOException;
-    method public libcore.timezone.CountryZonesFinder getCountryZonesFinder();
-    method public String getIanaVersion();
-    method public static libcore.timezone.TimeZoneFinder getInstance();
-    method public libcore.timezone.CountryTimeZones lookupCountryTimeZones(String);
-    method public void validate() throws java.io.IOException;
-  }
-
-  public final class TzDataSetVersion {
-    ctor public TzDataSetVersion(int, int, String, int) throws libcore.timezone.TzDataSetVersion.TzDataSetException;
-    method public static int currentFormatMajorVersion();
-    method public static int currentFormatMinorVersion();
-    method public int getFormatMajorVersion();
-    method public int getFormatMinorVersion();
-    method public int getRevision();
-    method public String getRulesVersion();
-    method public static boolean isCompatibleWithThisDevice(libcore.timezone.TzDataSetVersion);
-    method public static libcore.timezone.TzDataSetVersion readFromFile(java.io.File) throws java.io.IOException, libcore.timezone.TzDataSetVersion.TzDataSetException;
-    method public static libcore.timezone.TzDataSetVersion readTimeZoneModuleVersion() throws java.io.IOException, libcore.timezone.TzDataSetVersion.TzDataSetException;
-    method public byte[] toBytes();
-    field public static final String DEFAULT_FILE_NAME = "tz_version";
-  }
-
-  public static class TzDataSetVersion.TzDataSetException extends java.lang.Exception {
-    ctor public TzDataSetVersion.TzDataSetException(String);
-    ctor public TzDataSetVersion.TzDataSetException(String, Throwable);
-  }
-
-  public final class ZoneInfoDb implements java.lang.AutoCloseable {
-    method public static libcore.timezone.ZoneInfoDb getInstance();
-    method public String getVersion();
-    method public boolean hasTimeZone(String) throws java.io.IOException;
-    method public static libcore.timezone.ZoneInfoDb loadTzData(String);
-    method public libcore.util.ZoneInfo makeTimeZone(String) throws java.io.IOException;
-    method public void validate() throws java.io.IOException;
-  }
-
-}
-
-package libcore.util {
-
-  public final class ArrayUtils {
-    method public static void throwsIfOutOfBounds(int, int, int);
-  }
-
-  public class CoreLibraryDebug {
-    method public static libcore.util.DebugInfo getDebugInfo();
-  }
-
-  public class DebugInfo {
-    ctor public DebugInfo();
-    method public libcore.util.DebugInfo addStringEntry(String, String);
-    method public libcore.util.DebugInfo addStringEntry(String, int);
-    method public java.util.List<libcore.util.DebugInfo.DebugEntry> getDebugEntries();
-  }
-
-  public static class DebugInfo.DebugEntry {
-    ctor public DebugInfo.DebugEntry(String, String);
-    method public String getKey();
-    method public String getStringValue();
-  }
-
-  public final class EmptyArray {
-    field public static final boolean[] BOOLEAN;
-    field public static final byte[] BYTE;
-    field public static final float[] FLOAT;
-    field public static final int[] INT;
-    field public static final long[] LONG;
-    field public static final Object[] OBJECT;
-    field public static final String[] STRING;
-  }
-
-  public class FP16 {
-    method public static short ceil(short);
-    method public static int compare(short, short);
-    method public static boolean equals(short, short);
-    method public static short floor(short);
-    method public static boolean greater(short, short);
-    method public static boolean greaterEquals(short, short);
-    method public static boolean isInfinite(short);
-    method public static boolean isNaN(short);
-    method public static boolean isNormalized(short);
-    method public static boolean less(short, short);
-    method public static boolean lessEquals(short, short);
-    method public static short max(short, short);
-    method public static short min(short, short);
-    method public static short rint(short);
-    method public static float toFloat(short);
-    method public static short toHalf(float);
-    method public static String toHexString(short);
-    method public static short trunc(short);
-    field public static final short EPSILON = 5120; // 0x1400
-    field public static final int EXPONENT_BIAS = 15; // 0xf
-    field public static final int EXPONENT_SHIFT = 10; // 0xa
-    field public static final int EXPONENT_SIGNIFICAND_MASK = 32767; // 0x7fff
-    field public static final short LOWEST_VALUE = -1025; // 0xfffffbff
-    field public static final int MAX_EXPONENT = 15; // 0xf
-    field public static final short MAX_VALUE = 31743; // 0x7bff
-    field public static final int MIN_EXPONENT = -14; // 0xfffffff2
-    field public static final short MIN_NORMAL = 1024; // 0x400
-    field public static final short MIN_VALUE = 1; // 0x1
-    field public static final short NEGATIVE_INFINITY = -1024; // 0xfffffc00
-    field public static final short NEGATIVE_ZERO = -32768; // 0xffff8000
-    field public static final short NaN = 32256; // 0x7e00
-    field public static final short POSITIVE_INFINITY = 31744; // 0x7c00
-    field public static final short POSITIVE_ZERO = 0; // 0x0
-    field public static final int SHIFTED_EXPONENT_MASK = 31; // 0x1f
-    field public static final int SIGNIFICAND_MASK = 1023; // 0x3ff
-    field public static final int SIGN_MASK = 32768; // 0x8000
-    field public static final int SIGN_SHIFT = 15; // 0xf
-    field public static final int SIZE = 16; // 0x10
-  }
-
-  public class HexEncoding {
-    method public static byte[] decode(String) throws java.lang.IllegalArgumentException;
-    method public static byte[] decode(String, boolean) throws java.lang.IllegalArgumentException;
-    method public static byte[] decode(char[]) throws java.lang.IllegalArgumentException;
-    method public static byte[] decode(char[], boolean) throws java.lang.IllegalArgumentException;
-    method public static char[] encode(byte[]);
-    method public static char[] encode(byte[], boolean);
-    method public static char[] encode(byte[], int, int);
-    method public static String encodeToString(byte, boolean);
-    method public static String encodeToString(byte[]);
-    method public static String encodeToString(byte[], boolean);
-  }
-
-  public class NativeAllocationRegistry {
-    ctor public NativeAllocationRegistry(ClassLoader, long, long);
-    method public static void applyFreeFunction(long, long);
-    method public static libcore.util.NativeAllocationRegistry createMalloced(ClassLoader, long, long);
-    method public static libcore.util.NativeAllocationRegistry createMalloced(ClassLoader, long);
-    method public static libcore.util.NativeAllocationRegistry createNonmalloced(ClassLoader, long, long);
-    method public Runnable registerNativeAllocation(Object, long);
-  }
-
-  public class SneakyThrow {
-    method public static void sneakyThrow(Throwable);
-  }
-
-  public class XmlObjectFactory {
-    method public static org.xml.sax.XMLReader newXMLReader();
-    method public static org.xmlpull.v1.XmlPullParser newXmlPullParser();
-    method public static org.xmlpull.v1.XmlSerializer newXmlSerializer();
-  }
-
-  public final class ZoneInfo extends java.util.TimeZone {
-  }
-
-  public static class ZoneInfo.WallTime {
-    ctor public ZoneInfo.WallTime();
-    method public int getGmtOffset();
-    method public int getHour();
-    method public int getIsDst();
-    method public int getMinute();
-    method public int getMonth();
-    method public int getMonthDay();
-    method public int getSecond();
-    method public int getWeekDay();
-    method public int getYear();
-    method public int getYearDay();
-    method public void localtime(int, libcore.util.ZoneInfo);
-    method public int mktime(libcore.util.ZoneInfo);
-    method public void setGmtOffset(int);
-    method public void setHour(int);
-    method public void setIsDst(int);
-    method public void setMinute(int);
-    method public void setMonth(int);
-    method public void setMonthDay(int);
-    method public void setSecond(int);
-    method public void setWeekDay(int);
-    method public void setYear(int);
-    method public void setYearDay(int);
-  }
-
-}
-
-package org.apache.harmony.dalvik {
-
-  public final class NativeTestTarget {
-    ctor public NativeTestTarget();
-    method public static void emptyInlineMethod();
-    method public static void emptyInternalStaticMethod();
-    method public void emptyJniMethod0();
-    method @dalvik.annotation.optimization.FastNative public void emptyJniMethod0_Fast();
-    method public void emptyJniMethod6(int, int, int, int, int, int);
-    method public void emptyJniMethod6L(String, String[], int[][], Object, Object[], Object[][][][]);
-    method @dalvik.annotation.optimization.FastNative public void emptyJniMethod6L_Fast(String, String[], int[][], Object, Object[], Object[][][][]);
-    method @dalvik.annotation.optimization.FastNative public void emptyJniMethod6_Fast(int, int, int, int, int, int);
-    method public static void emptyJniStaticMethod0();
-    method @dalvik.annotation.optimization.CriticalNative public static void emptyJniStaticMethod0_Critical();
-    method @dalvik.annotation.optimization.FastNative public static void emptyJniStaticMethod0_Fast();
-    method public static void emptyJniStaticMethod6(int, int, int, int, int, int);
-    method public static void emptyJniStaticMethod6L(String, String[], int[][], Object, Object[], Object[][][][]);
-    method @dalvik.annotation.optimization.FastNative public static void emptyJniStaticMethod6L_Fast(String, String[], int[][], Object, Object[], Object[][][][]);
-    method @dalvik.annotation.optimization.CriticalNative public static void emptyJniStaticMethod6_Critical(int, int, int, int, int, int);
-    method @dalvik.annotation.optimization.FastNative public static void emptyJniStaticMethod6_Fast(int, int, int, int, int, int);
-    method public static void emptyJniStaticSynchronizedMethod0();
-    method public void emptyJniSynchronizedMethod0();
-  }
-
-}
-
-package org.apache.harmony.dalvik.ddmc {
-
-  public class Chunk {
-    ctor public Chunk(int, byte[], int, int);
-    ctor public Chunk(int, java.nio.ByteBuffer);
-    field public int type;
-  }
-
-  public abstract class ChunkHandler {
-    ctor public ChunkHandler();
-    method public abstract void connected();
-    method public static org.apache.harmony.dalvik.ddmc.Chunk createFailChunk(int, String);
-    method public abstract void disconnected();
-    method public static String getString(java.nio.ByteBuffer, int);
-    method public abstract org.apache.harmony.dalvik.ddmc.Chunk handleChunk(org.apache.harmony.dalvik.ddmc.Chunk);
-    method public static String name(int);
-    method public static void putString(java.nio.ByteBuffer, String);
-    method public static int type(String);
-    method public static java.nio.ByteBuffer wrapChunk(org.apache.harmony.dalvik.ddmc.Chunk);
-    field public static final java.nio.ByteOrder CHUNK_ORDER;
-  }
-
-  public class DdmServer {
-    method public static void registerHandler(int, org.apache.harmony.dalvik.ddmc.ChunkHandler);
-    method public static void registrationComplete();
-    method public static void sendChunk(org.apache.harmony.dalvik.ddmc.Chunk);
-  }
-
-  public class DdmVmInternal {
-    method public static void enableRecentAllocations(boolean);
-    method @dalvik.annotation.optimization.FastNative public static boolean getRecentAllocationStatus();
-    method @dalvik.annotation.optimization.FastNative public static byte[] getRecentAllocations();
-    method public static StackTraceElement[] getStackTraceById(int);
-    method public static byte[] getThreadStats();
-    method @dalvik.annotation.optimization.FastNative public static boolean heapInfoNotify(int);
-    method public static boolean heapSegmentNotify(int, int, boolean);
-    method public static void threadNotify(boolean);
-  }
-
-}
-
-package org.json {
-
-  public class JSONObject {
-    method public java.util.Set<java.lang.String> keySet();
-  }
-
-}
-
-package sun.misc {
-
-  public class Cleaner extends java.lang.ref.PhantomReference<java.lang.Object> {
-    method public void clean();
-    method public static sun.misc.Cleaner create(Object, Runnable);
-  }
-
-  public final class Unsafe {
-    method public int arrayBaseOffset(Class);
-    method public int arrayIndexScale(Class);
-    method @dalvik.annotation.optimization.FastNative public void copyMemory(long, long, long);
-    method @dalvik.annotation.optimization.FastNative public boolean getBoolean(Object, long);
-    method @dalvik.annotation.optimization.FastNative public byte getByte(Object, long);
-    method @dalvik.annotation.optimization.FastNative public byte getByte(long);
-    method @dalvik.annotation.optimization.FastNative public double getDouble(Object, long);
-    method @dalvik.annotation.optimization.FastNative public float getFloat(Object, long);
-    method @dalvik.annotation.optimization.FastNative public int getInt(Object, long);
-    method @dalvik.annotation.optimization.FastNative public int getInt(long);
-    method @dalvik.annotation.optimization.FastNative public long getLong(Object, long);
-    method @dalvik.annotation.optimization.FastNative public long getLong(long);
-    method @dalvik.annotation.optimization.FastNative public Object getObject(Object, long);
-    method public static sun.misc.Unsafe getUnsafe();
-    method public long objectFieldOffset(java.lang.reflect.Field);
-    method @dalvik.annotation.optimization.FastNative public void putBoolean(Object, long, boolean);
-    method @dalvik.annotation.optimization.FastNative public void putByte(Object, long, byte);
-    method @dalvik.annotation.optimization.FastNative public void putByte(long, byte);
-    method @dalvik.annotation.optimization.FastNative public void putDouble(Object, long, double);
-    method @dalvik.annotation.optimization.FastNative public void putFloat(Object, long, float);
-    method @dalvik.annotation.optimization.FastNative public void putInt(Object, long, int);
-    method @dalvik.annotation.optimization.FastNative public void putInt(long, int);
-    method @dalvik.annotation.optimization.FastNative public void putLong(Object, long, long);
-    method @dalvik.annotation.optimization.FastNative public void putLong(long, long);
-    method @dalvik.annotation.optimization.FastNative public void putObject(Object, long, Object);
-  }
-
-}
-
-package sun.security.jca {
-
-  public class Providers {
-    method public static Object startJarVerification();
-    method public static void stopJarVerification(Object);
-  }
-
-}
-
-package sun.security.pkcs {
-
-  public class ContentInfo {
-    method public byte[] getContentBytes() throws java.io.IOException;
-  }
-
-  public class PKCS7 {
-    ctor public PKCS7(java.io.InputStream) throws java.io.IOException, sun.security.pkcs.ParsingException;
-    ctor public PKCS7(byte[]) throws sun.security.pkcs.ParsingException;
-    method public java.security.cert.X509Certificate[] getCertificates();
-    method public sun.security.pkcs.ContentInfo getContentInfo();
-    method public sun.security.pkcs.SignerInfo[] getSignerInfos();
-    method public sun.security.pkcs.SignerInfo verify(sun.security.pkcs.SignerInfo, java.io.InputStream) throws java.io.IOException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
-    method public sun.security.pkcs.SignerInfo[] verify(byte[]) throws java.security.NoSuchAlgorithmException, java.security.SignatureException;
-  }
-
-  public class ParsingException extends java.io.IOException {
-  }
-
-  public class SignerInfo {
-    ctor public SignerInfo();
-    method public java.util.ArrayList<java.security.cert.X509Certificate> getCertificateChain(sun.security.pkcs.PKCS7) throws java.io.IOException;
-  }
-
-}
-
-package sun.security.util {
-
-  public final class ObjectIdentifier implements java.io.Serializable {
-    ctor public ObjectIdentifier(String) throws java.io.IOException;
-  }
-
-}
-
-package sun.security.x509 {
-
-  public class AlgorithmId implements java.io.Serializable {
-    ctor public AlgorithmId(sun.security.util.ObjectIdentifier);
-    method public String getName();
-  }
-
-}
-
-package sun.util.locale {
-
-  public class LanguageTag {
-    method public static boolean isLanguage(String);
-  }
-
-}
-
diff --git a/mmodules/core_platform_api/api/stable_platform/current.txt b/mmodules/core_platform_api/api/stable_platform/current.txt
new file mode 100644
index 0000000..15741a7
--- /dev/null
+++ b/mmodules/core_platform_api/api/stable_platform/current.txt
@@ -0,0 +1,865 @@
+// Signature format: 2.0
+package android.compat {
+
+  public final class Compatibility {
+    method public static void clearBehaviorChangeDelegate();
+    method public static void clearOverrides();
+    method public static boolean isChangeEnabled(long);
+    method public static void reportUnconditionalChange(long);
+    method public static void setBehaviorChangeDelegate(android.compat.Compatibility.BehaviorChangeDelegate);
+    method public static void setOverrides(android.compat.Compatibility.ChangeConfig);
+  }
+
+  public static interface Compatibility.BehaviorChangeDelegate {
+    method public default boolean isChangeEnabled(long);
+    method public default void onChangeReported(long);
+  }
+
+  public static final class Compatibility.ChangeConfig {
+    ctor public Compatibility.ChangeConfig(@NonNull java.util.Set<java.lang.Long>, @NonNull java.util.Set<java.lang.Long>);
+    method @NonNull public long[] getDisabledChangesArray();
+    method @NonNull public java.util.Set<java.lang.Long> getDisabledSet();
+    method @NonNull public long[] getEnabledChangesArray();
+    method @NonNull public java.util.Set<java.lang.Long> getEnabledSet();
+    method public boolean isEmpty();
+    method public boolean isForceDisabled(long);
+    method public boolean isForceEnabled(long);
+  }
+
+}
+
+package android.system {
+
+  public final class NetlinkSocketAddress extends java.net.SocketAddress {
+    ctor public NetlinkSocketAddress(int, int);
+    method public int getGroupsMask();
+    method public int getPortId();
+  }
+
+  public final class Os {
+    method @Nullable public static android.system.StructCapUserData[] capget(@NonNull android.system.StructCapUserHeader) throws android.system.ErrnoException;
+    method public static void capset(@NonNull android.system.StructCapUserHeader, @NonNull android.system.StructCapUserData[]) throws android.system.ErrnoException;
+    method public static int getpgid(int) throws android.system.ErrnoException;
+    method @Nullable public static android.system.StructRlimit getrlimit(int) throws android.system.ErrnoException;
+    method public static int getsockoptInt(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method @Nullable public static android.system.StructLinger getsockoptLinger(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method public static int ioctlInt(@NonNull java.io.FileDescriptor, int) throws android.system.ErrnoException;
+    method @Nullable public static java.io.FileDescriptor[] pipe2(int) throws android.system.ErrnoException;
+    method @Nullable public static String realpath(@Nullable String) throws android.system.ErrnoException;
+    method public static void setpgid(int, int) throws android.system.ErrnoException;
+    method public static void setregid(int, int) throws android.system.ErrnoException;
+    method public static void setreuid(int, int) throws android.system.ErrnoException;
+    method public static void setsockoptIfreq(@NonNull java.io.FileDescriptor, int, int, @Nullable String) throws android.system.ErrnoException;
+    method public static void setsockoptLinger(@NonNull java.io.FileDescriptor, int, int, @NonNull android.system.StructLinger) throws android.system.ErrnoException;
+    method public static long splice(@NonNull java.io.FileDescriptor, @Nullable android.system.Int64Ref, @NonNull java.io.FileDescriptor, @Nullable android.system.Int64Ref, long, int) throws android.system.ErrnoException;
+    method public static void unlink(@Nullable String) throws android.system.ErrnoException;
+  }
+
+  public final class OsConstants {
+    method public static int CAP_TO_INDEX(int);
+    method public static int CAP_TO_MASK(int);
+    field public static final int ARPHRD_LOOPBACK;
+    field public static final int EUSERS;
+    field public static final int MAP_POPULATE;
+    field public static final int O_DIRECT;
+    field public static final int PR_CAP_AMBIENT;
+    field public static final int PR_CAP_AMBIENT_RAISE;
+    field public static final int RLIMIT_NOFILE;
+    field public static final int RTMGRP_IPV4_IFADDR;
+    field public static final int SPLICE_F_MORE;
+    field public static final int SPLICE_F_MOVE;
+    field public static final int TIOCOUTQ;
+    field public static final int UDP_ENCAP;
+    field public static final int UDP_ENCAP_ESPINUDP;
+    field public static final int XATTR_CREATE;
+    field public static final int XATTR_REPLACE;
+    field public static final int _LINUX_CAPABILITY_VERSION_3;
+  }
+
+  public final class PacketSocketAddress extends java.net.SocketAddress {
+    ctor public PacketSocketAddress(int, int, byte[]);
+  }
+
+  public final class StructCapUserData {
+    ctor public StructCapUserData(int, int, int);
+    field public final int effective;
+    field public final int inheritable;
+    field public final int permitted;
+  }
+
+  public final class StructCapUserHeader {
+    ctor public StructCapUserHeader(int, int);
+  }
+
+  public final class StructLinger {
+    ctor public StructLinger(int, int);
+    method public boolean isOn();
+    field public final int l_linger;
+  }
+
+  public final class StructRlimit {
+    field public final long rlim_cur;
+  }
+
+  public final class UnixSocketAddress extends java.net.SocketAddress {
+    method public static android.system.UnixSocketAddress createFileSystem(String);
+  }
+
+  public final class VmSocketAddress extends java.net.SocketAddress {
+    ctor public VmSocketAddress(int, int);
+    method public int getSvmCid();
+    method public int getSvmPort();
+  }
+
+}
+
+package com.android.okhttp.internalandroidapi {
+
+  public final class AndroidResponseCacheAdapter {
+    ctor public AndroidResponseCacheAdapter(@NonNull com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder);
+    method public void close() throws java.io.IOException;
+    method public void delete() throws java.io.IOException;
+    method public void flush() throws java.io.IOException;
+    method @Nullable public java.net.CacheResponse get(@NonNull java.net.URI, @NonNull String, @Nullable java.util.Map<java.lang.String,java.util.List<java.lang.String>>) throws java.io.IOException;
+    method @NonNull public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
+    method public int getHitCount();
+    method public long getMaxSize();
+    method public int getNetworkCount();
+    method public int getRequestCount();
+    method public long getSize() throws java.io.IOException;
+    method @Nullable public java.net.CacheRequest put(@NonNull java.net.URI, @NonNull java.net.URLConnection) throws java.io.IOException;
+  }
+
+  public interface HasCacheHolder {
+    method @NonNull public com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder getCacheHolder();
+  }
+
+  public static final class HasCacheHolder.CacheHolder {
+    method @NonNull public static com.android.okhttp.internalandroidapi.HasCacheHolder.CacheHolder create(@NonNull java.io.File, long);
+    method public boolean isEquivalent(@NonNull java.io.File, long);
+  }
+
+}
+
+package dalvik.annotation.codegen {
+
+  @java.lang.annotation.Repeatable(CovariantReturnType.CovariantReturnTypes.class) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface CovariantReturnType {
+    method public abstract int presentAfter();
+    method public abstract Class<?> returnType();
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public static @interface CovariantReturnType.CovariantReturnTypes {
+    method public abstract dalvik.annotation.codegen.CovariantReturnType[] value();
+  }
+
+}
+
+package dalvik.annotation.optimization {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface CriticalNative {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface FastNative {
+  }
+
+}
+
+package dalvik.system {
+
+  public final class AnnotatedStackTraceElement {
+    method @Nullable public Object getBlockedOn();
+    method @Nullable public Object[] getHeldLocks();
+    method @NonNull public StackTraceElement getStackTraceElement();
+  }
+
+  public class AppSpecializationHooks {
+    method public static void handleCompatChangesBeforeBindingApplication();
+  }
+
+  public class BaseDexClassLoader extends java.lang.ClassLoader {
+    method public void addDexPath(@Nullable String);
+    method public void addNativePath(@NonNull java.util.Collection<java.lang.String>);
+    method @NonNull public String getLdLibraryPath();
+    method public void reportClassLoaderChain();
+    method public static void setReporter(@Nullable dalvik.system.BaseDexClassLoader.Reporter);
+  }
+
+  public static interface BaseDexClassLoader.Reporter {
+    method public void report(@NonNull java.util.Map<java.lang.String,java.lang.String>);
+  }
+
+  public final class BlockGuard {
+    method @NonNull public static dalvik.system.BlockGuard.Policy getThreadPolicy();
+    method @NonNull public static dalvik.system.BlockGuard.VmPolicy getVmPolicy();
+    method public static void setThreadPolicy(@NonNull dalvik.system.BlockGuard.Policy);
+    method public static void setVmPolicy(@NonNull dalvik.system.BlockGuard.VmPolicy);
+    field public static final dalvik.system.BlockGuard.Policy LAX_POLICY;
+    field public static final dalvik.system.BlockGuard.VmPolicy LAX_VM_POLICY;
+  }
+
+  public static interface BlockGuard.Policy {
+    method public int getPolicyMask();
+    method public void onReadFromDisk();
+    method public void onUnbufferedIO();
+    method public void onWriteToDisk();
+  }
+
+  public static interface BlockGuard.VmPolicy {
+    method public void onPathAccess(@NonNull String);
+  }
+
+  public final class CloseGuard {
+    method public void close();
+    method public static dalvik.system.CloseGuard get();
+    method public static dalvik.system.CloseGuard.Reporter getReporter();
+    method public void open(String);
+    method public void openWithCallSite(String, String);
+    method public static void setEnabled(boolean);
+    method public static void setReporter(dalvik.system.CloseGuard.Reporter);
+    method public void warnIfOpen();
+  }
+
+  public static interface CloseGuard.Reporter {
+    method public void report(String, Throwable);
+    method public default void report(String);
+  }
+
+  public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
+    ctor public DelegateLastClassLoader(String, String, ClassLoader, ClassLoader[]);
+  }
+
+  @Deprecated public final class DexFile {
+    method @Deprecated @NonNull public static dalvik.system.DexFile.OptimizationInfo getDexFileOptimizationInfo(@NonNull String, @NonNull String) throws java.io.FileNotFoundException;
+    method @Deprecated @Nullable public static String[] getDexFileOutputPaths(@NonNull String, @NonNull String) throws java.io.FileNotFoundException;
+    method @Deprecated public static int getDexOptNeeded(@NonNull String, @NonNull String, @NonNull String, @Nullable String, boolean, boolean) throws java.io.FileNotFoundException, java.io.IOException;
+    method @Deprecated @NonNull public static String getSafeModeCompilerFilter(@NonNull String);
+    method @Deprecated public static boolean isProfileGuidedCompilerFilter(@NonNull String);
+    method @Deprecated public static boolean isValidCompilerFilter(@NonNull String);
+    field @Deprecated public static final int DEX2OAT_FOR_FILTER = 3; // 0x3
+    field @Deprecated public static final int NO_DEXOPT_NEEDED = 0; // 0x0
+  }
+
+  @Deprecated public static final class DexFile.OptimizationInfo {
+    method @Deprecated @NonNull public String getReason();
+    method @Deprecated @NonNull public String getStatus();
+  }
+
+  public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
+    ctor public PathClassLoader(@NonNull String, @Nullable String, @Nullable ClassLoader, @Nullable ClassLoader[]);
+  }
+
+  public final class RuntimeHooks {
+    method public static void setTimeZoneIdSupplier(@NonNull java.util.function.Supplier<java.lang.String>);
+    method public static void setUncaughtExceptionPreHandler(@Nullable java.lang.Thread.UncaughtExceptionHandler);
+  }
+
+  public abstract class SocketTagger {
+    ctor public SocketTagger();
+    method public static dalvik.system.SocketTagger get();
+    method public static void set(dalvik.system.SocketTagger);
+    method public abstract void tag(java.io.FileDescriptor) throws java.net.SocketException;
+    method public final void tag(java.net.Socket) throws java.net.SocketException;
+    method public final void tag(java.net.DatagramSocket) throws java.net.SocketException;
+    method public abstract void untag(java.io.FileDescriptor) throws java.net.SocketException;
+    method public final void untag(java.net.Socket) throws java.net.SocketException;
+    method public final void untag(java.net.DatagramSocket) throws java.net.SocketException;
+  }
+
+  public final class VMDebug {
+    method public static void attachAgent(String, ClassLoader) throws java.io.IOException;
+    method public static long countInstancesOfClass(Class, boolean);
+    method public static long[] countInstancesOfClasses(Class[], boolean);
+    method public static void dumpHprofData(String) throws java.io.IOException;
+    method public static void dumpHprofData(String, java.io.FileDescriptor) throws java.io.IOException;
+    method public static void dumpHprofDataDdms();
+    method public static void dumpReferenceTables();
+    method public static int getAllocCount(int);
+    method @dalvik.annotation.optimization.FastNative public static int getLoadedClassCount();
+    method public static int getMethodTracingMode();
+    method public static String getRuntimeStat(String);
+    method public static java.util.Map<java.lang.String,java.lang.String> getRuntimeStats();
+    method public static String[] getVmFeatureList();
+    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggerConnected();
+    method @dalvik.annotation.optimization.FastNative public static boolean isDebuggingEnabled();
+    method @dalvik.annotation.optimization.FastNative public static long lastDebuggerActivity();
+    method @dalvik.annotation.optimization.FastNative public static void printLoadedClasses(int);
+    method public static void resetAllocCount(int);
+    method public static void setAllocTrackerStackDepth(int);
+    method public static void startAllocCounting();
+    method public static void startMethodTracing(String, int, int, boolean, int);
+    method public static void startMethodTracing(String, java.io.FileDescriptor, int, int, boolean, int, boolean);
+    method public static void startMethodTracingDdms(int, int, boolean, int);
+    method public static void stopAllocCounting();
+    method public static void stopMethodTracing();
+    method @dalvik.annotation.optimization.FastNative public static long threadCpuTimeNanos();
+    field public static final int KIND_ALL_COUNTS = -1; // 0xffffffff
+    field public static final int KIND_GLOBAL_ALLOCATED_BYTES = 2; // 0x2
+    field public static final int KIND_GLOBAL_ALLOCATED_OBJECTS = 1; // 0x1
+    field public static final int KIND_GLOBAL_CLASS_INIT_COUNT = 32; // 0x20
+    field public static final int KIND_GLOBAL_CLASS_INIT_TIME = 64; // 0x40
+    field public static final int KIND_GLOBAL_FREED_BYTES = 8; // 0x8
+    field public static final int KIND_GLOBAL_FREED_OBJECTS = 4; // 0x4
+    field public static final int KIND_GLOBAL_GC_INVOCATIONS = 16; // 0x10
+    field public static final int KIND_THREAD_ALLOCATED_BYTES = 131072; // 0x20000
+    field public static final int KIND_THREAD_ALLOCATED_OBJECTS = 65536; // 0x10000
+    field public static final int KIND_THREAD_GC_INVOCATIONS = 1048576; // 0x100000
+    field public static final int TRACE_COUNT_ALLOCS = 1; // 0x1
+  }
+
+  public final class VMRuntime {
+    method @dalvik.annotation.optimization.FastNative public long addressOf(Object);
+    method public static void bootCompleted();
+    method public void clampGrowthLimit();
+    method public void clearGrowthLimit();
+    method public static String getCurrentInstructionSet();
+    method public static String getInstructionSet(String);
+    method public static dalvik.system.VMRuntime getRuntime();
+    method public int getTargetSdkVersion();
+    method @dalvik.annotation.optimization.FastNative public boolean is64Bit();
+    method public static boolean is64BitAbi(String);
+    method public static boolean is64BitInstructionSet(String);
+    method @dalvik.annotation.optimization.FastNative public boolean isCheckJniEnabled();
+    method @dalvik.annotation.optimization.FastNative public boolean isNativeDebuggable();
+    method public static boolean isValidClassLoaderContext(String);
+    method @dalvik.annotation.optimization.FastNative public Object newNonMovableArray(Class<?>, int);
+    method @dalvik.annotation.optimization.FastNative public Object newUnpaddedArray(Class<?>, int);
+    method public void notifyStartupCompleted();
+    method public void preloadDexCaches();
+    method public static void registerAppInfo(String, String, String, String[], int);
+    method public void registerNativeAllocation(long);
+    method @Deprecated public void registerNativeAllocation(int);
+    method public void registerNativeFree(long);
+    method @Deprecated public void registerNativeFree(int);
+    method public static void registerSensitiveThread();
+    method public void requestConcurrentGC();
+    method public static void resetJitCounters();
+    method public static void setDedupeHiddenApiWarnings(boolean);
+    method public void setDisabledCompatChanges(long[]);
+    method public void setHiddenApiAccessLogSamplingRate(int);
+    method public void setHiddenApiExemptions(String[]);
+    method public static void setHiddenApiUsageLogger(dalvik.system.VMRuntime.HiddenApiUsageLogger);
+    method public static void setNonSdkApiUsageConsumer(java.util.function.Consumer<java.lang.String>);
+    method public static void setProcessDataDirectory(String);
+    method public static void setProcessPackageName(String);
+    method public void setTargetSdkVersion(int);
+    method public void updateProcessState(int);
+    method public String vmInstructionSet();
+    method public String vmLibrary();
+    field public static final int CODE_PATH_TYPE_PRIMARY_APK = 1; // 0x1
+    field public static final int CODE_PATH_TYPE_SECONDARY_DEX = 4; // 0x4
+    field public static final int CODE_PATH_TYPE_SPLIT_APK = 2; // 0x2
+    field public static final int SDK_VERSION_CUR_DEVELOPMENT = 10000; // 0x2710
+  }
+
+  public static interface VMRuntime.HiddenApiUsageLogger {
+    method public void hiddenApiUsed(int, String, String, int, boolean);
+    field public static final int ACCESS_METHOD_JNI = 2; // 0x2
+    field public static final int ACCESS_METHOD_LINKING = 3; // 0x3
+    field public static final int ACCESS_METHOD_NONE = 0; // 0x0
+    field public static final int ACCESS_METHOD_REFLECTION = 1; // 0x1
+  }
+
+  public final class VMStack {
+    method @Nullable @dalvik.annotation.optimization.FastNative public static dalvik.system.AnnotatedStackTraceElement[] getAnnotatedThreadStackTrace(Thread);
+  }
+
+  public final class ZygoteHooks {
+    method public static void gcAndFinalize();
+    method public static boolean isIndefiniteThreadSuspensionSafe();
+    method public static void onBeginPreload();
+    method public static void onEndPreload();
+    method public static void postForkChild(int, boolean, boolean, String);
+    method public static void postForkCommon();
+    method public static void postForkSystemServer(int);
+    method public static void preFork();
+    method public static void startZygoteNoThreadCreation();
+    method public static void stopZygoteNoThreadCreation();
+  }
+
+}
+
+package java.io {
+
+  public final class FileDescriptor {
+    method public int getInt$();
+    method public void setInt$(int);
+  }
+
+  public class FileInputStream extends java.io.InputStream {
+    ctor public FileInputStream(java.io.FileDescriptor, boolean);
+  }
+
+}
+
+package java.lang {
+
+  public class Thread implements java.lang.Runnable {
+    method public static java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionPreHandler();
+    method public static void setUncaughtExceptionPreHandler(java.lang.Thread.UncaughtExceptionHandler);
+  }
+
+}
+
+package java.net {
+
+  public class DatagramSocket implements java.io.Closeable {
+    method public java.io.FileDescriptor getFileDescriptor$();
+  }
+
+  public final class Inet4Address extends java.net.InetAddress {
+    field public static final java.net.InetAddress ALL;
+    field public static final java.net.InetAddress ANY;
+    field public static final java.net.InetAddress LOOPBACK;
+  }
+
+  public final class Inet6Address extends java.net.InetAddress {
+    field public static final java.net.InetAddress ANY;
+    field public static final java.net.InetAddress LOOPBACK;
+  }
+
+  public class InetAddress implements java.io.Serializable {
+    method public static void clearDnsCache();
+    method @NonNull public static java.net.InetAddress[] getAllByNameOnNet(@Nullable String, int) throws java.net.UnknownHostException;
+    method @NonNull public static java.net.InetAddress getByNameOnNet(@Nullable String, int) throws java.net.UnknownHostException;
+    method @Deprecated public static boolean isNumeric(String);
+    method @Deprecated public static java.net.InetAddress parseNumericAddress(String);
+  }
+
+  public class InetSocketAddress extends java.net.SocketAddress {
+    ctor public InetSocketAddress();
+  }
+
+  public class ServerSocket implements java.io.Closeable {
+    method public java.net.SocketImpl getImpl() throws java.net.SocketException;
+  }
+
+  public class Socket implements java.io.Closeable {
+    method public java.io.FileDescriptor getFileDescriptor$();
+  }
+
+  public abstract class SocketImpl implements java.net.SocketOptions {
+    method public java.io.FileDescriptor getFD$();
+  }
+
+}
+
+package java.nio {
+
+  public abstract class ByteBuffer extends java.nio.Buffer implements java.lang.Comparable<java.nio.ByteBuffer> {
+    method public void setAccessible(boolean);
+  }
+
+  public class DirectByteBuffer extends java.nio.MappedByteBuffer {
+    ctor public DirectByteBuffer(int, long, java.io.FileDescriptor, Runnable, boolean);
+    method public final long address();
+    method public final void setAccessible(boolean);
+  }
+
+  public final class NIOAccess {
+    method public static Object getBaseArray(java.nio.Buffer);
+    method public static int getBaseArrayOffset(java.nio.Buffer);
+  }
+
+  public final class NioUtils {
+    method public static void freeDirectBuffer(java.nio.ByteBuffer);
+    method public static byte[] unsafeArray(java.nio.ByteBuffer);
+    method public static int unsafeArrayOffset(java.nio.ByteBuffer);
+  }
+
+}
+
+package java.security {
+
+  public abstract class Provider extends java.util.Properties {
+    method public void warmUpServiceProvision();
+  }
+
+  public abstract class Signature extends java.security.SignatureSpi {
+    method public java.security.SignatureSpi getCurrentSpi();
+  }
+
+}
+
+package java.text {
+
+  public abstract class DateFormat extends java.text.Format {
+    method public static final void set24HourTimePref(Boolean);
+  }
+
+}
+
+package java.util {
+
+  public class LinkedHashMap<K, V> extends java.util.HashMap<K,V> implements java.util.Map<K,V> {
+    method public java.util.Map.Entry<K,V> eldest();
+  }
+
+}
+
+package java.util.zip {
+
+  public class ZipEntry implements java.lang.Cloneable {
+    method public long getDataOffset();
+  }
+
+}
+
+package javax.crypto {
+
+  public class Cipher {
+    method public javax.crypto.CipherSpi getCurrentSpi();
+  }
+
+  public class Mac implements java.lang.Cloneable {
+    method public javax.crypto.MacSpi getCurrentSpi();
+  }
+
+}
+
+package javax.net.ssl {
+
+  public abstract class HttpsURLConnection extends java.net.HttpURLConnection {
+    method public static javax.net.ssl.HostnameVerifier getStrictHostnameVerifier();
+  }
+
+}
+
+package libcore.content.type {
+
+  public final class MimeMap {
+    method @NonNull public libcore.content.type.MimeMap.Builder buildUpon();
+    method @NonNull public static libcore.content.type.MimeMap.Builder builder();
+    method @NonNull public java.util.Set<java.lang.String> extensions();
+    method @NonNull public static libcore.content.type.MimeMap getDefault();
+    method @Nullable public String guessExtensionFromMimeType(@Nullable String);
+    method @Nullable public String guessMimeTypeFromExtension(@Nullable String);
+    method public boolean hasExtension(@Nullable String);
+    method public boolean hasMimeType(@Nullable String);
+    method @NonNull public java.util.Set<java.lang.String> mimeTypes();
+    method public static void setDefaultSupplier(@NonNull java.util.function.Supplier<libcore.content.type.MimeMap>);
+  }
+
+  public static final class MimeMap.Builder {
+    method @NonNull public libcore.content.type.MimeMap.Builder addMimeMapping(@NonNull String, @NonNull java.util.List<java.lang.String>);
+    method @NonNull public libcore.content.type.MimeMap build();
+  }
+
+}
+
+package libcore.io {
+
+  public class ForwardingOs implements libcore.io.Os {
+    ctor protected ForwardingOs(@NonNull libcore.io.Os);
+    method public boolean access(@Nullable String, int) throws android.system.ErrnoException;
+    method public java.io.FileDescriptor open(@Nullable String, int, int) throws android.system.ErrnoException;
+    method public void remove(@Nullable String) throws android.system.ErrnoException;
+    method public void rename(@Nullable String, @Nullable String) throws android.system.ErrnoException;
+    method @Nullable public android.system.StructStat stat(@Nullable String) throws android.system.ErrnoException;
+    method public void unlink(@Nullable String) throws android.system.ErrnoException;
+  }
+
+  public final class IoBridge {
+    method public static void closeAndSignalBlockedThreads(@NonNull java.io.FileDescriptor) throws java.io.IOException;
+    method @NonNull public static java.io.FileDescriptor open(@NonNull String, int) throws java.io.FileNotFoundException;
+    method public static int read(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int) throws java.io.IOException;
+    method public static void write(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int) throws java.io.IOException;
+  }
+
+  public final class IoUtils {
+    method public static int acquireRawFd(@NonNull java.io.FileDescriptor);
+    method public static void close(@Nullable java.io.FileDescriptor) throws java.io.IOException;
+    method public static void closeQuietly(@Nullable AutoCloseable);
+    method public static void closeQuietly(@Nullable java.io.FileDescriptor);
+    method public static void closeQuietly(@Nullable java.net.Socket);
+    method @NonNull public static byte[] readFileAsByteArray(@NonNull String) throws java.io.IOException;
+    method @NonNull public static String readFileAsString(@NonNull String) throws java.io.IOException;
+    method public static void setBlocking(@NonNull java.io.FileDescriptor, boolean) throws java.io.IOException;
+    method public static void setFdOwner(@NonNull java.io.FileDescriptor, @NonNull Object);
+  }
+
+  public final class Memory {
+    method public static void memmove(@NonNull Object, int, @NonNull Object, int, long);
+    method public static int peekInt(@NonNull byte[], int, @NonNull java.nio.ByteOrder);
+    method public static short peekShort(@NonNull byte[], int, @NonNull java.nio.ByteOrder);
+    method public static void pokeInt(@NonNull byte[], int, int, @NonNull java.nio.ByteOrder);
+    method public static void pokeLong(@NonNull byte[], int, long, @NonNull java.nio.ByteOrder);
+    method public static void pokeShort(@NonNull byte[], int, short, @NonNull java.nio.ByteOrder);
+  }
+
+  public interface Os {
+    method public static boolean compareAndSetDefault(libcore.io.Os, libcore.io.Os);
+    method public static libcore.io.Os getDefault();
+  }
+
+  public final class Streams {
+    method public static int copy(@NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws java.io.IOException;
+    method public static void readFully(@NonNull java.io.InputStream, @NonNull byte[]) throws java.io.IOException;
+    method @NonNull public static byte[] readFully(@NonNull java.io.InputStream) throws java.io.IOException;
+    method @NonNull public static String readFully(@NonNull java.io.Reader) throws java.io.IOException;
+    method @NonNull public static byte[] readFullyNoClose(@NonNull java.io.InputStream) throws java.io.IOException;
+    method public static int readSingleByte(@NonNull java.io.InputStream) throws java.io.IOException;
+    method public static long skipByReading(@NonNull java.io.InputStream, long) throws java.io.IOException;
+    method public static void writeSingleByte(@NonNull java.io.OutputStream, int) throws java.io.IOException;
+  }
+
+}
+
+package libcore.net {
+
+  public class InetAddressUtils {
+    method public static boolean isNumericAddress(String);
+    method public static java.net.InetAddress parseNumericAddress(String);
+  }
+
+  public abstract class NetworkSecurityPolicy {
+    ctor public NetworkSecurityPolicy();
+    method public static libcore.net.NetworkSecurityPolicy getInstance();
+    method public abstract boolean isCertificateTransparencyVerificationRequired(String);
+    method public abstract boolean isCleartextTrafficPermitted();
+    method public abstract boolean isCleartextTrafficPermitted(String);
+    method public static void setInstance(libcore.net.NetworkSecurityPolicy);
+  }
+
+}
+
+package libcore.net.event {
+
+  public final class NetworkEventDispatcher {
+    method public void dispatchNetworkConfigurationChange();
+    method public static libcore.net.event.NetworkEventDispatcher getInstance();
+  }
+
+}
+
+package libcore.net.http {
+
+  public interface Dns {
+    method @NonNull public java.util.List<java.net.InetAddress> lookup(@Nullable String) throws java.net.UnknownHostException;
+  }
+
+  public class HttpURLConnectionFactory {
+    method @NonNull public static libcore.net.http.HttpURLConnectionFactory createInstance();
+    method public java.net.URLConnection openConnection(@NonNull java.net.URL, @NonNull javax.net.SocketFactory, @NonNull java.net.Proxy) throws java.io.IOException;
+    method public void setDns(@NonNull libcore.net.http.Dns);
+    method public void setNewConnectionPool(int, long, @NonNull java.util.concurrent.TimeUnit);
+  }
+
+}
+
+package libcore.util {
+
+  public final class EmptyArray {
+    field @NonNull public static final boolean[] BOOLEAN;
+    field @NonNull public static final byte[] BYTE;
+    field @NonNull public static final float[] FLOAT;
+    field @NonNull public static final int[] INT;
+    field @NonNull public static final long[] LONG;
+    field @NonNull public static final Object[] OBJECT;
+    field @NonNull public static final String[] STRING;
+  }
+
+  public final class FP16 {
+    method public static short ceil(short);
+    method public static int compare(short, short);
+    method public static boolean equals(short, short);
+    method public static short floor(short);
+    method public static boolean greater(short, short);
+    method public static boolean greaterEquals(short, short);
+    method public static boolean isInfinite(short);
+    method public static boolean isNaN(short);
+    method public static boolean isNormalized(short);
+    method public static boolean less(short, short);
+    method public static boolean lessEquals(short, short);
+    method public static short max(short, short);
+    method public static short min(short, short);
+    method public static short rint(short);
+    method public static float toFloat(short);
+    method public static short toHalf(float);
+    method public static String toHexString(short);
+    method public static short trunc(short);
+    field public static final short EPSILON = 5120; // 0x1400
+    field public static final int EXPONENT_BIAS = 15; // 0xf
+    field public static final int EXPONENT_SHIFT = 10; // 0xa
+    field public static final int EXPONENT_SIGNIFICAND_MASK = 32767; // 0x7fff
+    field public static final short LOWEST_VALUE = -1025; // 0xfffffbff
+    field public static final int MAX_EXPONENT = 15; // 0xf
+    field public static final short MAX_VALUE = 31743; // 0x7bff
+    field public static final int MIN_EXPONENT = -14; // 0xfffffff2
+    field public static final short MIN_NORMAL = 1024; // 0x400
+    field public static final short MIN_VALUE = 1; // 0x1
+    field public static final short NEGATIVE_INFINITY = -1024; // 0xfffffc00
+    field public static final short NEGATIVE_ZERO = -32768; // 0xffff8000
+    field public static final short NaN = 32256; // 0x7e00
+    field public static final short POSITIVE_INFINITY = 31744; // 0x7c00
+    field public static final short POSITIVE_ZERO = 0; // 0x0
+    field public static final int SHIFTED_EXPONENT_MASK = 31; // 0x1f
+    field public static final int SIGNIFICAND_MASK = 1023; // 0x3ff
+    field public static final int SIGN_MASK = 32768; // 0x8000
+    field public static final int SIGN_SHIFT = 15; // 0xf
+    field public static final int SIZE = 16; // 0x10
+  }
+
+  public class HexEncoding {
+    method public static byte[] decode(String) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(String, boolean) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(char[]) throws java.lang.IllegalArgumentException;
+    method public static byte[] decode(char[], boolean) throws java.lang.IllegalArgumentException;
+    method public static char[] encode(byte[]);
+    method public static char[] encode(byte[], boolean);
+    method public static char[] encode(byte[], int, int);
+    method public static String encodeToString(byte, boolean);
+    method public static String encodeToString(byte[]);
+    method public static String encodeToString(byte[], boolean);
+  }
+
+  public class NativeAllocationRegistry {
+    ctor public NativeAllocationRegistry(@NonNull ClassLoader, long, long);
+    method public static void applyFreeFunction(long, long);
+    method public static libcore.util.NativeAllocationRegistry createMalloced(@NonNull ClassLoader, long, long);
+    method public static libcore.util.NativeAllocationRegistry createMalloced(@NonNull ClassLoader, long);
+    method public static libcore.util.NativeAllocationRegistry createNonmalloced(@NonNull ClassLoader, long, long);
+    method @NonNull public Runnable registerNativeAllocation(@NonNull Object, long);
+  }
+
+  public class SneakyThrow {
+    method public static void sneakyThrow(@NonNull Throwable);
+  }
+
+  public class XmlObjectFactory {
+    method @NonNull public static org.xml.sax.XMLReader newXMLReader();
+    method @NonNull public static org.xmlpull.v1.XmlPullParser newXmlPullParser();
+    method @NonNull public static org.xmlpull.v1.XmlSerializer newXmlSerializer();
+  }
+
+}
+
+package org.apache.harmony.dalvik.ddmc {
+
+  public class Chunk {
+    ctor public Chunk(int, byte[], int, int);
+    ctor public Chunk(int, java.nio.ByteBuffer);
+    field public int type;
+  }
+
+  public abstract class ChunkHandler {
+    ctor public ChunkHandler();
+    method public static org.apache.harmony.dalvik.ddmc.Chunk createFailChunk(int, String);
+    method public abstract org.apache.harmony.dalvik.ddmc.Chunk handleChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    method public static String name(int);
+    method public abstract void onConnected();
+    method public abstract void onDisconnected();
+    method public static int type(String);
+    method public static java.nio.ByteBuffer wrapChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    field public static final java.nio.ByteOrder CHUNK_ORDER;
+  }
+
+  public final class DdmServer {
+    method public static void registerHandler(int, org.apache.harmony.dalvik.ddmc.ChunkHandler);
+    method public static void registrationComplete();
+    method public static void sendChunk(org.apache.harmony.dalvik.ddmc.Chunk);
+    method public static org.apache.harmony.dalvik.ddmc.ChunkHandler unregisterHandler(int);
+  }
+
+  public final class DdmVmInternal {
+    method public static void setRecentAllocationsTrackingEnabled(boolean);
+    method public static void setThreadNotifyEnabled(boolean);
+  }
+
+}
+
+package org.json {
+
+  public class JSONObject {
+    method @NonNull public java.util.Set<java.lang.String> keySet();
+  }
+
+}
+
+package sun.misc {
+
+  public class Cleaner extends java.lang.ref.PhantomReference<java.lang.Object> {
+    method public void clean();
+    method public static sun.misc.Cleaner create(Object, Runnable);
+  }
+
+  public final class Unsafe {
+    method public int arrayBaseOffset(Class);
+    method public int arrayIndexScale(Class);
+    method @dalvik.annotation.optimization.FastNative public void copyMemory(long, long, long);
+    method @dalvik.annotation.optimization.FastNative public boolean getBoolean(Object, long);
+    method @dalvik.annotation.optimization.FastNative public byte getByte(Object, long);
+    method @dalvik.annotation.optimization.FastNative public byte getByte(long);
+    method @dalvik.annotation.optimization.FastNative public double getDouble(Object, long);
+    method @dalvik.annotation.optimization.FastNative public double getDouble(long);
+    method @dalvik.annotation.optimization.FastNative public float getFloat(Object, long);
+    method @dalvik.annotation.optimization.FastNative public float getFloat(long);
+    method @dalvik.annotation.optimization.FastNative public int getInt(Object, long);
+    method @dalvik.annotation.optimization.FastNative public int getInt(long);
+    method @dalvik.annotation.optimization.FastNative public long getLong(Object, long);
+    method @dalvik.annotation.optimization.FastNative public long getLong(long);
+    method @dalvik.annotation.optimization.FastNative public Object getObject(Object, long);
+    method public static sun.misc.Unsafe getUnsafe();
+    method public long objectFieldOffset(java.lang.reflect.Field);
+    method @dalvik.annotation.optimization.FastNative public void putBoolean(Object, long, boolean);
+    method @dalvik.annotation.optimization.FastNative public void putByte(Object, long, byte);
+    method @dalvik.annotation.optimization.FastNative public void putByte(long, byte);
+    method @dalvik.annotation.optimization.FastNative public void putDouble(Object, long, double);
+    method @dalvik.annotation.optimization.FastNative public void putDouble(long, double);
+    method @dalvik.annotation.optimization.FastNative public void putFloat(Object, long, float);
+    method @dalvik.annotation.optimization.FastNative public void putFloat(long, float);
+    method @dalvik.annotation.optimization.FastNative public void putInt(Object, long, int);
+    method @dalvik.annotation.optimization.FastNative public void putInt(long, int);
+    method @dalvik.annotation.optimization.FastNative public void putLong(Object, long, long);
+    method @dalvik.annotation.optimization.FastNative public void putLong(long, long);
+    method @dalvik.annotation.optimization.FastNative public void putObject(Object, long, Object);
+  }
+
+}
+
+package sun.security.jca {
+
+  public class Providers {
+    method public static Object startJarVerification();
+    method public static void stopJarVerification(Object);
+  }
+
+}
+
+package sun.security.pkcs {
+
+  public class PKCS7 {
+    ctor public PKCS7(java.io.InputStream) throws java.io.IOException, sun.security.pkcs.ParsingException;
+    ctor public PKCS7(byte[]) throws sun.security.pkcs.ParsingException;
+    method public java.security.cert.X509Certificate[] getCertificates();
+    method public sun.security.pkcs.SignerInfo[] getSignerInfos();
+    method public sun.security.pkcs.SignerInfo verify(sun.security.pkcs.SignerInfo, java.io.InputStream) throws java.io.IOException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
+    method public sun.security.pkcs.SignerInfo[] verify(byte[]) throws java.security.NoSuchAlgorithmException, java.security.SignatureException;
+  }
+
+  public class ParsingException extends java.io.IOException {
+  }
+
+  public class SignerInfo {
+    ctor public SignerInfo();
+    method public java.util.ArrayList<java.security.cert.X509Certificate> getCertificateChain(sun.security.pkcs.PKCS7) throws java.io.IOException;
+  }
+
+}
+
+package sun.security.util {
+
+  public final class ObjectIdentifier implements java.io.Serializable {
+    ctor public ObjectIdentifier(String) throws java.io.IOException;
+  }
+
+}
+
+package sun.security.x509 {
+
+  public class AlgorithmId implements java.io.Serializable {
+    ctor public AlgorithmId(sun.security.util.ObjectIdentifier);
+    method public String getName();
+  }
+
+}
+
diff --git a/mmodules/core_platform_api/api/platform/last-api.txt b/mmodules/core_platform_api/api/stable_platform/last-api.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/last-api.txt
copy to mmodules/core_platform_api/api/stable_platform/last-api.txt
diff --git a/mmodules/core_platform_api/api/platform/last-api.txt b/mmodules/core_platform_api/api/stable_platform/last-incompatibilities.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/last-api.txt
copy to mmodules/core_platform_api/api/stable_platform/last-incompatibilities.txt
diff --git a/mmodules/core_platform_api/api/platform/last-removed.txt b/mmodules/core_platform_api/api/stable_platform/last-removed.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/last-removed.txt
copy to mmodules/core_platform_api/api/stable_platform/last-removed.txt
diff --git a/mmodules/core_platform_api/api/platform/current-removed.txt b/mmodules/core_platform_api/api/stable_platform/removed.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/current-removed.txt
copy to mmodules/core_platform_api/api/stable_platform/removed.txt
diff --git a/mmodules/intracoreapi/Android.bp b/mmodules/intracoreapi/Android.bp
index c2a3de3..513b044 100644
--- a/mmodules/intracoreapi/Android.bp
+++ b/mmodules/intracoreapi/Android.bp
@@ -12,59 +12,99 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Generates stub source files for the intra-core API of the ART module.
-// i.e. every class/member that is either in the public API or annotated with
-// @IntraCoreApi.
-//
-// The API specification .txt files managed by this only contain the additional
-// classes/members that are in the intra-core API but which are not in the public
-// API.
-droidstubs {
-    name: "art-module-intra-core-api-stubs-source",
+// Referenced implicitly from art.module.intra.core.api.
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_license":
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-GPL
+    //   SPDX-license-identifier-GPL-2.0
+    //   SPDX-license-identifier-LGPL
+    //   SPDX-license-identifier-MIT
+    //   SPDX-license-identifier-W3C
+    default_applicable_licenses: ["libcore_license"],
+}
+
+filegroup {
+    name: "art.module.intra.core.api.api.public.latest",
     srcs: [
-        ":art_module_api_files",
+        "api/intra/last-api.txt",
     ],
-    sdk_version: "none",
-    system_modules: "none",
+}
 
-    installable: false,
-    args: "--hide-annotation libcore.api.Hide " +
-        "--show-single-annotation libcore.api.IntraCoreApi " +
-        "--skip-annotation-instance-methods=false ",
-    merge_inclusion_annotations_dirs: ["ojluni-annotated-mmodule-stubs"],
+// Referenced implicitly from art.module.intra.core.api.
+filegroup {
+    name: "art.module.intra.core.api-removed.api.public.latest",
+    srcs: [
+        "api/intra/last-removed.txt",
+    ],
+}
 
-    api_filename: "api.txt",
-    removed_api_filename: "removed.txt",
-    previous_api: "previous.txt",
-    check_api: {
-        current: {
-            api_file: "api/intra/current-api.txt",
-            removed_api_file: "api/intra/current-removed.txt",
-        },
-        last_released: {
-            api_file: "api/intra/last-api.txt",
-            removed_api_file: "api/intra/last-removed.txt",
-        },
-    },
+// Referenced implicitly from art.module.intra.core.api.
+filegroup {
+    name: "art.module.intra.core.api-incompatibilities.api.public.latest",
+    srcs: [
+        "api/intra/last-incompatibilities.txt",
+    ],
 }
 
 // A library containing the intra-core API stubs of the ART module.
 //
 // Intra-core APIs are only intended for the use of other core library modules.
-java_library {
-    name: "art.module.intra.core.api.stubs",
+//
+// The API specification .txt files managed by this only contain the additional
+// classes/members that are in the intra-core API but which are not the public API.
+java_sdk_library {
+    name: "art.module.intra.core.api",
     visibility: [
         "//libcore/mmodules/core_platform_api",
     ],
     srcs: [
-        ":art-module-intra-core-api-stubs-source",
-        ":openjdk_lambda_stub_files",
-        ":openjdk_generated_annotation_stub_files",
+        ":art_module_api_files",
     ],
+    api_dir: "api/intra",
+    api_only: true,
+    droiddoc_options: [
+        "--hide-annotation libcore.api.Hide",
+        "--show-single-annotation libcore.api.IntraCoreApi",
+        "--skip-annotation-instance-methods=false",
+    ],
+    merge_inclusion_annotations_dirs: ["ojluni-annotated-mmodule-stubs"],
 
     sdk_version: "none",
     system_modules: "none",
     patch_module: "java.base",
+
+    // Don't copy any output files to the dist.
+    no_dist: true,
+}
+
+// Bootstrap the art-module-intra-core-api-stubs-system-modules.
+//
+// This is needed to build art-module-intra-core-api-stubs-system-modules-lib
+// which is in turn needed to build art-module-intra-core-api-stubs-system-modules
+java_system_modules {
+    name: "art-module-intra-core-api-stubs-bootstrap-system-modules",
+    libs: [
+        // The intra core API stubs library.
+        "art.module.intra.core.api.stubs",
+    ],
+}
+
+// A library containing additional classes that are needed in the system modules.
+java_library {
+    name: "art-module-intra-core-api-stubs-system-modules-lib",
+    srcs: [
+        ":openjdk_lambda_stub_files",
+        ":openjdk_generated_annotation_stub_files",
+    ],
+    libs: [
+        "art.module.intra.core.api",
+    ],
+    system_modules: "art-module-intra-core-api-stubs-bootstrap-system-modules",
+    sdk_version: "none",
+    patch_module: "java.base",
 }
 
 // Used when compiling against art.module.intra.core.api.stubs.
@@ -76,5 +116,11 @@
         "//external/conscrypt",
         "//external/icu/android_icu4j",
     ],
-    libs: ["art.module.intra.core.api.stubs"],
+    libs: [
+        // The intra core API stubs library.
+        "art.module.intra.core.api.stubs",
+
+        // Additional classes needed by javac but which are not present in the stubs.
+        "art-module-intra-core-api-stubs-system-modules-lib",
+    ],
 }
diff --git a/mmodules/intracoreapi/api/intra/current-api.txt b/mmodules/intracoreapi/api/intra/current-api.txt
deleted file mode 100644
index 5137a10..0000000
--- a/mmodules/intracoreapi/api/intra/current-api.txt
+++ /dev/null
@@ -1,151 +0,0 @@
-// Signature format: 2.0
-package android.compat {
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public final class Compatibility {
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static boolean isChangeEnabled(long);
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static void reportChange(long);
-  }
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static final class Compatibility.ChangeConfig {
-    ctor public Compatibility.ChangeConfig(java.util.Set<java.lang.Long>, java.util.Set<java.lang.Long>);
-    method public long[] forceDisabledChangesArray();
-    method public java.util.Set<java.lang.Long> forceDisabledSet();
-    method public long[] forceEnabledChangesArray();
-    method public java.util.Set<java.lang.Long> forceEnabledSet();
-    method public boolean isEmpty();
-    method public boolean isForceDisabled(long);
-    method public boolean isForceEnabled(long);
-  }
-
-}
-
-package dalvik.annotation.compat {
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public class VersionCodes {
-    field @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static final int O = 26; // 0x1a
-    field @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static final int P = 28; // 0x1c
-    field @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static final int Q = 29; // 0x1d
-  }
-
-}
-
-package dalvik.annotation.optimization {
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) @libcore.api.IntraCoreApi public @interface ReachabilitySensitive {
-  }
-
-}
-
-package dalvik.system {
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public final class BlockGuard {
-    method @NonNull @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static dalvik.system.BlockGuard.Policy getThreadPolicy();
-  }
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static interface BlockGuard.Policy {
-    method @libcore.api.IntraCoreApi public void onNetwork();
-  }
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public final class CloseGuard {
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public void close();
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static dalvik.system.CloseGuard get();
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public void open(String);
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public void warnIfOpen();
-  }
-
-}
-
-package java.net {
-
-  public class Socket implements java.io.Closeable {
-    method public java.io.FileDescriptor getFileDescriptor$();
-  }
-
-}
-
-package java.nio.charset {
-
-  public abstract class Charset implements java.lang.Comparable<java.nio.charset.Charset> {
-    ctor @libcore.api.IntraCoreApi protected Charset(String, String[]);
-  }
-
-  public abstract class CharsetDecoder {
-    ctor protected CharsetDecoder(java.nio.charset.Charset, float, float);
-  }
-
-  public abstract class CharsetEncoder {
-    ctor protected CharsetEncoder(java.nio.charset.Charset, float, float, byte[], boolean);
-  }
-
-}
-
-package java.security.spec {
-
-  public class ECParameterSpec implements java.security.spec.AlgorithmParameterSpec {
-    method public String getCurveName();
-    method public void setCurveName(String);
-  }
-
-}
-
-package libcore.api {
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.ANNOTATION_TYPE}) @libcore.api.IntraCoreApi public @interface CorePlatformApi {
-  }
-
-  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.ANNOTATION_TYPE}) @libcore.api.IntraCoreApi public @interface IntraCoreApi {
-  }
-
-}
-
-package libcore.io {
-
-  @libcore.api.IntraCoreApi public final class AsynchronousCloseMonitor {
-    method @libcore.api.IntraCoreApi public static void signalBlockedThreads(java.io.FileDescriptor);
-  }
-
-}
-
-package libcore.net {
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public abstract class NetworkSecurityPolicy {
-    ctor @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public NetworkSecurityPolicy();
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static libcore.net.NetworkSecurityPolicy getInstance();
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public abstract boolean isCertificateTransparencyVerificationRequired(String);
-  }
-
-}
-
-package libcore.util {
-
-  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public class NativeAllocationRegistry {
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static libcore.util.NativeAllocationRegistry createMalloced(ClassLoader, long);
-    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public Runnable registerNativeAllocation(Object, long);
-  }
-
-  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE}) @libcore.api.IntraCoreApi public @interface NonNull {
-  }
-
-  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE}) @libcore.api.IntraCoreApi public @interface Nullable {
-  }
-
-}
-
-package sun.security.util {
-
-  public interface DerEncoder {
-    method public void derEncode(java.io.OutputStream) throws java.io.IOException;
-  }
-
-}
-
-package sun.security.x509 {
-
-  public class AlgorithmId implements sun.security.util.DerEncoder java.io.Serializable {
-    method public void derEncode(java.io.OutputStream) throws java.io.IOException;
-    method public static sun.security.x509.AlgorithmId get(String) throws java.security.NoSuchAlgorithmException;
-    method public String getName();
-  }
-
-}
-
diff --git a/mmodules/intracoreapi/api/intra/current.txt b/mmodules/intracoreapi/api/intra/current.txt
new file mode 100644
index 0000000..7e813f9
--- /dev/null
+++ b/mmodules/intracoreapi/api/intra/current.txt
@@ -0,0 +1,169 @@
+// Signature format: 2.0
+package android.compat {
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public final class Compatibility {
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static boolean isChangeEnabled(long);
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static void reportUnconditionalChange(long);
+  }
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static final class Compatibility.ChangeConfig {
+    ctor @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public Compatibility.ChangeConfig(@NonNull java.util.Set<java.lang.Long>, @NonNull java.util.Set<java.lang.Long>);
+    method @NonNull @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public long[] getDisabledChangesArray();
+    method @NonNull @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public java.util.Set<java.lang.Long> getDisabledSet();
+    method @NonNull @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public long[] getEnabledChangesArray();
+    method @NonNull @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public java.util.Set<java.lang.Long> getEnabledSet();
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public boolean isEmpty();
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public boolean isForceDisabled(long);
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public boolean isForceEnabled(long);
+  }
+
+}
+
+package android.system {
+
+  public final class Os {
+    method @Nullable @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static String realpath(@Nullable String) throws android.system.ErrnoException;
+  }
+
+}
+
+package dalvik.annotation.compat {
+
+  @libcore.api.IntraCoreApi public class VersionCodes {
+    field @libcore.api.IntraCoreApi public static final int CUR_DEVELOPMENT = 10000; // 0x2710
+    field @libcore.api.IntraCoreApi public static final int O = 26; // 0x1a
+    field @libcore.api.IntraCoreApi public static final int P = 28; // 0x1c
+    field @libcore.api.IntraCoreApi public static final int Q = 29; // 0x1d
+    field @libcore.api.IntraCoreApi public static final int R = 30; // 0x1e
+    field @libcore.api.IntraCoreApi public static final int S = 31; // 0x1f
+  }
+
+}
+
+package dalvik.annotation.optimization {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public @interface FastNative {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) @libcore.api.IntraCoreApi public @interface ReachabilitySensitive {
+  }
+
+}
+
+package dalvik.system {
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public final class BlockGuard {
+    method @NonNull @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static dalvik.system.BlockGuard.Policy getThreadPolicy();
+  }
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static interface BlockGuard.Policy {
+    method @libcore.api.IntraCoreApi public void onNetwork();
+  }
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public final class CloseGuard {
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public void close();
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static dalvik.system.CloseGuard get();
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public void open(String);
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public void warnIfOpen();
+  }
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public final class VMRuntime {
+    method @dalvik.annotation.optimization.FastNative @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public long addressOf(Object);
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static dalvik.system.VMRuntime getRuntime();
+    method @dalvik.annotation.optimization.FastNative @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public Object newNonMovableArray(Class<?>, int);
+  }
+
+}
+
+package java.net {
+
+  public class Socket implements java.io.Closeable {
+    method @libcore.api.IntraCoreApi public java.io.FileDescriptor getFileDescriptor$();
+  }
+
+}
+
+package java.nio.charset {
+
+  public abstract class CharsetEncoder {
+    ctor @libcore.api.IntraCoreApi protected CharsetEncoder(java.nio.charset.Charset, float, float, byte[], boolean);
+  }
+
+}
+
+package java.security.spec {
+
+  public class ECParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+    method @libcore.api.IntraCoreApi public String getCurveName();
+    method @libcore.api.IntraCoreApi public void setCurveName(String);
+  }
+
+}
+
+package libcore.api {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.ANNOTATION_TYPE}) @libcore.api.IntraCoreApi public @interface CorePlatformApi {
+    method @libcore.api.IntraCoreApi public abstract libcore.api.CorePlatformApi.Status status() default libcore.api.CorePlatformApi.Status.LEGACY_ONLY;
+  }
+
+  @libcore.api.IntraCoreApi public enum CorePlatformApi.Status {
+    enum_constant @libcore.api.IntraCoreApi public static final libcore.api.CorePlatformApi.Status LEGACY_ONLY;
+    enum_constant @libcore.api.IntraCoreApi public static final libcore.api.CorePlatformApi.Status STABLE;
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.ANNOTATION_TYPE}) @libcore.api.IntraCoreApi public @interface IntraCoreApi {
+  }
+
+}
+
+package libcore.io {
+
+  @libcore.api.IntraCoreApi public final class AsynchronousCloseMonitor {
+    method @libcore.api.IntraCoreApi public static void signalBlockedThreads(java.io.FileDescriptor);
+  }
+
+}
+
+package libcore.net {
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public abstract class NetworkSecurityPolicy {
+    ctor @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public NetworkSecurityPolicy();
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static libcore.net.NetworkSecurityPolicy getInstance();
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public abstract boolean isCertificateTransparencyVerificationRequired(String);
+  }
+
+}
+
+package libcore.util {
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public class NativeAllocationRegistry {
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public static libcore.util.NativeAllocationRegistry createMalloced(@NonNull ClassLoader, long);
+    method @NonNull @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public Runnable registerNativeAllocation(@NonNull Object, long);
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE}) @libcore.api.IntraCoreApi public @interface NonNull {
+  }
+
+  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE}) @libcore.api.IntraCoreApi public @interface Nullable {
+  }
+
+}
+
+package sun.security.util {
+
+  @libcore.api.IntraCoreApi public interface DerEncoder {
+    method @libcore.api.IntraCoreApi public void derEncode(java.io.OutputStream) throws java.io.IOException;
+  }
+
+}
+
+package sun.security.x509 {
+
+  @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public class AlgorithmId implements sun.security.util.DerEncoder java.io.Serializable {
+    method @libcore.api.IntraCoreApi public void derEncode(java.io.OutputStream) throws java.io.IOException;
+    method @libcore.api.IntraCoreApi public static sun.security.x509.AlgorithmId get(String) throws java.security.NoSuchAlgorithmException;
+    method @libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE) @libcore.api.IntraCoreApi public String getName();
+  }
+
+}
+
diff --git a/mmodules/core_platform_api/api/platform/last-api.txt b/mmodules/intracoreapi/api/intra/last-incompatibilities.txt
similarity index 100%
copy from mmodules/core_platform_api/api/platform/last-api.txt
copy to mmodules/intracoreapi/api/intra/last-incompatibilities.txt
diff --git a/mmodules/intracoreapi/api/intra/current-removed.txt b/mmodules/intracoreapi/api/intra/removed.txt
similarity index 100%
rename from mmodules/intracoreapi/api/intra/current-removed.txt
rename to mmodules/intracoreapi/api/intra/removed.txt
diff --git a/non_openjdk_java_files.bp b/non_openjdk_java_files.bp
old mode 100644
new mode 100755
index e3c8c16..59a78e3
--- a/non_openjdk_java_files.bp
+++ b/non_openjdk_java_files.bp
@@ -32,11 +32,10 @@
         "dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java",
         "dalvik/src/main/java/dalvik/bytecode/Opcodes.java",
         "dalvik/src/main/java/dalvik/system/AllocationLimitError.java",
+        "dalvik/src/main/java/dalvik/system/AppSpecializationHooks.java",
         "dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java",
         "dalvik/src/main/java/dalvik/system/BlockGuard.java",
         "dalvik/src/main/java/dalvik/system/CloseGuard.java",
-        "dalvik/src/main/java/dalvik/system/DalvikLogHandler.java",
-        "dalvik/src/main/java/dalvik/system/DalvikLogging.java",
         "dalvik/src/main/java/dalvik/system/DelegateLastClassLoader.java",
         "dalvik/src/main/java/dalvik/system/DexClassLoader.java",
         "dalvik/src/main/java/dalvik/system/DexFile.java",
@@ -49,10 +48,8 @@
         "dalvik/src/main/java/dalvik/system/RuntimeHooks.java",
         "dalvik/src/main/java/dalvik/system/SocketTagger.java",
         "dalvik/src/main/java/dalvik/system/TemporaryDirectory.java",
-        "dalvik/src/main/java/dalvik/system/ThreadPrioritySetter.java",
         "dalvik/src/main/java/dalvik/system/VMDebug.java",
         "dalvik/src/main/java/dalvik/system/ZygoteHooks.java",
-        "dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java",
         "dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/Chunk.java",
         "dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/ChunkHandler.java",
         "dalvik/src/main/java/org/apache/harmony/dalvik/ddmc/DdmServer.java",
@@ -143,9 +140,11 @@
         "luni/src/main/java/android/system/StructAddrinfo.java",
         "luni/src/main/java/android/system/StructCapUserData.java",
         "luni/src/main/java/android/system/StructCapUserHeader.java",
+        "luni/src/main/java/android/system/StructCmsghdr.java",
         "luni/src/main/java/android/system/StructGroupReq.java",
         "luni/src/main/java/android/system/StructIfaddrs.java",
         "luni/src/main/java/android/system/StructLinger.java",
+        "luni/src/main/java/android/system/StructMsghdr.java",
         "luni/src/main/java/android/system/StructPasswd.java",
         "luni/src/main/java/android/system/StructPollfd.java",
         "luni/src/main/java/android/system/StructRlimit.java",
@@ -156,20 +155,9 @@
         "luni/src/main/java/android/system/StructUcred.java",
         "luni/src/main/java/android/system/StructUtsname.java",
         "luni/src/main/java/android/system/UnixSocketAddress.java",
+        "luni/src/main/java/android/system/VmSocketAddress.java",
         "luni/src/main/java/java/lang/FindBugsSuppressWarnings.java",
         "luni/src/main/java/java/lang/ref/FinalizerReference.java",
-        "luni/src/main/java/java/math/BigDecimal.java",
-        "luni/src/main/java/java/math/BigInt.java",
-        "luni/src/main/java/java/math/BigInteger.java",
-        "luni/src/main/java/java/math/BitLevel.java",
-        "luni/src/main/java/java/math/Conversion.java",
-        "luni/src/main/java/java/math/Division.java",
-        "luni/src/main/java/java/math/Logical.java",
-        "luni/src/main/java/java/math/MathContext.java",
-        "luni/src/main/java/java/math/Multiplication.java",
-        "luni/src/main/java/java/math/NativeBN.java",
-        "luni/src/main/java/java/math/Primality.java",
-        "luni/src/main/java/java/math/RoundingMode.java",
         "luni/src/main/java/java/net/DefaultFileNameMap.java",
         "luni/src/main/java/java/nio/NIOAccess.java",
         "luni/src/main/java/java/nio/NioUtils.java",
@@ -235,9 +223,7 @@
         "luni/src/main/java/libcore/icu/DateIntervalFormat.java",
         "luni/src/main/java/libcore/icu/ICU.java",
         "luni/src/main/java/libcore/icu/LocaleData.java",
-        "luni/src/main/java/libcore/icu/RelativeDateTimeFormatter.java",
         "luni/src/main/java/libcore/icu/TimeZoneNames.java",
-        "luni/src/main/java/libcore/internal/StringPool.java",
         "luni/src/main/java/libcore/io/AsynchronousCloseMonitor.java",
         "luni/src/main/java/libcore/io/ForwardingOs.java",
         "luni/src/main/java/libcore/io/IoBridge.java",
@@ -249,20 +235,10 @@
         "luni/src/main/java/libcore/net/InetAddressUtils.java",
         "luni/src/main/java/libcore/net/NetworkSecurityPolicy.java",
         "luni/src/main/java/libcore/net/event/NetworkEventDispatcher.java",
-        "luni/src/main/java/libcore/timezone/CountryTimeZones.java",
-        "luni/src/main/java/libcore/timezone/CountryZonesFinder.java",
-        "luni/src/main/java/libcore/timezone/TelephonyLookup.java",
-        "luni/src/main/java/libcore/timezone/TelephonyNetwork.java",
-        "luni/src/main/java/libcore/timezone/TelephonyNetworkFinder.java",
-        "luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java",
-        "luni/src/main/java/libcore/timezone/TimeZoneFinder.java",
-        "luni/src/main/java/libcore/timezone/TzDataSetVersion.java",
-        "luni/src/main/java/libcore/timezone/XmlUtils.java",
-        "luni/src/main/java/libcore/timezone/ZoneInfoDb.java",
+        "luni/src/main/java/libcore/net/http/Dns.java",
+        "luni/src/main/java/libcore/net/http/HttpURLConnectionFactory.java",
         "luni/src/main/java/libcore/util/ArrayUtils.java",
         "luni/src/main/java/libcore/util/BasicLruCache.java",
-        "luni/src/main/java/libcore/util/CoreLibraryDebug.java",
-        "luni/src/main/java/libcore/util/DebugInfo.java",
         "luni/src/main/java/libcore/util/EmptyArray.java",
         "luni/src/main/java/libcore/util/FP16.java",
         "luni/src/main/java/libcore/util/HexEncoding.java",
@@ -374,9 +350,9 @@
     srcs: [
         "luni/src/main/java/java/net/AddressCache.java",
         "luni/src/main/java/libcore/icu/CollationKeyICU.java",
-        "luni/src/main/java/libcore/icu/DateTimeFormat.java",
         "luni/src/main/java/libcore/icu/DateUtilsBridge.java",
         "luni/src/main/java/libcore/internal/Java9LanguageFeatures.java",
+        "luni/src/main/java/libcore/internal/StringPool.java",
         "luni/src/main/java/libcore/io/ClassPathURLStreamHandler.java",
         "luni/src/main/java/libcore/io/BlockGuardOs.java",
         "luni/src/main/java/libcore/io/BufferIterator.java",
@@ -385,6 +361,7 @@
         "luni/src/main/java/libcore/io/MemoryMappedFile.java",
         "luni/src/main/java/libcore/io/NioBufferIterator.java",
         "luni/src/main/java/libcore/math/MathUtils.java",
+        "luni/src/main/java/libcore/math/NativeBN.java",
         "luni/src/main/java/libcore/net/event/NetworkEventListener.java",
         "luni/src/main/java/libcore/net/http/HttpDate.java",
         "luni/src/main/java/libcore/reflect/AnnotatedElements.java",
@@ -438,15 +415,3 @@
         ":non_openjdk_javadoc_files",
     ],
 }
-
-// timezone-related source that is also used in host tests / tools and its
-// dependencies.
-filegroup {
-    name: "timezone_host_files",
-    srcs: [
-        "luni/src/main/java/libcore/api/CorePlatformApi.java",
-        "luni/src/main/java/libcore/api/IntraCoreApi.java",
-        "luni/src/main/java/libcore/timezone/TimeZoneDataFiles.java",
-        "luni/src/main/java/libcore/timezone/TzDataSetVersion.java",
-    ],
-}
diff --git a/ojluni/Android.bp b/ojluni/Android.bp
index 20a5a5a..4c9e703 100644
--- a/ojluni/Android.bp
+++ b/ojluni/Android.bp
@@ -12,6 +12,38 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["libcore_ojluni_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'filegroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// http://go/android-license-faq
+license {
+    name: "libcore_ojluni_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+        "SPDX-license-identifier-GPL-2.0",
+        "SPDX-license-identifier-GPL-with-classpath-exception",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 // Phony target that causes the build to check the license file in this
 // directory, detect that it is a GPL license and then copy all the files
 // from this directory and its subdirectories in to the
@@ -21,6 +53,6 @@
 
     // A phony module must have at least one dependency.
     required: [
-        "core-oj",
+        "core-all",
     ],
 }
diff --git a/ojluni/annotations/README b/ojluni/annotations/README
index e88e233..15d9738 100644
--- a/ojluni/annotations/README
+++ b/ojluni/annotations/README
@@ -31,8 +31,8 @@
       check in this change, it will have undesirable consequences until you
       reach step 4, below.
    2. make openjdk-mmodule-stubs-no-javadoc
-   3. FILES="your/package/and/Class.java another/package/AnotherClass.java"; for FILE in $FILES; do mkdir -p libcore/ojluni/annotations/mmodule/$(dirname ${FILE}) && cp out/soong/.intermediates/libcore/openjdk-mmodule-stubs-no-javadoc/android_common/stubsDir/${FILE} libcore/ojluni/annotations/mmodule/${FILE/%.java/.annotated.java}; done
-   4. Add @libcore.api.Hide to each top-level class in the new files. This will prevent it from being added to the public API. A JavaDoc @hide tag will not work as metalava will ignore javadoc from the stub files.
+   3. FILES="your/package/and/Class.java another/package/AnotherClass.java"; for FILE in $FILES; do mkdir -p libcore/ojluni/annotations/mmodule/$(dirname ${FILE}) && unzip out/soong/.intermediates/libcore/openjdk-mmodule-stubs-no-javadoc/android_common/metalava/openjdk-mmodule-stubs-no-javadoc-stubs.srcjar ${FILE} && mv ${FILE} libcore/ojluni/annotations/mmodule/${FILE/%.java/.annotated.java}; done
+   4. Add @libcore.api.Hide to each class, methods and fields in the new files. This will prevent it from being added to the public API. A JavaDoc @hide tag will not work as metalava will ignore javadoc from the stub files.
    5. Add @libcore.api.CorePlatformApi and @libcore.api.IntraCoreApi as desired
       to the classes and members.
  - To see the effect of the files under this directory:
diff --git a/ojluni/annotations/hiddenapi/java/io/Console.java b/ojluni/annotations/hiddenapi/java/io/Console.java
index 24039a1..2f74f97 100644
--- a/ojluni/annotations/hiddenapi/java/io/Console.java
+++ b/ojluni/annotations/hiddenapi/java/io/Console.java
@@ -75,7 +75,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static native java.lang.String encoding();
 
     private static native boolean echo(boolean on) throws java.io.IOException;
diff --git a/ojluni/annotations/hiddenapi/java/io/File.java b/ojluni/annotations/hiddenapi/java/io/File.java
index bdcce88..3a6a78d 100644
--- a/ojluni/annotations/hiddenapi/java/io/File.java
+++ b/ojluni/annotations/hiddenapi/java/io/File.java
@@ -298,7 +298,7 @@
         UNSAFE = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private transient volatile java.nio.file.Path filePath;
 
     @UnsupportedAppUsage
@@ -308,7 +308,7 @@
         fs = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.lang.String path;
 
     {
@@ -327,7 +327,7 @@
         pathSeparatorChar = 0;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final transient int prefixLength;
 
     {
@@ -348,7 +348,7 @@
 
     private static final long serialVersionUID = 301077366599181567L; // 0x42da4450e0de4ffL
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private transient java.io.File.PathStatus status;
 
     @SuppressWarnings({"unchecked", "deprecation", "all"})
diff --git a/ojluni/annotations/hiddenapi/java/io/FileDescriptor.java b/ojluni/annotations/hiddenapi/java/io/FileDescriptor.java
index 6e9e74d..00c0915 100644
--- a/ojluni/annotations/hiddenapi/java/io/FileDescriptor.java
+++ b/ojluni/annotations/hiddenapi/java/io/FileDescriptor.java
@@ -66,7 +66,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public boolean isSocket$() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/io/FileInputStream.java b/ojluni/annotations/hiddenapi/java/io/FileInputStream.java
index 1999d13..5f2bdd4 100644
--- a/ojluni/annotations/hiddenapi/java/io/FileInputStream.java
+++ b/ojluni/annotations/hiddenapi/java/io/FileInputStream.java
@@ -104,7 +104,7 @@
 
     private volatile boolean closed = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.io.FileDescriptor fd;
 
     {
diff --git a/ojluni/annotations/hiddenapi/java/io/FileOutputStream.java b/ojluni/annotations/hiddenapi/java/io/FileOutputStream.java
index 5e35fc0..69b0dc9 100644
--- a/ojluni/annotations/hiddenapi/java/io/FileOutputStream.java
+++ b/ojluni/annotations/hiddenapi/java/io/FileOutputStream.java
@@ -109,7 +109,7 @@
 
     private volatile boolean closed = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.io.FileDescriptor fd;
 
     {
diff --git a/ojluni/annotations/hiddenapi/java/io/ObjectOutputStream.java b/ojluni/annotations/hiddenapi/java/io/ObjectOutputStream.java
index 1e429a4..25b28337 100644
--- a/ojluni/annotations/hiddenapi/java/io/ObjectOutputStream.java
+++ b/ojluni/annotations/hiddenapi/java/io/ObjectOutputStream.java
@@ -300,7 +300,7 @@
 
     private byte[] primVals;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int protocol = 2; // 0x2
 
     private final java.io.ObjectOutputStream.ReplaceTable subs;
diff --git a/ojluni/annotations/hiddenapi/java/io/RandomAccessFile.java b/ojluni/annotations/hiddenapi/java/io/RandomAccessFile.java
index 5bc7d69..a4b6a96 100644
--- a/ojluni/annotations/hiddenapi/java/io/RandomAccessFile.java
+++ b/ojluni/annotations/hiddenapi/java/io/RandomAccessFile.java
@@ -225,7 +225,7 @@
 
     private volatile boolean closed = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.io.FileDescriptor fd;
 
     private int flushAfterWrite = 0; // 0x0
diff --git a/ojluni/annotations/hiddenapi/java/lang/Class.java b/ojluni/annotations/hiddenapi/java/lang/Class.java
index a9c1515..30b28ed 100644
--- a/ojluni/annotations/hiddenapi/java/lang/Class.java
+++ b/ojluni/annotations/hiddenapi/java/lang/Class.java
@@ -421,7 +421,7 @@
 
     private transient int classSize;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private transient int clinitThreadId;
 
     private transient java.lang.Class<?> componentType;
@@ -465,7 +465,7 @@
 
     private static final long serialVersionUID = 3206093459760846163L; // 0x2c7e5503d9bf9553L
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private transient int status;
 
     private transient java.lang.Class<? super T> superClass;
diff --git a/ojluni/annotations/hiddenapi/java/lang/Enum.java b/ojluni/annotations/hiddenapi/java/lang/Enum.java
index 8cd1b6d..06b3864 100644
--- a/ojluni/annotations/hiddenapi/java/lang/Enum.java
+++ b/ojluni/annotations/hiddenapi/java/lang/Enum.java
@@ -77,7 +77,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static <T extends java.lang.Enum<T>> T[] getSharedConstants(
             java.lang.Class<T> enumType) {
         throw new RuntimeException("Stub!");
@@ -96,14 +96,14 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.lang.String name;
 
     {
         name = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final int ordinal;
 
     {
diff --git a/ojluni/annotations/hiddenapi/java/lang/Runtime.java b/ojluni/annotations/hiddenapi/java/lang/Runtime.java
index 798967f..dab7708 100644
--- a/ojluni/annotations/hiddenapi/java/lang/Runtime.java
+++ b/ojluni/annotations/hiddenapi/java/lang/Runtime.java
@@ -31,7 +31,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class Runtime {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private Runtime() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/lang/String.java b/ojluni/annotations/hiddenapi/java/lang/String.java
index a7b137a..e3a264b 100644
--- a/ojluni/annotations/hiddenapi/java/lang/String.java
+++ b/ojluni/annotations/hiddenapi/java/lang/String.java
@@ -138,7 +138,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     native void getCharsNoCheck(int start, int end, char[] buffer, int index);
 
     @Deprecated
@@ -255,7 +255,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     static int indexOf(
             char[] source,
             int sourceOffset,
@@ -289,7 +289,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     static int lastIndexOf(
             char[] source,
             int sourceOffset,
@@ -457,7 +457,7 @@
         count = 0;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int hash;
 
     private static final java.io.ObjectStreamField[] serialPersistentFields;
diff --git a/ojluni/annotations/hiddenapi/java/lang/System.java b/ojluni/annotations/hiddenapi/java/lang/System.java
index 407473b..95a2ed2 100644
--- a/ojluni/annotations/hiddenapi/java/lang/System.java
+++ b/ojluni/annotations/hiddenapi/java/lang/System.java
@@ -31,7 +31,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class System {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private System() {
         throw new RuntimeException("Stub!");
     }
@@ -271,7 +271,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static native void log(char type, java.lang.String message, java.lang.Throwable th);
 
     private static final int ARRAYCOPY_SHORT_BOOLEAN_ARRAY_THRESHOLD = 32; // 0x20
diff --git a/ojluni/annotations/hiddenapi/java/lang/Thread.java b/ojluni/annotations/hiddenapi/java/lang/Thread.java
index 36ca327..235743c 100644
--- a/ojluni/annotations/hiddenapi/java/lang/Thread.java
+++ b/ojluni/annotations/hiddenapi/java/lang/Thread.java
@@ -55,7 +55,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     Thread(java.lang.ThreadGroup group, java.lang.String name, int priority, boolean daemon) {
         throw new RuntimeException("Stub!");
     }
@@ -306,7 +306,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public final void dispatchUncaughtException(java.lang.Throwable e) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/lang/ThreadGroup.java b/ojluni/annotations/hiddenapi/java/lang/ThreadGroup.java
index bc69033..da33ff9 100644
--- a/ojluni/annotations/hiddenapi/java/lang/ThreadGroup.java
+++ b/ojluni/annotations/hiddenapi/java/lang/ThreadGroup.java
@@ -159,7 +159,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void add(java.lang.Thread t) {
         throw new RuntimeException("Stub!");
     }
@@ -168,7 +168,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void threadTerminated(java.lang.Thread t) {
         throw new RuntimeException("Stub!");
     }
@@ -205,7 +205,7 @@
     @UnsupportedAppUsage
     java.lang.ThreadGroup[] groups;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     static final java.lang.ThreadGroup mainThreadGroup;
 
     static {
@@ -216,7 +216,7 @@
 
     int nUnstartedThreads = 0; // 0x0
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     java.lang.String name;
 
     @UnsupportedAppUsage
diff --git a/ojluni/annotations/hiddenapi/java/lang/ThreadLocal.java b/ojluni/annotations/hiddenapi/java/lang/ThreadLocal.java
index ca5927b..8d8791a 100644
--- a/ojluni/annotations/hiddenapi/java/lang/ThreadLocal.java
+++ b/ojluni/annotations/hiddenapi/java/lang/ThreadLocal.java
@@ -63,7 +63,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     java.lang.ThreadLocal.ThreadLocalMap getMap(java.lang.Thread t) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/lang/Throwable.java b/ojluni/annotations/hiddenapi/java/lang/Throwable.java
index 00a3b2c..6dfb2ed 100644
--- a/ojluni/annotations/hiddenapi/java/lang/Throwable.java
+++ b/ojluni/annotations/hiddenapi/java/lang/Throwable.java
@@ -105,7 +105,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static native java.lang.Object nativeFillInStackTrace();
 
     public java.lang.StackTraceElement[] getStackTrace() {
@@ -166,7 +166,7 @@
     @UnsupportedAppUsage
     private java.lang.StackTraceElement[] stackTrace;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.util.List<java.lang.Throwable> suppressedExceptions;
 
     @SuppressWarnings({"unchecked", "deprecation", "all"})
diff --git a/ojluni/annotations/hiddenapi/java/lang/invoke/SerializedLambda.java b/ojluni/annotations/hiddenapi/java/lang/invoke/SerializedLambda.java
index fd92ce4..81cf355 100644
--- a/ojluni/annotations/hiddenapi/java/lang/invoke/SerializedLambda.java
+++ b/ojluni/annotations/hiddenapi/java/lang/invoke/SerializedLambda.java
@@ -29,7 +29,7 @@
 
 public final class SerializedLambda implements Serializable {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public SerializedLambda(Class<?> capturingClass,
                             String functionalInterfaceClass,
                             String functionalInterfaceMethodName,
@@ -41,7 +41,7 @@
                             String instantiatedMethodType,
                             Object[] capturedArgs) { }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public String getCapturingClass() {
         throw new RuntimeException("Stub!");
     }
@@ -81,12 +81,12 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public final String getInstantiatedMethodType() {
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public int getCapturedArgCount() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/lang/ref/Reference.java b/ojluni/annotations/hiddenapi/java/lang/ref/Reference.java
index 74359c1..41d6897 100644
--- a/ojluni/annotations/hiddenapi/java/lang/ref/Reference.java
+++ b/ojluni/annotations/hiddenapi/java/lang/ref/Reference.java
@@ -43,7 +43,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final native T getReferent();
 
     public void clear() {
diff --git a/ojluni/annotations/hiddenapi/java/lang/ref/ReferenceQueue.java b/ojluni/annotations/hiddenapi/java/lang/ref/ReferenceQueue.java
index e123068..52d82d8 100644
--- a/ojluni/annotations/hiddenapi/java/lang/ref/ReferenceQueue.java
+++ b/ojluni/annotations/hiddenapi/java/lang/ref/ReferenceQueue.java
@@ -68,7 +68,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     static void add(java.lang.ref.Reference<?> list) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/lang/reflect/Constructor.java b/ojluni/annotations/hiddenapi/java/lang/reflect/Constructor.java
index 5accc6a..ad0cf99 100644
--- a/ojluni/annotations/hiddenapi/java/lang/reflect/Constructor.java
+++ b/ojluni/annotations/hiddenapi/java/lang/reflect/Constructor.java
@@ -40,7 +40,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.lang.reflect.Constructor<T> serializationCopy(
             java.lang.Class<?> ctor, java.lang.Class<?> cl) {
         throw new RuntimeException("Stub!");
diff --git a/ojluni/annotations/hiddenapi/java/lang/reflect/Parameter.java b/ojluni/annotations/hiddenapi/java/lang/reflect/Parameter.java
index 9982658..5795b8a 100644
--- a/ojluni/annotations/hiddenapi/java/lang/reflect/Parameter.java
+++ b/ojluni/annotations/hiddenapi/java/lang/reflect/Parameter.java
@@ -30,7 +30,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class Parameter implements java.lang.reflect.AnnotatedElement {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     Parameter(
             java.lang.String name,
             int modifiers,
diff --git a/ojluni/annotations/hiddenapi/java/lang/reflect/Proxy.java b/ojluni/annotations/hiddenapi/java/lang/reflect/Proxy.java
index 593de12..dc835bf 100644
--- a/ojluni/annotations/hiddenapi/java/lang/reflect/Proxy.java
+++ b/ojluni/annotations/hiddenapi/java/lang/reflect/Proxy.java
@@ -98,7 +98,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static java.lang.Object invoke(
             java.lang.reflect.Proxy proxy, java.lang.reflect.Method method, java.lang.Object[] args)
             throws java.lang.Throwable {
diff --git a/ojluni/annotations/hiddenapi/java/net/HttpCookie.java b/ojluni/annotations/hiddenapi/java/net/HttpCookie.java
index 97189e4..bc80525 100644
--- a/ojluni/annotations/hiddenapi/java/net/HttpCookie.java
+++ b/ojluni/annotations/hiddenapi/java/net/HttpCookie.java
@@ -232,7 +232,7 @@
 
     private static final java.lang.String SET_COOKIE2 = "set-cookie2:";
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     static final java.util.Map<java.lang.String, java.net.HttpCookie.CookieAttributeAssignor>
             assignors;
 
@@ -240,16 +240,16 @@
         assignors = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.lang.String comment;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.lang.String commentURL;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.lang.String domain;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.lang.String header;
 
     {
@@ -260,35 +260,35 @@
         publicAlternatives = "Use {@link #setHttpOnly()}/{@link #isHttpOnly()} instead.")
     private boolean httpOnly;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private long maxAge = -1L; // 0xffffffffffffffffL
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.lang.String name;
 
     {
         name = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.lang.String path;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.lang.String portlist;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean secure;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean toDiscard;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static final java.lang.String tspecials = ",;= \t";
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.lang.String value;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int version = 1; // 0x1
 
     @UnsupportedAppUsage
diff --git a/ojluni/annotations/hiddenapi/java/net/Inet4Address.java b/ojluni/annotations/hiddenapi/java/net/Inet4Address.java
index b8f60cb..6f04e54 100644
--- a/ojluni/annotations/hiddenapi/java/net/Inet4Address.java
+++ b/ojluni/annotations/hiddenapi/java/net/Inet4Address.java
@@ -31,7 +31,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class Inet4Address extends java.net.InetAddress {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     Inet4Address() {
         throw new RuntimeException("Stub!");
     }
@@ -108,14 +108,14 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final java.net.InetAddress ALL;
 
     static {
         ALL = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final java.net.InetAddress ANY;
 
     static {
diff --git a/ojluni/annotations/hiddenapi/java/net/Inet6Address.java b/ojluni/annotations/hiddenapi/java/net/Inet6Address.java
index fc48ebc..a0fe9d8 100644
--- a/ojluni/annotations/hiddenapi/java/net/Inet6Address.java
+++ b/ojluni/annotations/hiddenapi/java/net/Inet6Address.java
@@ -31,7 +31,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class Inet6Address extends java.net.InetAddress {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     Inet6Address() {
         throw new RuntimeException("Stub!");
     }
@@ -177,7 +177,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final java.net.InetAddress ANY;
 
     static {
@@ -206,7 +206,7 @@
         UNSAFE = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final transient java.net.Inet6Address.Inet6AddressHolder holder6;
 
     {
@@ -301,16 +301,16 @@
             throw new RuntimeException("Stub!");
         }
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         byte[] ipaddress;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         int scope_id;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         boolean scope_id_set;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         java.net.NetworkInterface scope_ifname;
 
         boolean scope_ifname_set;
diff --git a/ojluni/annotations/hiddenapi/java/net/InetAddress.java b/ojluni/annotations/hiddenapi/java/net/InetAddress.java
index 2be05b7..4951374 100644
--- a/ojluni/annotations/hiddenapi/java/net/InetAddress.java
+++ b/ojluni/annotations/hiddenapi/java/net/InetAddress.java
@@ -182,7 +182,7 @@
     }
 
     @UnsupportedAppUsage(maxTargetSdk = VersionCodes.P, trackingBug = 78686891,
-        publicAlternatives = "Use {@link android.net.InetAddresses#isNumericAddress} "
+        publicAlternatives = "Use {@code android.net.InetAddresses#isNumericAddress} "
         + "instead. There is a behavioural difference between the original method and its "
         + "replacement.")
     public static boolean isNumeric(java.lang.String address) {
@@ -202,7 +202,7 @@
      * @deprecated Use {@code android.net.InetAddresses.parseNumericAddress(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = VersionCodes.P, trackingBug = 78686891,
-        publicAlternatives = "Use {@link android.net.InetAddresses#parseNumericAddress} "
+        publicAlternatives = "Use {@code android.net.InetAddresses#parseNumericAddress} "
         + "instead. There is a behavioural difference between the original method and its "
         + "replacement.")
     public static java.net.InetAddress parseNumericAddress(java.lang.String numericAddress) {
@@ -289,16 +289,16 @@
             throw new RuntimeException("Stub!");
         }
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         int address;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         int family;
 
         @UnsupportedAppUsage
         java.lang.String hostName;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         java.lang.String originalHostName;
     }
 }
diff --git a/ojluni/annotations/hiddenapi/java/net/InterfaceAddress.java b/ojluni/annotations/hiddenapi/java/net/InterfaceAddress.java
index 84d3537..fbd408e 100644
--- a/ojluni/annotations/hiddenapi/java/net/InterfaceAddress.java
+++ b/ojluni/annotations/hiddenapi/java/net/InterfaceAddress.java
@@ -30,7 +30,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class InterfaceAddress {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     InterfaceAddress() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/net/Proxy.java b/ojluni/annotations/hiddenapi/java/net/Proxy.java
index ff91f35..4ce14a3 100644
--- a/ojluni/annotations/hiddenapi/java/net/Proxy.java
+++ b/ojluni/annotations/hiddenapi/java/net/Proxy.java
@@ -30,7 +30,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class Proxy {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private Proxy() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/net/ServerSocket.java b/ojluni/annotations/hiddenapi/java/net/ServerSocket.java
index ad0788a..71b137e 100644
--- a/ojluni/annotations/hiddenapi/java/net/ServerSocket.java
+++ b/ojluni/annotations/hiddenapi/java/net/ServerSocket.java
@@ -168,7 +168,7 @@
 
     private boolean created = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static java.net.SocketImplFactory factory;
 
     private java.net.SocketImpl impl;
diff --git a/ojluni/annotations/hiddenapi/java/net/Socket.java b/ojluni/annotations/hiddenapi/java/net/Socket.java
index 495d134..822dd65 100644
--- a/ojluni/annotations/hiddenapi/java/net/Socket.java
+++ b/ojluni/annotations/hiddenapi/java/net/Socket.java
@@ -306,7 +306,7 @@
 
     private boolean created = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static java.net.SocketImplFactory factory;
 
     @UnsupportedAppUsage
diff --git a/ojluni/annotations/hiddenapi/java/net/URI.java b/ojluni/annotations/hiddenapi/java/net/URI.java
index ec7e242..99ab372 100644
--- a/ojluni/annotations/hiddenapi/java/net/URI.java
+++ b/ojluni/annotations/hiddenapi/java/net/URI.java
@@ -666,7 +666,7 @@
     @UnsupportedAppUsage
     private transient int port = -1; // 0xffffffff
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private transient java.lang.String query;
 
     private transient java.lang.String scheme;
diff --git a/ojluni/annotations/hiddenapi/java/net/URL.java b/ojluni/annotations/hiddenapi/java/net/URL.java
index 9ba6358..7f3857f 100644
--- a/ojluni/annotations/hiddenapi/java/net/URL.java
+++ b/ojluni/annotations/hiddenapi/java/net/URL.java
@@ -257,7 +257,7 @@
 
     private int port = -1; // 0xffffffff
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.lang.String protocol;
 
     private static final java.lang.String protocolPathProp = "java.protocol.handler.pkgs";
diff --git a/ojluni/annotations/hiddenapi/java/nio/Buffer.java b/ojluni/annotations/hiddenapi/java/nio/Buffer.java
index fa8b31b..e135277 100644
--- a/ojluni/annotations/hiddenapi/java/nio/Buffer.java
+++ b/ojluni/annotations/hiddenapi/java/nio/Buffer.java
@@ -149,7 +149,7 @@
     @UnsupportedAppUsage
     long address;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int capacity;
 
     @UnsupportedAppUsage
diff --git a/ojluni/annotations/hiddenapi/java/nio/ByteBuffer.java b/ojluni/annotations/hiddenapi/java/nio/ByteBuffer.java
index 32f53a4..bd5718d 100644
--- a/ojluni/annotations/hiddenapi/java/nio/ByteBuffer.java
+++ b/ojluni/annotations/hiddenapi/java/nio/ByteBuffer.java
@@ -264,19 +264,19 @@
 
     boolean bigEndian = true;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     final byte[] hb;
 
     {
         hb = new byte[0];
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     boolean isReadOnly;
 
     boolean nativeByteOrder;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     final int offset;
 
     {
diff --git a/ojluni/annotations/hiddenapi/java/nio/CharBuffer.java b/ojluni/annotations/hiddenapi/java/nio/CharBuffer.java
index b447bb6..f6c3342 100644
--- a/ojluni/annotations/hiddenapi/java/nio/CharBuffer.java
+++ b/ojluni/annotations/hiddenapi/java/nio/CharBuffer.java
@@ -155,7 +155,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     abstract java.lang.String toString(int start, int end);
 
     public final int length() {
diff --git a/ojluni/annotations/hiddenapi/java/nio/DirectByteBuffer.java b/ojluni/annotations/hiddenapi/java/nio/DirectByteBuffer.java
index b6ca5fb..deef630 100644
--- a/ojluni/annotations/hiddenapi/java/nio/DirectByteBuffer.java
+++ b/ojluni/annotations/hiddenapi/java/nio/DirectByteBuffer.java
@@ -36,7 +36,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private DirectByteBuffer(long addr, int cap) {
         super(0, 0, 0, 0);
         throw new RuntimeException("Stub!");
diff --git a/ojluni/annotations/hiddenapi/java/nio/charset/Charset.java b/ojluni/annotations/hiddenapi/java/nio/charset/Charset.java
index fb86662..d81556d 100644
--- a/ojluni/annotations/hiddenapi/java/nio/charset/Charset.java
+++ b/ojluni/annotations/hiddenapi/java/nio/charset/Charset.java
@@ -167,7 +167,7 @@
         cache2 = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static java.nio.charset.Charset defaultCharset;
 
     private static java.lang.ThreadLocal<java.lang.ThreadLocal<?>> gate;
diff --git a/ojluni/annotations/hiddenapi/java/nio/charset/CharsetEncoder.java b/ojluni/annotations/hiddenapi/java/nio/charset/CharsetEncoder.java
index 0e0fd2d..0c986ff 100644
--- a/ojluni/annotations/hiddenapi/java/nio/charset/CharsetEncoder.java
+++ b/ojluni/annotations/hiddenapi/java/nio/charset/CharsetEncoder.java
@@ -138,7 +138,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean canEncode(java.nio.CharBuffer cb) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/nio/file/FileTreeWalker.java b/ojluni/annotations/hiddenapi/java/nio/file/FileTreeWalker.java
index 0f9ae1e..13a06a7 100644
--- a/ojluni/annotations/hiddenapi/java/nio/file/FileTreeWalker.java
+++ b/ojluni/annotations/hiddenapi/java/nio/file/FileTreeWalker.java
@@ -72,20 +72,20 @@
 
     private boolean closed;
 
-    @android.compat.annotation.UnsupportedAppUsage private final boolean followLinks;
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) private final boolean followLinks;
 
     {
         followLinks = false;
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.nio.file.LinkOption[] linkOptions;
 
     {
         linkOptions = new java.nio.file.LinkOption[0];
     }
 
-    @android.compat.annotation.UnsupportedAppUsage private final int maxDepth;
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) private final int maxDepth;
 
     {
         maxDepth = 0;
diff --git a/ojluni/annotations/hiddenapi/java/security/KeyPairGenerator.java b/ojluni/annotations/hiddenapi/java/security/KeyPairGenerator.java
index e5ceb2a..268a5a2 100644
--- a/ojluni/annotations/hiddenapi/java/security/KeyPairGenerator.java
+++ b/ojluni/annotations/hiddenapi/java/security/KeyPairGenerator.java
@@ -38,7 +38,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static java.security.KeyPairGenerator getInstance(
             sun.security.jca.GetInstance.Instance instance, java.lang.String algorithm) {
         throw new RuntimeException("Stub!");
diff --git a/ojluni/annotations/hiddenapi/java/text/DecimalFormatSymbols.java b/ojluni/annotations/hiddenapi/java/text/DecimalFormatSymbols.java
index 1b0a68c..e6945a3 100644
--- a/ojluni/annotations/hiddenapi/java/text/DecimalFormatSymbols.java
+++ b/ojluni/annotations/hiddenapi/java/text/DecimalFormatSymbols.java
@@ -100,7 +100,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.lang.String getPercentString() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/text/NumberFormat.java b/ojluni/annotations/hiddenapi/java/text/NumberFormat.java
index 232c94f..7aaf4c1 100644
--- a/ojluni/annotations/hiddenapi/java/text/NumberFormat.java
+++ b/ojluni/annotations/hiddenapi/java/text/NumberFormat.java
@@ -201,7 +201,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static java.text.NumberFormat getInstance(java.util.Locale desiredLocale, int choice) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/time/Duration.java b/ojluni/annotations/hiddenapi/java/time/Duration.java
index ca99832..6f4332b 100644
--- a/ojluni/annotations/hiddenapi/java/time/Duration.java
+++ b/ojluni/annotations/hiddenapi/java/time/Duration.java
@@ -254,7 +254,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(trackingBug = 172313849)
     private java.math.BigDecimal toSeconds() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/time/OffsetDateTime.java b/ojluni/annotations/hiddenapi/java/time/OffsetDateTime.java
index d4822c4..adb1f64 100644
--- a/ojluni/annotations/hiddenapi/java/time/OffsetDateTime.java
+++ b/ojluni/annotations/hiddenapi/java/time/OffsetDateTime.java
@@ -71,7 +71,7 @@
                 java.lang.Comparable<java.time.OffsetDateTime>,
                 java.io.Serializable {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private OffsetDateTime(java.time.LocalDateTime dateTime, java.time.ZoneOffset offset) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/time/ZoneId.java b/ojluni/annotations/hiddenapi/java/time/ZoneId.java
index ef4a7aa..8854125 100644
--- a/ojluni/annotations/hiddenapi/java/time/ZoneId.java
+++ b/ojluni/annotations/hiddenapi/java/time/ZoneId.java
@@ -92,7 +92,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     static java.time.ZoneId of(java.lang.String zoneId, boolean checkAvailable) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/util/ArrayList.java b/ojluni/annotations/hiddenapi/java/util/ArrayList.java
index 3d90fa5..1d7691f 100644
--- a/ojluni/annotations/hiddenapi/java/util/ArrayList.java
+++ b/ojluni/annotations/hiddenapi/java/util/ArrayList.java
@@ -404,7 +404,7 @@
             throw new RuntimeException("Stub!");
         }
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         private final int offset;
 
         {
diff --git a/ojluni/annotations/hiddenapi/java/util/HashMap.java b/ojluni/annotations/hiddenapi/java/util/HashMap.java
index b13c25c..e72432b 100644
--- a/ojluni/annotations/hiddenapi/java/util/HashMap.java
+++ b/ojluni/annotations/hiddenapi/java/util/HashMap.java
@@ -531,17 +531,17 @@
             hash = 0;
         }
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         final K key;
 
         {
             key = null;
         }
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         java.util.HashMap.Node<K, V> next;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         V value;
     }
 
diff --git a/ojluni/annotations/hiddenapi/java/util/HashSet.java b/ojluni/annotations/hiddenapi/java/util/HashSet.java
index 896c1aa..3fd999d 100644
--- a/ojluni/annotations/hiddenapi/java/util/HashSet.java
+++ b/ojluni/annotations/hiddenapi/java/util/HashSet.java
@@ -47,7 +47,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
+    HashSet(int initialCapacity, float loadFactor, boolean fake) {
         throw new RuntimeException("Stub!");
     }
 
diff --git a/ojluni/annotations/hiddenapi/java/util/LinkedHashMap.java b/ojluni/annotations/hiddenapi/java/util/LinkedHashMap.java
index 99d649b..3fec006 100644
--- a/ojluni/annotations/hiddenapi/java/util/LinkedHashMap.java
+++ b/ojluni/annotations/hiddenapi/java/util/LinkedHashMap.java
@@ -145,7 +145,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     final boolean accessOrder;
 
     {
diff --git a/ojluni/annotations/hiddenapi/java/util/Random.java b/ojluni/annotations/hiddenapi/java/util/Random.java
index 930688a..cc1b70f 100644
--- a/ojluni/annotations/hiddenapi/java/util/Random.java
+++ b/ojluni/annotations/hiddenapi/java/util/Random.java
@@ -38,7 +38,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(trackingBug = 172313849)
     private static long seedUniquifier() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/util/UUID.java b/ojluni/annotations/hiddenapi/java/util/UUID.java
index 353f2b6..06bd0db 100644
--- a/ojluni/annotations/hiddenapi/java/util/UUID.java
+++ b/ojluni/annotations/hiddenapi/java/util/UUID.java
@@ -98,14 +98,14 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final long leastSigBits;
 
     {
         leastSigBits = 0;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final long mostSigBits;
 
     {
diff --git a/ojluni/annotations/hiddenapi/java/util/Vector.java b/ojluni/annotations/hiddenapi/java/util/Vector.java
index 91a6aa7..bf0f326 100644
--- a/ojluni/annotations/hiddenapi/java/util/Vector.java
+++ b/ojluni/annotations/hiddenapi/java/util/Vector.java
@@ -162,7 +162,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     E elementData(int index) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/java/util/concurrent/ThreadPoolExecutor.java b/ojluni/annotations/hiddenapi/java/util/concurrent/ThreadPoolExecutor.java
index 0d27711..50003b4 100644
--- a/ojluni/annotations/hiddenapi/java/util/concurrent/ThreadPoolExecutor.java
+++ b/ojluni/annotations/hiddenapi/java/util/concurrent/ThreadPoolExecutor.java
@@ -334,7 +334,7 @@
 
     private static final int TIDYING = 1073741824; // 0x40000000
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private volatile boolean allowCoreThreadTimeOut;
 
     private long completedTaskCount;
@@ -348,7 +348,7 @@
         ctl = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private static final java.util.concurrent.RejectedExecutionHandler defaultHandler;
 
     static {
@@ -361,7 +361,7 @@
 
     private int largestPoolSize;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private final java.util.concurrent.locks.ReentrantLock mainLock;
 
     {
diff --git a/ojluni/annotations/hiddenapi/java/util/logging/Handler.java b/ojluni/annotations/hiddenapi/java/util/logging/Handler.java
index 7243f48..fd1344c 100644
--- a/ojluni/annotations/hiddenapi/java/util/logging/Handler.java
+++ b/ojluni/annotations/hiddenapi/java/util/logging/Handler.java
@@ -118,6 +118,6 @@
         offValue = 0;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     boolean sealed = true;
 }
diff --git a/ojluni/annotations/hiddenapi/java/util/logging/LogManager.java b/ojluni/annotations/hiddenapi/java/util/logging/LogManager.java
index 8900a5a..ae16209 100644
--- a/ojluni/annotations/hiddenapi/java/util/logging/LogManager.java
+++ b/ojluni/annotations/hiddenapi/java/util/logging/LogManager.java
@@ -173,7 +173,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     java.util.logging.Formatter getFormatterProperty(
             java.lang.String name, java.util.logging.Formatter defaultValue) {
         throw new RuntimeException("Stub!");
diff --git a/ojluni/annotations/hiddenapi/java/util/regex/Matcher.java b/ojluni/annotations/hiddenapi/java/util/regex/Matcher.java
index 686f1bd..4600ff0 100644
--- a/ojluni/annotations/hiddenapi/java/util/regex/Matcher.java
+++ b/ojluni/annotations/hiddenapi/java/util/regex/Matcher.java
@@ -232,7 +232,7 @@
 
     boolean anchoringBounds = true;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     int appendPos = 0; // 0x0
 
     int from;
diff --git a/ojluni/annotations/hiddenapi/java/util/zip/Deflater.java b/ojluni/annotations/hiddenapi/java/util/zip/Deflater.java
index 0dbe2a5..aa27720 100644
--- a/ojluni/annotations/hiddenapi/java/util/zip/Deflater.java
+++ b/ojluni/annotations/hiddenapi/java/util/zip/Deflater.java
@@ -160,17 +160,17 @@
 
     public static final int SYNC_FLUSH = 2; // 0x2
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private byte[] buf;
 
     private long bytesRead;
 
     private long bytesWritten;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean finish;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean finished;
 
     private final dalvik.system.CloseGuard guard;
@@ -179,19 +179,19 @@
         guard = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int len;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int level;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int off;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean setParams;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int strategy;
 
     private final java.util.zip.ZStreamRef zsRef;
diff --git a/ojluni/annotations/hiddenapi/java/util/zip/Inflater.java b/ojluni/annotations/hiddenapi/java/util/zip/Inflater.java
index e5a5dcc..5b770b8 100644
--- a/ojluni/annotations/hiddenapi/java/util/zip/Inflater.java
+++ b/ojluni/annotations/hiddenapi/java/util/zip/Inflater.java
@@ -132,7 +132,7 @@
 
     private static native void end(long addr);
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private byte[] buf;
 
     private long bytesRead;
@@ -145,7 +145,7 @@
         defaultBuf = new byte[0];
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean finished;
 
     private final dalvik.system.CloseGuard guard;
@@ -154,13 +154,13 @@
         guard = null;
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int len;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean needDict;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int off;
 
     private final java.util.zip.ZStreamRef zsRef;
diff --git a/ojluni/annotations/hiddenapi/java/util/zip/ZipEntry.java b/ojluni/annotations/hiddenapi/java/util/zip/ZipEntry.java
index 91440c2..2b9936d 100644
--- a/ojluni/annotations/hiddenapi/java/util/zip/ZipEntry.java
+++ b/ojluni/annotations/hiddenapi/java/util/zip/ZipEntry.java
@@ -31,7 +31,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class ZipEntry implements java.util.zip.ZipConstants, java.lang.Cloneable {
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public ZipEntry(
             java.lang.String name,
             java.lang.String comment,
@@ -189,7 +189,7 @@
 
     int flag = 0; // 0x0
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     int method = -1; // 0xffffffff
 
     java.nio.file.attribute.FileTime mtime;
diff --git a/ojluni/annotations/hiddenapi/java/util/zip/ZipFile.java b/ojluni/annotations/hiddenapi/java/util/zip/ZipFile.java
index b2c72a7..02fd000 100644
--- a/ojluni/annotations/hiddenapi/java/util/zip/ZipFile.java
+++ b/ojluni/annotations/hiddenapi/java/util/zip/ZipFile.java
@@ -192,7 +192,7 @@
 
     private java.util.Deque<java.util.zip.Inflater> inflaterCache;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private long jzfile;
 
     private final boolean locsig;
@@ -244,7 +244,7 @@
             throw new RuntimeException("Stub!");
         }
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public java.util.zip.ZipEntry nextElement() {
             throw new RuntimeException("Stub!");
         }
diff --git a/ojluni/annotations/hiddenapi/java/util/zip/ZipInputStream.java b/ojluni/annotations/hiddenapi/java/util/zip/ZipInputStream.java
index 18c1b2e..14a0a66 100644
--- a/ojluni/annotations/hiddenapi/java/util/zip/ZipInputStream.java
+++ b/ojluni/annotations/hiddenapi/java/util/zip/ZipInputStream.java
@@ -100,12 +100,12 @@
 
     private boolean entryEOF = false;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int flag;
 
     private long remaining;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private byte[] tmpbuf;
 
     private java.util.zip.ZipCoder zc;
diff --git a/ojluni/annotations/hiddenapi/java/util/zip/ZipOutputStream.java b/ojluni/annotations/hiddenapi/java/util/zip/ZipOutputStream.java
index fd4276a..a733cda 100644
--- a/ojluni/annotations/hiddenapi/java/util/zip/ZipOutputStream.java
+++ b/ojluni/annotations/hiddenapi/java/util/zip/ZipOutputStream.java
@@ -144,10 +144,10 @@
 
     private long locoff = 0; // 0x0
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private int method = 8; // 0x8
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private java.util.HashSet<java.lang.String> names;
 
     @UnsupportedAppUsage
diff --git a/ojluni/annotations/hiddenapi/sun/misc/Cleaner.java b/ojluni/annotations/hiddenapi/sun/misc/Cleaner.java
index cdbc658..be9bc498 100644
--- a/ojluni/annotations/hiddenapi/sun/misc/Cleaner.java
+++ b/ojluni/annotations/hiddenapi/sun/misc/Cleaner.java
@@ -53,10 +53,10 @@
         throw new RuntimeException("Stub!");
     }
 
-    private static final java.lang.ref.ReferenceQueue<java.lang.Object> dummyQueue;
+    private static final java.lang.ref.ReferenceQueue<java.lang.Object> fakeQueue;
 
     static {
-        dummyQueue = null;
+        fakeQueue = null;
     }
 
     private static sun.misc.Cleaner first;
diff --git a/ojluni/annotations/hiddenapi/sun/misc/Unsafe.java b/ojluni/annotations/hiddenapi/sun/misc/Unsafe.java
index c6dad00..63d7a33 100644
--- a/ojluni/annotations/hiddenapi/sun/misc/Unsafe.java
+++ b/ojluni/annotations/hiddenapi/sun/misc/Unsafe.java
@@ -230,33 +230,33 @@
     @UnsupportedAppUsage
     public native void putDouble(long address, double x);
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public native void copyMemoryToPrimitiveArray(
             long srcAddr, java.lang.Object dst, long dstOffset, long bytes);
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public native void copyMemoryFromPrimitiveArray(
             java.lang.Object src, long srcOffset, long dstAddr, long bytes);
 
     @UnsupportedAppUsage
     public native void copyMemory(long srcAddr, long dstAddr, long bytes);
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public int getAndAddInt(java.lang.Object o, long offset, int delta) {
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public long getAndAddLong(java.lang.Object o, long offset, long delta) {
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public int getAndSetInt(java.lang.Object o, long offset, int newValue) {
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public long getAndSetLong(java.lang.Object o, long offset, long newValue) {
         throw new RuntimeException("Stub!");
     }
@@ -276,7 +276,7 @@
     @UnsupportedAppUsage
     public native void fullFence();
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final int INVALID_FIELD_OFFSET = -1; // 0xffffffff
 
     @UnsupportedAppUsage private static final sun.misc.Unsafe THE_ONE;
diff --git a/ojluni/annotations/hiddenapi/sun/net/util/IPAddressUtil.java b/ojluni/annotations/hiddenapi/sun/net/util/IPAddressUtil.java
index 0134b09..4ff149a 100644
--- a/ojluni/annotations/hiddenapi/sun/net/util/IPAddressUtil.java
+++ b/ojluni/annotations/hiddenapi/sun/net/util/IPAddressUtil.java
@@ -40,12 +40,12 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static boolean isIPv4LiteralAddress(java.lang.String src) {
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static boolean isIPv6LiteralAddress(java.lang.String src) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/net/www/URLConnection.java b/ojluni/annotations/hiddenapi/sun/net/www/URLConnection.java
index 12b1d72..eeb25f4 100644
--- a/ojluni/annotations/hiddenapi/sun/net/www/URLConnection.java
+++ b/ojluni/annotations/hiddenapi/sun/net/www/URLConnection.java
@@ -28,7 +28,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public abstract class URLConnection extends java.net.URLConnection {
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public URLConnection(java.net.URL u) {
         super(null);
         throw new RuntimeException("Stub!");
@@ -38,7 +38,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public void setProperties(sun.net.www.MessageHeader properties) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/nio/fs/BasicFileAttributesHolder.java b/ojluni/annotations/hiddenapi/sun/nio/fs/BasicFileAttributesHolder.java
index 3d6c0e8..ef8b1a4 100644
--- a/ojluni/annotations/hiddenapi/sun/nio/fs/BasicFileAttributesHolder.java
+++ b/ojluni/annotations/hiddenapi/sun/nio/fs/BasicFileAttributesHolder.java
@@ -28,7 +28,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public interface BasicFileAttributesHolder {
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.nio.file.attribute.BasicFileAttributes get();
 
     public void invalidate();
diff --git a/ojluni/annotations/hiddenapi/sun/security/jca/GetInstance.java b/ojluni/annotations/hiddenapi/sun/security/jca/GetInstance.java
index 92756f9..ef3d1a6 100644
--- a/ojluni/annotations/hiddenapi/sun/security/jca/GetInstance.java
+++ b/ojluni/annotations/hiddenapi/sun/security/jca/GetInstance.java
@@ -75,7 +75,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static sun.security.jca.GetInstance.Instance getInstance(
             java.lang.String type,
             java.lang.Class<?> clazz,
@@ -94,7 +94,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static sun.security.jca.GetInstance.Instance getInstance(
             java.lang.String type,
             java.lang.Class<?> clazz,
@@ -114,7 +114,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static sun.security.jca.GetInstance.Instance getInstance(
             java.lang.String type,
             java.lang.Class<?> clazz,
@@ -156,13 +156,13 @@
             throw new RuntimeException("Stub!");
         }
 
-        @android.compat.annotation.UnsupportedAppUsage public final java.lang.Object impl;
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) public final java.lang.Object impl;
 
         {
             impl = null;
         }
 
-        @android.compat.annotation.UnsupportedAppUsage public final java.security.Provider provider;
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) public final java.security.Provider provider;
 
         {
             provider = null;
diff --git a/ojluni/annotations/hiddenapi/sun/security/jca/JCAUtil.java b/ojluni/annotations/hiddenapi/sun/security/jca/JCAUtil.java
index ca703f2..ab1acb6 100644
--- a/ojluni/annotations/hiddenapi/sun/security/jca/JCAUtil.java
+++ b/ojluni/annotations/hiddenapi/sun/security/jca/JCAUtil.java
@@ -39,7 +39,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static java.security.SecureRandom getSecureRandom() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/jca/ProviderConfig.java b/ojluni/annotations/hiddenapi/sun/security/jca/ProviderConfig.java
index f6abd8b..f6a49a1 100644
--- a/ojluni/annotations/hiddenapi/sun/security/jca/ProviderConfig.java
+++ b/ojluni/annotations/hiddenapi/sun/security/jca/ProviderConfig.java
@@ -47,7 +47,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private boolean hasArgument() {
         throw new RuntimeException("Stub!");
     }
@@ -56,7 +56,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private void disableLoad() {
         throw new RuntimeException("Stub!");
     }
@@ -94,7 +94,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage private static final java.lang.Class[] CL_STRING;
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) private static final java.lang.Class[] CL_STRING;
 
     static {
         CL_STRING = new java.lang.Class[0];
@@ -107,7 +107,7 @@
 
     private static final java.lang.String P11_SOL_NAME = "sun.security.pkcs11.SunPKCS11";
 
-    @android.compat.annotation.UnsupportedAppUsage private final java.lang.String argument;
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) private final java.lang.String argument;
 
     {
         argument = null;
diff --git a/ojluni/annotations/hiddenapi/sun/security/jca/Providers.java b/ojluni/annotations/hiddenapi/sun/security/jca/Providers.java
index 4e116fc..440afbf 100644
--- a/ojluni/annotations/hiddenapi/sun/security/jca/Providers.java
+++ b/ojluni/annotations/hiddenapi/sun/security/jca/Providers.java
@@ -38,12 +38,12 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static java.lang.Object startJarVerification() {
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static void stopJarVerification(java.lang.Object obj) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/pkcs/PKCS9Attributes.java b/ojluni/annotations/hiddenapi/sun/security/pkcs/PKCS9Attributes.java
index a5e9019..c4dd15b 100644
--- a/ojluni/annotations/hiddenapi/sun/security/pkcs/PKCS9Attributes.java
+++ b/ojluni/annotations/hiddenapi/sun/security/pkcs/PKCS9Attributes.java
@@ -75,7 +75,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.security.pkcs.PKCS9Attribute getAttribute(java.lang.String name) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/pkcs/SignerInfo.java b/ojluni/annotations/hiddenapi/sun/security/pkcs/SignerInfo.java
index 162b420..cecae94 100644
--- a/ojluni/annotations/hiddenapi/sun/security/pkcs/SignerInfo.java
+++ b/ojluni/annotations/hiddenapi/sun/security/pkcs/SignerInfo.java
@@ -129,7 +129,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public byte[] getEncryptedDigest() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/timestamp/TimestampToken.java b/ojluni/annotations/hiddenapi/sun/security/timestamp/TimestampToken.java
index 1c2d115..19ebc55 100644
--- a/ojluni/annotations/hiddenapi/sun/security/timestamp/TimestampToken.java
+++ b/ojluni/annotations/hiddenapi/sun/security/timestamp/TimestampToken.java
@@ -33,7 +33,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.util.Date getDate() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/util/Cache.java b/ojluni/annotations/hiddenapi/sun/security/util/Cache.java
index 1520e12..9df085e 100644
--- a/ojluni/annotations/hiddenapi/sun/security/util/Cache.java
+++ b/ojluni/annotations/hiddenapi/sun/security/util/Cache.java
@@ -63,7 +63,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static <K, V> sun.security.util.Cache<K, V> newHardMemoryCache(int size) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/util/DerInputStream.java b/ojluni/annotations/hiddenapi/sun/security/util/DerInputStream.java
index fefcd08..f021927 100644
--- a/ojluni/annotations/hiddenapi/sun/security/util/DerInputStream.java
+++ b/ojluni/annotations/hiddenapi/sun/security/util/DerInputStream.java
@@ -148,7 +148,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.lang.String getUTF8String() throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/util/DerOutputStream.java b/ojluni/annotations/hiddenapi/sun/security/util/DerOutputStream.java
index 772018d..507625d 100644
--- a/ojluni/annotations/hiddenapi/sun/security/util/DerOutputStream.java
+++ b/ojluni/annotations/hiddenapi/sun/security/util/DerOutputStream.java
@@ -30,7 +30,7 @@
 public class DerOutputStream extends java.io.ByteArrayOutputStream
         implements sun.security.util.DerEncoder {
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public DerOutputStream(int size) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/util/DerValue.java b/ojluni/annotations/hiddenapi/sun/security/util/DerValue.java
index 42825d3..77d37c4 100644
--- a/ojluni/annotations/hiddenapi/sun/security/util/DerValue.java
+++ b/ojluni/annotations/hiddenapi/sun/security/util/DerValue.java
@@ -146,7 +146,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.math.BigInteger getPositiveBigInteger() throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/util/ManifestDigester.java b/ojluni/annotations/hiddenapi/sun/security/util/ManifestDigester.java
index b8d89f8..d62d9b3 100644
--- a/ojluni/annotations/hiddenapi/sun/security/util/ManifestDigester.java
+++ b/ojluni/annotations/hiddenapi/sun/security/util/ManifestDigester.java
@@ -76,7 +76,7 @@
             throw new RuntimeException("Stub!");
         }
 
-        @android.compat.annotation.UnsupportedAppUsage
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public byte[] digestWorkaround(java.security.MessageDigest md) {
             throw new RuntimeException("Stub!");
         }
diff --git a/ojluni/annotations/hiddenapi/sun/security/util/PropertyExpander.java b/ojluni/annotations/hiddenapi/sun/security/util/PropertyExpander.java
index 9a86630..91faf10 100644
--- a/ojluni/annotations/hiddenapi/sun/security/util/PropertyExpander.java
+++ b/ojluni/annotations/hiddenapi/sun/security/util/PropertyExpander.java
@@ -33,7 +33,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static java.lang.String expand(java.lang.String value)
             throws sun.security.util.PropertyExpander.ExpandException {
         throw new RuntimeException("Stub!");
diff --git a/ojluni/annotations/hiddenapi/sun/security/util/ResourcesMgr.java b/ojluni/annotations/hiddenapi/sun/security/util/ResourcesMgr.java
index 0a66cae..9e63fdd 100644
--- a/ojluni/annotations/hiddenapi/sun/security/util/ResourcesMgr.java
+++ b/ojluni/annotations/hiddenapi/sun/security/util/ResourcesMgr.java
@@ -32,7 +32,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static java.lang.String getString(java.lang.String s) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/x509/AlgorithmId.java b/ojluni/annotations/hiddenapi/sun/security/x509/AlgorithmId.java
index 7948de8..215bc7a 100644
--- a/ojluni/annotations/hiddenapi/sun/security/x509/AlgorithmId.java
+++ b/ojluni/annotations/hiddenapi/sun/security/x509/AlgorithmId.java
@@ -67,7 +67,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public final byte[] encode() throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/x509/CRLNumberExtension.java b/ojluni/annotations/hiddenapi/sun/security/x509/CRLNumberExtension.java
index 40eb1b8..249c353 100644
--- a/ojluni/annotations/hiddenapi/sun/security/x509/CRLNumberExtension.java
+++ b/ojluni/annotations/hiddenapi/sun/security/x509/CRLNumberExtension.java
@@ -74,7 +74,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.math.BigInteger get(java.lang.String name) throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/x509/CertificateExtensions.java b/ojluni/annotations/hiddenapi/sun/security/x509/CertificateExtensions.java
index da41fbd..6b7eedb 100644
--- a/ojluni/annotations/hiddenapi/sun/security/x509/CertificateExtensions.java
+++ b/ojluni/annotations/hiddenapi/sun/security/x509/CertificateExtensions.java
@@ -66,7 +66,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.security.x509.Extension get(java.lang.String name) throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/x509/KeyUsageExtension.java b/ojluni/annotations/hiddenapi/sun/security/x509/KeyUsageExtension.java
index cd2ff3e..3c15a4c 100644
--- a/ojluni/annotations/hiddenapi/sun/security/x509/KeyUsageExtension.java
+++ b/ojluni/annotations/hiddenapi/sun/security/x509/KeyUsageExtension.java
@@ -69,7 +69,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.lang.Boolean get(java.lang.String name) throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/x509/NetscapeCertTypeExtension.java b/ojluni/annotations/hiddenapi/sun/security/x509/NetscapeCertTypeExtension.java
index 2db2b55..0460d8b 100644
--- a/ojluni/annotations/hiddenapi/sun/security/x509/NetscapeCertTypeExtension.java
+++ b/ojluni/annotations/hiddenapi/sun/security/x509/NetscapeCertTypeExtension.java
@@ -70,7 +70,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public java.lang.Boolean get(java.lang.String name) throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/x509/SubjectAlternativeNameExtension.java b/ojluni/annotations/hiddenapi/sun/security/x509/SubjectAlternativeNameExtension.java
index 9b1ed0b..dea94a4 100644
--- a/ojluni/annotations/hiddenapi/sun/security/x509/SubjectAlternativeNameExtension.java
+++ b/ojluni/annotations/hiddenapi/sun/security/x509/SubjectAlternativeNameExtension.java
@@ -67,7 +67,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.security.x509.GeneralNames get(java.lang.String name) throws java.io.IOException {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/security/x509/X500Name.java b/ojluni/annotations/hiddenapi/sun/security/x509/X500Name.java
index fd8e9b6..bdc7ce7 100644
--- a/ojluni/annotations/hiddenapi/sun/security/x509/X500Name.java
+++ b/ojluni/annotations/hiddenapi/sun/security/x509/X500Name.java
@@ -45,7 +45,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public X500Name(
             java.lang.String commonName,
             java.lang.String organizationUnit,
@@ -55,7 +55,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public X500Name(
             java.lang.String commonName,
             java.lang.String organizationUnit,
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/AbstractCalendar.java b/ojluni/annotations/hiddenapi/sun/util/calendar/AbstractCalendar.java
index 3e1be5e..8bea9b5 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/AbstractCalendar.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/AbstractCalendar.java
@@ -37,7 +37,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.Era[] getEras() {
         throw new RuntimeException("Stub!");
     }
@@ -75,7 +75,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public long getTimeOfDayValue(sun.util.calendar.CalendarDate date) {
         throw new RuntimeException("Stub!");
     }
@@ -104,7 +104,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static long getDayOfWeekDateOnOrBefore(long fixedDate, int dayOfWeek) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/BaseCalendar.java b/ojluni/annotations/hiddenapi/sun/util/calendar/BaseCalendar.java
index 660ba87..dc410b6 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/BaseCalendar.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/BaseCalendar.java
@@ -185,10 +185,10 @@
             throw new RuntimeException("Stub!");
         }
 
-        @android.compat.annotation.UnsupportedAppUsage
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public abstract int getNormalizedYear();
 
-        @android.compat.annotation.UnsupportedAppUsage
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public abstract void setNormalizedYear(int normalizedYear);
 
         protected final boolean hit(int year) {
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarDate.java b/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarDate.java
index 165920c..fdeeb5b 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarDate.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarDate.java
@@ -84,7 +84,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.CalendarDate setDayOfMonth(int date) {
         throw new RuntimeException("Stub!");
     }
@@ -101,7 +101,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.CalendarDate setHours(int hours) {
         throw new RuntimeException("Stub!");
     }
@@ -114,7 +114,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.CalendarDate setMinutes(int minutes) {
         throw new RuntimeException("Stub!");
     }
@@ -127,7 +127,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.CalendarDate setSeconds(int seconds) {
         throw new RuntimeException("Stub!");
     }
@@ -140,7 +140,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.CalendarDate setMillis(int millis) {
         throw new RuntimeException("Stub!");
     }
@@ -149,12 +149,12 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public long getTimeOfDay() {
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.CalendarDate setDate(int year, int month, int dayOfMonth) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarSystem.java b/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarSystem.java
index c847641..e658a34 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarSystem.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarSystem.java
@@ -34,7 +34,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static sun.util.calendar.Gregorian getGregorianCalendar() {
         throw new RuntimeException("Stub!");
     }
@@ -62,10 +62,10 @@
 
     public abstract sun.util.calendar.CalendarDate newCalendarDate();
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public abstract sun.util.calendar.CalendarDate newCalendarDate(java.util.TimeZone zone);
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public abstract long getTime(sun.util.calendar.CalendarDate date);
 
     public abstract int getYearLength(sun.util.calendar.CalendarDate date);
@@ -88,7 +88,7 @@
     public abstract sun.util.calendar.CalendarDate setTimeOfDay(
             sun.util.calendar.CalendarDate date, int timeOfDay);
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public abstract boolean validate(sun.util.calendar.CalendarDate date);
 
     public abstract boolean normalize(sun.util.calendar.CalendarDate date);
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarUtils.java b/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarUtils.java
index 2a05603..f2908a5 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarUtils.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/CalendarUtils.java
@@ -40,12 +40,12 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final long floorDivide(long n, long d) {
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final int floorDivide(int n, int d) {
         throw new RuntimeException("Stub!");
     }
@@ -58,12 +58,12 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final long mod(long x, long y) {
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public static final int mod(int x, int y) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/Era.java b/ojluni/annotations/hiddenapi/sun/util/calendar/Era.java
index 49aa034..f865e59 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/Era.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/Era.java
@@ -28,7 +28,7 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class Era {
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public Era(java.lang.String name, java.lang.String abbr, long since, boolean localTime) {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/ImmutableGregorianDate.java b/ojluni/annotations/hiddenapi/sun/util/calendar/ImmutableGregorianDate.java
index aa92656..4aff983 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/ImmutableGregorianDate.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/ImmutableGregorianDate.java
@@ -242,7 +242,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     private void unsupported() {
         throw new RuntimeException("Stub!");
     }
diff --git a/ojluni/annotations/hiddenapi/sun/util/calendar/LocalGregorianCalendar.java b/ojluni/annotations/hiddenapi/sun/util/calendar/LocalGregorianCalendar.java
index b1320c5..edbcf2a 100644
--- a/ojluni/annotations/hiddenapi/sun/util/calendar/LocalGregorianCalendar.java
+++ b/ojluni/annotations/hiddenapi/sun/util/calendar/LocalGregorianCalendar.java
@@ -68,12 +68,12 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public sun.util.calendar.LocalGregorianCalendar.Date newCalendarDate(java.util.TimeZone zone) {
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public boolean validate(sun.util.calendar.CalendarDate date) {
         throw new RuntimeException("Stub!");
     }
@@ -82,7 +82,7 @@
         throw new RuntimeException("Stub!");
     }
 
-    @android.compat.annotation.UnsupportedAppUsage
+    @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     public boolean normalize(sun.util.calendar.CalendarDate date) {
         throw new RuntimeException("Stub!");
     }
@@ -122,7 +122,7 @@
             throw new RuntimeException("Stub!");
         }
 
-        @android.compat.annotation.UnsupportedAppUsage
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public sun.util.calendar.LocalGregorianCalendar.Date setEra(sun.util.calendar.Era era) {
             throw new RuntimeException("Stub!");
         }
@@ -131,17 +131,17 @@
             throw new RuntimeException("Stub!");
         }
 
-        @android.compat.annotation.UnsupportedAppUsage
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public sun.util.calendar.LocalGregorianCalendar.Date setYear(int localYear) {
             throw new RuntimeException("Stub!");
         }
 
-        @android.compat.annotation.UnsupportedAppUsage
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public int getNormalizedYear() {
             throw new RuntimeException("Stub!");
         }
 
-        @android.compat.annotation.UnsupportedAppUsage
+        @android.compat.annotation.UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
         public void setNormalizedYear(int normalizedYear) {
             throw new RuntimeException("Stub!");
         }
diff --git a/ojluni/annotations/mmodule/java/io/FileDescriptor.annotated.java b/ojluni/annotations/mmodule/java/io/FileDescriptor.annotated.java
index 89006c1..cc16cd6 100644
--- a/ojluni/annotations/mmodule/java/io/FileDescriptor.annotated.java
+++ b/ojluni/annotations/mmodule/java/io/FileDescriptor.annotated.java
@@ -36,10 +36,12 @@
 
 public native void sync() throws java.io.SyncFailedException;
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public int getInt$() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public void setInt$(int fd) { throw new RuntimeException("Stub!"); }
 
 public long getOwnerId$() { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/io/FileInputStream.annotated.java b/ojluni/annotations/mmodule/java/io/FileInputStream.annotated.java
index 11013ac..ac26aeb 100644
--- a/ojluni/annotations/mmodule/java/io/FileInputStream.annotated.java
+++ b/ojluni/annotations/mmodule/java/io/FileInputStream.annotated.java
@@ -38,7 +38,8 @@
 
 public FileInputStream(java.io.FileDescriptor fdObj) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public FileInputStream(java.io.FileDescriptor fdObj, boolean isFdOwner) { throw new RuntimeException("Stub!"); }
 
 public int read() throws java.io.IOException { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/lang/Class.annotated.java b/ojluni/annotations/mmodule/java/lang/Class.annotated.java
index fd80abb..41411bf 100644
--- a/ojluni/annotations/mmodule/java/lang/Class.annotated.java
+++ b/ojluni/annotations/mmodule/java/lang/Class.annotated.java
@@ -83,7 +83,6 @@
 
 public java.lang.Package getPackage() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
 public java.lang.String getPackageName$() { throw new RuntimeException("Stub!"); }
 
 public java.lang.Class<?>[] getInterfaces() { throw new RuntimeException("Stub!"); }
@@ -134,12 +133,10 @@
 
 public native java.lang.reflect.Field[] getDeclaredFields();
 
-@libcore.api.CorePlatformApi
 public native java.lang.reflect.Field[] getDeclaredFieldsUnchecked(boolean publicOnly);
 
 public java.lang.reflect.Method[] getDeclaredMethods() throws java.lang.SecurityException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
 public native java.lang.reflect.Method[] getDeclaredMethodsUnchecked(boolean publicOnly);
 
 public java.lang.reflect.Constructor<?>[] getDeclaredConstructors() throws java.lang.SecurityException { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/lang/Math.annotated.java b/ojluni/annotations/mmodule/java/lang/Math.annotated.java
deleted file mode 100644
index 46fae4e..0000000
--- a/ojluni/annotations/mmodule/java/lang/Math.annotated.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package java.lang;
-
-import java.util.Random;
-
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public final class Math {
-
-Math() { throw new RuntimeException("Stub!"); }
-
-public static native double sin(double a);
-
-public static native double cos(double a);
-
-public static native double tan(double a);
-
-public static native double asin(double a);
-
-public static native double acos(double a);
-
-public static native double atan(double a);
-
-public static double toRadians(double angdeg) { throw new RuntimeException("Stub!"); }
-
-public static double toDegrees(double angrad) { throw new RuntimeException("Stub!"); }
-
-public static native double exp(double a);
-
-public static native double log(double a);
-
-public static native double log10(double a);
-
-public static native double sqrt(double a);
-
-public static native double cbrt(double a);
-
-public static native double IEEEremainder(double f1, double f2);
-
-public static native double ceil(double a);
-
-public static native double floor(double a);
-
-public static native double rint(double a);
-
-public static native double atan2(double y, double x);
-
-public static native double pow(double a, double b);
-
-public static int round(float a) { throw new RuntimeException("Stub!"); }
-
-public static long round(double a) { throw new RuntimeException("Stub!"); }
-
-public static double random() { throw new RuntimeException("Stub!"); }
-
-@libcore.api.CorePlatformApi
-public static long randomLongInternal() { throw new RuntimeException("Stub!"); }
-
-public static int addExact(int x, int y) { throw new RuntimeException("Stub!"); }
-
-public static long addExact(long x, long y) { throw new RuntimeException("Stub!"); }
-
-public static int subtractExact(int x, int y) { throw new RuntimeException("Stub!"); }
-
-public static long subtractExact(long x, long y) { throw new RuntimeException("Stub!"); }
-
-public static int multiplyExact(int x, int y) { throw new RuntimeException("Stub!"); }
-
-public static long multiplyExact(long x, long y) { throw new RuntimeException("Stub!"); }
-
-public static int incrementExact(int a) { throw new RuntimeException("Stub!"); }
-
-public static long incrementExact(long a) { throw new RuntimeException("Stub!"); }
-
-public static int decrementExact(int a) { throw new RuntimeException("Stub!"); }
-
-public static long decrementExact(long a) { throw new RuntimeException("Stub!"); }
-
-public static int negateExact(int a) { throw new RuntimeException("Stub!"); }
-
-public static long negateExact(long a) { throw new RuntimeException("Stub!"); }
-
-public static int toIntExact(long value) { throw new RuntimeException("Stub!"); }
-
-public static int floorDiv(int x, int y) { throw new RuntimeException("Stub!"); }
-
-public static long floorDiv(long x, long y) { throw new RuntimeException("Stub!"); }
-
-public static int floorMod(int x, int y) { throw new RuntimeException("Stub!"); }
-
-public static long floorMod(long x, long y) { throw new RuntimeException("Stub!"); }
-
-public static int abs(int a) { throw new RuntimeException("Stub!"); }
-
-public static long abs(long a) { throw new RuntimeException("Stub!"); }
-
-public static float abs(float a) { throw new RuntimeException("Stub!"); }
-
-public static double abs(double a) { throw new RuntimeException("Stub!"); }
-
-public static int max(int a, int b) { throw new RuntimeException("Stub!"); }
-
-public static long max(long a, long b) { throw new RuntimeException("Stub!"); }
-
-public static float max(float a, float b) { throw new RuntimeException("Stub!"); }
-
-public static double max(double a, double b) { throw new RuntimeException("Stub!"); }
-
-public static int min(int a, int b) { throw new RuntimeException("Stub!"); }
-
-public static long min(long a, long b) { throw new RuntimeException("Stub!"); }
-
-public static float min(float a, float b) { throw new RuntimeException("Stub!"); }
-
-public static double min(double a, double b) { throw new RuntimeException("Stub!"); }
-
-public static double ulp(double d) { throw new RuntimeException("Stub!"); }
-
-public static float ulp(float f) { throw new RuntimeException("Stub!"); }
-
-public static double signum(double d) { throw new RuntimeException("Stub!"); }
-
-public static float signum(float f) { throw new RuntimeException("Stub!"); }
-
-public static native double sinh(double x);
-
-public static native double cosh(double x);
-
-public static native double tanh(double x);
-
-public static native double hypot(double x, double y);
-
-public static native double expm1(double x);
-
-public static native double log1p(double x);
-
-public static double copySign(double magnitude, double sign) { throw new RuntimeException("Stub!"); }
-
-public static float copySign(float magnitude, float sign) { throw new RuntimeException("Stub!"); }
-
-public static int getExponent(float f) { throw new RuntimeException("Stub!"); }
-
-public static int getExponent(double d) { throw new RuntimeException("Stub!"); }
-
-public static double nextAfter(double start, double direction) { throw new RuntimeException("Stub!"); }
-
-public static float nextAfter(float start, double direction) { throw new RuntimeException("Stub!"); }
-
-public static double nextUp(double d) { throw new RuntimeException("Stub!"); }
-
-public static float nextUp(float f) { throw new RuntimeException("Stub!"); }
-
-public static double nextDown(double d) { throw new RuntimeException("Stub!"); }
-
-public static float nextDown(float f) { throw new RuntimeException("Stub!"); }
-
-public static double scalb(double d, int scaleFactor) { throw new RuntimeException("Stub!"); }
-
-public static float scalb(float f, int scaleFactor) { throw new RuntimeException("Stub!"); }
-
-public static final double E = 2.718281828459045;
-
-public static final double PI = 3.141592653589793;
-}
-
diff --git a/ojluni/annotations/mmodule/java/lang/System.annotated.java b/ojluni/annotations/mmodule/java/lang/System.annotated.java
index 918b3da..43286e5 100644
--- a/ojluni/annotations/mmodule/java/lang/System.annotated.java
+++ b/ojluni/annotations/mmodule/java/lang/System.annotated.java
@@ -102,8 +102,5 @@
 public static final java.io.PrintStream out;
 static { out = null; }
 
-@libcore.api.CorePlatformApi
-public static void logE(String message, Throwable th) { throw new RuntimeException("Stub!"); }
-
 }
 
diff --git a/ojluni/annotations/mmodule/java/lang/Thread.annotated.java b/ojluni/annotations/mmodule/java/lang/Thread.annotated.java
index 2499518..8308a99 100644
--- a/ojluni/annotations/mmodule/java/lang/Thread.annotated.java
+++ b/ojluni/annotations/mmodule/java/lang/Thread.annotated.java
@@ -141,10 +141,12 @@
 
 public static java.lang.Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static void setUncaughtExceptionPreHandler(UncaughtExceptionHandler eh) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static UncaughtExceptionHandler getUncaughtExceptionPreHandler() { throw new RuntimeException("Stub!"); }
 
 public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/net/DatagramSocket.annotated.java b/ojluni/annotations/mmodule/java/net/DatagramSocket.annotated.java
index b462769..52b5bff 100644
--- a/ojluni/annotations/mmodule/java/net/DatagramSocket.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/DatagramSocket.annotated.java
@@ -103,7 +103,8 @@
 
 public static synchronized void setDatagramSocketImplFactory(java.net.DatagramSocketImplFactory fac) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public java.io.FileDescriptor getFileDescriptor$() { throw new RuntimeException("Stub!"); }
 }
 
diff --git a/ojluni/annotations/mmodule/java/net/Inet4Address.annotated.java b/ojluni/annotations/mmodule/java/net/Inet4Address.annotated.java
index e10debf..4692b1f 100644
--- a/ojluni/annotations/mmodule/java/net/Inet4Address.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/Inet4Address.annotated.java
@@ -62,15 +62,18 @@
 
 public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static final java.net.InetAddress ALL;
 static { ALL = null; }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static final java.net.InetAddress ANY;
 static { ANY = null; }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static final java.net.InetAddress LOOPBACK;
 static { LOOPBACK = null; }
 }
diff --git a/ojluni/annotations/mmodule/java/net/Inet6Address.annotated.java b/ojluni/annotations/mmodule/java/net/Inet6Address.annotated.java
index 7888710..567d877 100644
--- a/ojluni/annotations/mmodule/java/net/Inet6Address.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/Inet6Address.annotated.java
@@ -28,7 +28,6 @@
 package java.net;
 
 
-@libcore.api.CorePlatformApi
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class Inet6Address extends java.net.InetAddress {
 
@@ -60,7 +59,6 @@
 
 public byte[] getAddress() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
 public int getScopeId() { throw new RuntimeException("Stub!"); }
 
 public java.net.NetworkInterface getScopedInterface() { throw new RuntimeException("Stub!"); }
@@ -73,11 +71,13 @@
 
 public boolean isIPv4CompatibleAddress() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static final java.net.InetAddress ANY;
 static { ANY = null; }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static final java.net.InetAddress LOOPBACK;
 static { LOOPBACK = null; }
 }
diff --git a/ojluni/annotations/mmodule/java/net/InetAddress.annotated.java b/ojluni/annotations/mmodule/java/net/InetAddress.annotated.java
index 6f4492d..6229cc9 100644
--- a/ojluni/annotations/mmodule/java/net/InetAddress.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/InetAddress.annotated.java
@@ -87,19 +87,24 @@
 
 public static java.net.InetAddress getLocalHost() throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static boolean isNumeric(java.lang.String address) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static java.net.InetAddress parseNumericAddress(java.lang.String numericAddress) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static void clearDnsCache() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static java.net.InetAddress getByNameOnNet(java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static java.net.InetAddress[] getAllByNameOnNet(java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
 }
 
diff --git a/ojluni/annotations/mmodule/java/net/InetSocketAddress.annotated.java b/ojluni/annotations/mmodule/java/net/InetSocketAddress.annotated.java
index 554b8e3..b2bf439 100644
--- a/ojluni/annotations/mmodule/java/net/InetSocketAddress.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/InetSocketAddress.annotated.java
@@ -30,7 +30,8 @@
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class InetSocketAddress extends java.net.SocketAddress {
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public InetSocketAddress() { throw new RuntimeException("Stub!"); }
 
 public InetSocketAddress(int port) { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/net/ServerSocket.annotated.java b/ojluni/annotations/mmodule/java/net/ServerSocket.annotated.java
index 1fcd05f..0bf1fe7 100644
--- a/ojluni/annotations/mmodule/java/net/ServerSocket.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/ServerSocket.annotated.java
@@ -52,7 +52,8 @@
 
 public java.net.Socket accept() throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public SocketImpl getImpl() throws SocketException { throw new RuntimeException("Stub!"); }
 
 protected final void implAccept(java.net.Socket s) throws java.io.IOException { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/net/Socket.annotated.java b/ojluni/annotations/mmodule/java/net/Socket.annotated.java
index e021788..61014de 100644
--- a/ojluni/annotations/mmodule/java/net/Socket.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/Socket.annotated.java
@@ -139,7 +139,8 @@
 
 public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.IntraCoreApi
 public java.io.FileDescriptor getFileDescriptor$() { throw new RuntimeException("Stub!"); }
 }
diff --git a/ojluni/annotations/mmodule/java/net/SocketImpl.annotated.java b/ojluni/annotations/mmodule/java/net/SocketImpl.annotated.java
index 2146d2f..a29ed76 100644
--- a/ojluni/annotations/mmodule/java/net/SocketImpl.annotated.java
+++ b/ojluni/annotations/mmodule/java/net/SocketImpl.annotated.java
@@ -62,7 +62,8 @@
 
 protected java.io.FileDescriptor getFileDescriptor() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public java.io.FileDescriptor getFD$() { throw new RuntimeException("Stub!"); }
 
 protected java.net.InetAddress getInetAddress() { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/nio/ByteBuffer.annotated.java b/ojluni/annotations/mmodule/java/nio/ByteBuffer.annotated.java
index d0caba2..369d0a5 100644
--- a/ojluni/annotations/mmodule/java/nio/ByteBuffer.annotated.java
+++ b/ojluni/annotations/mmodule/java/nio/ByteBuffer.annotated.java
@@ -151,7 +151,8 @@
 
 public boolean isAccessible() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public void setAccessible(boolean value) { throw new RuntimeException("Stub!"); }
 }
 
diff --git a/ojluni/annotations/mmodule/java/nio/DirectByteBuffer.annotated.java b/ojluni/annotations/mmodule/java/nio/DirectByteBuffer.annotated.java
index 51e0e9c..8031e93 100644
--- a/ojluni/annotations/mmodule/java/nio/DirectByteBuffer.annotated.java
+++ b/ojluni/annotations/mmodule/java/nio/DirectByteBuffer.annotated.java
@@ -28,108 +28,163 @@
 package java.nio;
 
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class DirectByteBuffer extends java.nio.MappedByteBuffer implements sun.nio.ch.DirectBuffer {
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public DirectByteBuffer(int cap, long addr, java.io.FileDescriptor fd, java.lang.Runnable unmapper, boolean isReadOnly) { super(0, 0, 0, 0); throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.lang.Object attachment() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final sun.misc.Cleaner cleaner() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer slice() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer duplicate() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer asReadOnlyBuffer() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final long address() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final byte get() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final byte get(int i) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.nio.ByteBuffer get(byte[] dst, int dstOffset, int length) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.nio.ByteBuffer put(java.nio.ByteBuffer src) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer put(byte x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer put(int i, byte x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.nio.ByteBuffer put(byte[] src, int srcOffset, int length) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer compact() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final boolean isDirect() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final boolean isReadOnly() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final char getChar() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final char getChar(int i) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putChar(char x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putChar(int i, char x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.CharBuffer asCharBuffer() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final short getShort() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final short getShort(int i) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putShort(short x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putShort(int i, short x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ShortBuffer asShortBuffer() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public int getInt() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public int getInt(int i) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putInt(int x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putInt(int i, int x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.IntBuffer asIntBuffer() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final long getLong() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final long getLong(int i) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putLong(long x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putLong(int i, long x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.LongBuffer asLongBuffer() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final float getFloat() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final float getFloat(int i) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putFloat(float x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putFloat(int i, float x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.FloatBuffer asFloatBuffer() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final double getDouble() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final double getDouble(int i) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putDouble(double x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.ByteBuffer putDouble(int i, double x) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final java.nio.DoubleBuffer asDoubleBuffer() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public final boolean isAccessible() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public final void setAccessible(boolean value) { throw new RuntimeException("Stub!"); }
 }
 
diff --git a/ojluni/annotations/mmodule/java/nio/charset/CharsetDecoder.annotated.java b/ojluni/annotations/mmodule/java/nio/charset/CharsetDecoder.annotated.java
deleted file mode 100644
index ed0f378..0000000
--- a/ojluni/annotations/mmodule/java/nio/charset/CharsetDecoder.annotated.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-// -- This file was mechanically generated: Do not edit! -- //
-
-
-package java.nio.charset;
-
-import java.nio.CharBuffer;
-import java.nio.ByteBuffer;
-import java.nio.Buffer;
-import java.nio.charset.CoderMalfunctionError;
-
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public abstract class CharsetDecoder {
-
-@libcore.api.IntraCoreApi
-protected CharsetDecoder(java.nio.charset.Charset cs, float averageCharsPerByte, float maxCharsPerByte) { throw new RuntimeException("Stub!"); }
-
-public final java.nio.charset.Charset charset() { throw new RuntimeException("Stub!"); }
-
-public final java.lang.String replacement() { throw new RuntimeException("Stub!"); }
-
-public final java.nio.charset.CharsetDecoder replaceWith(java.lang.String newReplacement) { throw new RuntimeException("Stub!"); }
-
-protected void implReplaceWith(java.lang.String newReplacement) { throw new RuntimeException("Stub!"); }
-
-public java.nio.charset.CodingErrorAction malformedInputAction() { throw new RuntimeException("Stub!"); }
-
-public final java.nio.charset.CharsetDecoder onMalformedInput(java.nio.charset.CodingErrorAction newAction) { throw new RuntimeException("Stub!"); }
-
-protected void implOnMalformedInput(java.nio.charset.CodingErrorAction newAction) { throw new RuntimeException("Stub!"); }
-
-public java.nio.charset.CodingErrorAction unmappableCharacterAction() { throw new RuntimeException("Stub!"); }
-
-public final java.nio.charset.CharsetDecoder onUnmappableCharacter(java.nio.charset.CodingErrorAction newAction) { throw new RuntimeException("Stub!"); }
-
-protected void implOnUnmappableCharacter(java.nio.charset.CodingErrorAction newAction) { throw new RuntimeException("Stub!"); }
-
-public final float averageCharsPerByte() { throw new RuntimeException("Stub!"); }
-
-public final float maxCharsPerByte() { throw new RuntimeException("Stub!"); }
-
-public final java.nio.charset.CoderResult decode(java.nio.ByteBuffer in, java.nio.CharBuffer out, boolean endOfInput) { throw new RuntimeException("Stub!"); }
-
-public final java.nio.charset.CoderResult flush(java.nio.CharBuffer out) { throw new RuntimeException("Stub!"); }
-
-protected java.nio.charset.CoderResult implFlush(java.nio.CharBuffer out) { throw new RuntimeException("Stub!"); }
-
-public final java.nio.charset.CharsetDecoder reset() { throw new RuntimeException("Stub!"); }
-
-protected void implReset() { throw new RuntimeException("Stub!"); }
-
-protected abstract java.nio.charset.CoderResult decodeLoop(java.nio.ByteBuffer in, java.nio.CharBuffer out);
-
-public final java.nio.CharBuffer decode(java.nio.ByteBuffer in) throws java.nio.charset.CharacterCodingException { throw new RuntimeException("Stub!"); }
-
-public boolean isAutoDetecting() { throw new RuntimeException("Stub!"); }
-
-public boolean isCharsetDetected() { throw new RuntimeException("Stub!"); }
-
-public java.nio.charset.Charset detectedCharset() { throw new RuntimeException("Stub!"); }
-}
-
diff --git a/ojluni/annotations/mmodule/java/security/Provider.annotated.java b/ojluni/annotations/mmodule/java/security/Provider.annotated.java
index 2fce78b..3ffe654 100644
--- a/ojluni/annotations/mmodule/java/security/Provider.annotated.java
+++ b/ojluni/annotations/mmodule/java/security/Provider.annotated.java
@@ -106,7 +106,8 @@
 
 public boolean isRegistered() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public synchronized void warmUpServiceProvision() { throw new RuntimeException("Stub!"); }
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public static class Service {
diff --git a/ojluni/annotations/mmodule/java/security/Signature.annotated.java b/ojluni/annotations/mmodule/java/security/Signature.annotated.java
index 87323e2..8aaf5c4 100644
--- a/ojluni/annotations/mmodule/java/security/Signature.annotated.java
+++ b/ojluni/annotations/mmodule/java/security/Signature.annotated.java
@@ -86,7 +86,8 @@
 
 public java.lang.Object clone() throws java.lang.CloneNotSupportedException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public java.security.SignatureSpi getCurrentSpi() { throw new RuntimeException("Stub!"); }
 
 protected static final int SIGN = 2; // 0x2
diff --git a/ojluni/annotations/mmodule/java/sun/security/x509/AlgorithmId.annotated.java b/ojluni/annotations/mmodule/java/sun/security/x509/AlgorithmId.annotated.java
deleted file mode 100644
index dc2baf6..0000000
--- a/ojluni/annotations/mmodule/java/sun/security/x509/AlgorithmId.annotated.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.x509;
-import java.io.Serializable;
-import java.security.NoSuchAlgorithmException;
-
-import sun.security.util.DerEncoder;
-import sun.security.util.ObjectIdentifier;
-
-@libcore.api.CorePlatformApi
-@libcore.api.IntraCoreApi
-@libcore.api.Hide
-public class AlgorithmId implements Serializable, DerEncoder {
-
-    @libcore.api.CorePlatformApi
-    public AlgorithmId(ObjectIdentifier oid) {
-        throw new RuntimeException("Stub!");
-    }
-
-    @libcore.api.CorePlatformApi
-    @libcore.api.IntraCoreApi
-    public String getName() {
-        throw new RuntimeException("Stub!");
-    }
-
-    @libcore.api.IntraCoreApi
-    public static AlgorithmId get(String algname) throws NoSuchAlgorithmException {
-        throw new RuntimeException("Stub!");
-    }
-}
diff --git a/ojluni/annotations/mmodule/java/text/DateFormat.annotated.java b/ojluni/annotations/mmodule/java/text/DateFormat.annotated.java
index 5a28d02..12a850e 100644
--- a/ojluni/annotations/mmodule/java/text/DateFormat.annotated.java
+++ b/ojluni/annotations/mmodule/java/text/DateFormat.annotated.java
@@ -83,7 +83,8 @@
 
 public static final java.text.DateFormat getInstance() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static final void set24HourTimePref(java.lang.Boolean is24Hour) { throw new RuntimeException("Stub!"); }
 
 public static java.util.Locale[] getAvailableLocales() { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/util/LinkedHashMap.annotated.java b/ojluni/annotations/mmodule/java/util/LinkedHashMap.annotated.java
index 9f494b6..ada21b1 100644
--- a/ojluni/annotations/mmodule/java/util/LinkedHashMap.annotated.java
+++ b/ojluni/annotations/mmodule/java/util/LinkedHashMap.annotated.java
@@ -48,7 +48,8 @@
 
 public void clear() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public java.util.Map.Entry<K,V> eldest() { throw new RuntimeException("Stub!"); }
 
 protected boolean removeEldestEntry(java.util.Map.Entry<K,V> eldest) { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/util/Locale.annotated.java b/ojluni/annotations/mmodule/java/util/Locale.annotated.java
deleted file mode 100644
index d8aad03..0000000
--- a/ojluni/annotations/mmodule/java/util/Locale.annotated.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
- * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
- *
- * The original version of this source code and documentation
- * is copyrighted and owned by Taligent, Inc., a wholly-owned
- * subsidiary of IBM. These materials are provided under terms
- * of a License Agreement between Taligent and Sun. This technology
- * is protected by multiple US and International patents.
- *
- * This notice and attribution to Taligent may not be removed.
- * Taligent is a registered trademark of Taligent, Inc.
- *
- */
-
-
-package java.util;
-
-import java.text.MessageFormat;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.io.ObjectInputStream;
-
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public final class Locale implements java.lang.Cloneable, java.io.Serializable {
-
-public Locale(java.lang.String language, java.lang.String country, java.lang.String variant) { throw new RuntimeException("Stub!"); }
-
-public Locale(java.lang.String language, java.lang.String country) { throw new RuntimeException("Stub!"); }
-
-public Locale(java.lang.String language) { throw new RuntimeException("Stub!"); }
-
-public static java.util.Locale getDefault() { throw new RuntimeException("Stub!"); }
-
-public static java.util.Locale getDefault(java.util.Locale.Category category) { throw new RuntimeException("Stub!"); }
-
-public static java.util.Locale initDefault() { throw new RuntimeException("Stub!"); }
-
-public static synchronized void setDefault(java.util.Locale newLocale) { throw new RuntimeException("Stub!"); }
-
-public static synchronized void setDefault(java.util.Locale.Category category, java.util.Locale newLocale) { throw new RuntimeException("Stub!"); }
-
-public static java.util.Locale[] getAvailableLocales() { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String[] getISOCountries() { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String[] getISOLanguages() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getLanguage() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getScript() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getCountry() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getVariant() { throw new RuntimeException("Stub!"); }
-
-public boolean hasExtensions() { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale stripExtensions() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getExtension(char key) { throw new RuntimeException("Stub!"); }
-
-public java.util.Set<java.lang.Character> getExtensionKeys() { throw new RuntimeException("Stub!"); }
-
-public java.util.Set<java.lang.String> getUnicodeLocaleAttributes() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getUnicodeLocaleType(java.lang.String key) { throw new RuntimeException("Stub!"); }
-
-public java.util.Set<java.lang.String> getUnicodeLocaleKeys() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String toString() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String toLanguageTag() { throw new RuntimeException("Stub!"); }
-
-public static java.util.Locale forLanguageTag(java.lang.String languageTag) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getISO3Language() throws java.util.MissingResourceException { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getISO3Country() throws java.util.MissingResourceException { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayLanguage() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayLanguage(java.util.Locale locale) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayScript() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayScript(java.util.Locale inLocale) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayCountry() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayCountry(java.util.Locale locale) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayVariant() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayVariant(java.util.Locale inLocale) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayName() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getDisplayName(java.util.Locale locale) { throw new RuntimeException("Stub!"); }
-
-public java.lang.Object clone() { throw new RuntimeException("Stub!"); }
-
-public int hashCode() { throw new RuntimeException("Stub!"); }
-
-public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
-
-@libcore.api.CorePlatformApi
-public static java.lang.String adjustLanguageCode(java.lang.String languageCode) { throw new RuntimeException("Stub!"); }
-
-public static java.util.List<java.util.Locale> filter(java.util.List<java.util.Locale.LanguageRange> priorityList, java.util.Collection<java.util.Locale> locales, java.util.Locale.FilteringMode mode) { throw new RuntimeException("Stub!"); }
-
-public static java.util.List<java.util.Locale> filter(java.util.List<java.util.Locale.LanguageRange> priorityList, java.util.Collection<java.util.Locale> locales) { throw new RuntimeException("Stub!"); }
-
-public static java.util.List<java.lang.String> filterTags(java.util.List<java.util.Locale.LanguageRange> priorityList, java.util.Collection<java.lang.String> tags, java.util.Locale.FilteringMode mode) { throw new RuntimeException("Stub!"); }
-
-public static java.util.List<java.lang.String> filterTags(java.util.List<java.util.Locale.LanguageRange> priorityList, java.util.Collection<java.lang.String> tags) { throw new RuntimeException("Stub!"); }
-
-public static java.util.Locale lookup(java.util.List<java.util.Locale.LanguageRange> priorityList, java.util.Collection<java.util.Locale> locales) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String lookupTag(java.util.List<java.util.Locale.LanguageRange> priorityList, java.util.Collection<java.lang.String> tags) { throw new RuntimeException("Stub!"); }
-
-public static final java.util.Locale CANADA;
-static { CANADA = null; }
-
-public static final java.util.Locale CANADA_FRENCH;
-static { CANADA_FRENCH = null; }
-
-public static final java.util.Locale CHINA;
-static { CHINA = null; }
-
-public static final java.util.Locale CHINESE;
-static { CHINESE = null; }
-
-public static final java.util.Locale ENGLISH;
-static { ENGLISH = null; }
-
-public static final java.util.Locale FRANCE;
-static { FRANCE = null; }
-
-public static final java.util.Locale FRENCH;
-static { FRENCH = null; }
-
-public static final java.util.Locale GERMAN;
-static { GERMAN = null; }
-
-public static final java.util.Locale GERMANY;
-static { GERMANY = null; }
-
-public static final java.util.Locale ITALIAN;
-static { ITALIAN = null; }
-
-public static final java.util.Locale ITALY;
-static { ITALY = null; }
-
-public static final java.util.Locale JAPAN;
-static { JAPAN = null; }
-
-public static final java.util.Locale JAPANESE;
-static { JAPANESE = null; }
-
-public static final java.util.Locale KOREA;
-static { KOREA = null; }
-
-public static final java.util.Locale KOREAN;
-static { KOREAN = null; }
-
-public static final java.util.Locale PRC;
-static { PRC = null; }
-
-public static final char PRIVATE_USE_EXTENSION = 120; // 0x0078 'x'
-
-public static final java.util.Locale ROOT;
-static { ROOT = null; }
-
-public static final java.util.Locale SIMPLIFIED_CHINESE;
-static { SIMPLIFIED_CHINESE = null; }
-
-public static final java.util.Locale TAIWAN;
-static { TAIWAN = null; }
-
-public static final java.util.Locale TRADITIONAL_CHINESE;
-static { TRADITIONAL_CHINESE = null; }
-
-public static final java.util.Locale UK;
-static { UK = null; }
-
-public static final char UNICODE_LOCALE_EXTENSION = 117; // 0x0075 'u'
-
-public static final java.util.Locale US;
-static { US = null; }
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public static final class Builder {
-
-public Builder() { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setLocale(java.util.Locale locale) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setLanguageTag(java.lang.String languageTag) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setLanguage(java.lang.String language) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setScript(java.lang.String script) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setRegion(java.lang.String region) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setVariant(java.lang.String variant) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setExtension(char key, java.lang.String value) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder setUnicodeLocaleKeyword(java.lang.String key, java.lang.String type) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder addUnicodeLocaleAttribute(java.lang.String attribute) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder removeUnicodeLocaleAttribute(java.lang.String attribute) { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder clear() { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale.Builder clearExtensions() { throw new RuntimeException("Stub!"); }
-
-public java.util.Locale build() { throw new RuntimeException("Stub!"); }
-}
-
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public static enum Category {
-DISPLAY,
-FORMAT;
-}
-
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public static enum FilteringMode {
-AUTOSELECT_FILTERING,
-EXTENDED_FILTERING,
-IGNORE_EXTENDED_RANGES,
-MAP_EXTENDED_RANGES,
-REJECT_EXTENDED_RANGES;
-}
-
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public static final class LanguageRange {
-
-public LanguageRange(java.lang.String range) { throw new RuntimeException("Stub!"); }
-
-public LanguageRange(java.lang.String range, double weight) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getRange() { throw new RuntimeException("Stub!"); }
-
-public double getWeight() { throw new RuntimeException("Stub!"); }
-
-public static java.util.List<java.util.Locale.LanguageRange> parse(java.lang.String ranges) { throw new RuntimeException("Stub!"); }
-
-public static java.util.List<java.util.Locale.LanguageRange> parse(java.lang.String ranges, java.util.Map<java.lang.String, java.util.List<java.lang.String>> map) { throw new RuntimeException("Stub!"); }
-
-public static java.util.List<java.util.Locale.LanguageRange> mapEquivalents(java.util.List<java.util.Locale.LanguageRange> priorityList, java.util.Map<java.lang.String, java.util.List<java.lang.String>> map) { throw new RuntimeException("Stub!"); }
-
-public int hashCode() { throw new RuntimeException("Stub!"); }
-
-public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
-
-public static final double MAX_WEIGHT = 1.0;
-
-public static final double MIN_WEIGHT = 0.0;
-}
-
-}
-
diff --git a/ojluni/annotations/mmodule/java/util/concurrent/CompletableFuture.annotated.java b/ojluni/annotations/mmodule/java/util/concurrent/CompletableFuture.annotated.java
index 5ac5a83..0015ff1 100644
--- a/ojluni/annotations/mmodule/java/util/concurrent/CompletableFuture.annotated.java
+++ b/ojluni/annotations/mmodule/java/util/concurrent/CompletableFuture.annotated.java
@@ -175,7 +175,6 @@
 
 public java.util.concurrent.CompletableFuture<T> orTimeout(long timeout, java.util.concurrent.TimeUnit unit) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
 public java.util.concurrent.CompletableFuture<T> completeOnTimeout(T value, long timeout, java.util.concurrent.TimeUnit unit) { throw new RuntimeException("Stub!"); }
 
 public static java.util.concurrent.Executor delayedExecutor(long delay, java.util.concurrent.TimeUnit unit, java.util.concurrent.Executor executor) { throw new RuntimeException("Stub!"); }
@@ -184,7 +183,6 @@
 
 public static <U> java.util.concurrent.CompletionStage<U> completedStage(U value) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
 public static <U> java.util.concurrent.CompletableFuture<U> failedFuture(java.lang.Throwable ex) { throw new RuntimeException("Stub!"); }
 
 public static <U> java.util.concurrent.CompletionStage<U> failedStage(java.lang.Throwable ex) { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/java/util/zip/ZipEntry.annotated.java b/ojluni/annotations/mmodule/java/util/zip/ZipEntry.annotated.java
index 66b3616..6c840f7 100644
--- a/ojluni/annotations/mmodule/java/util/zip/ZipEntry.annotated.java
+++ b/ojluni/annotations/mmodule/java/util/zip/ZipEntry.annotated.java
@@ -38,7 +38,8 @@
 
 public ZipEntry(java.util.zip.ZipEntry e) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public long getDataOffset() { throw new RuntimeException("Stub!"); }
 
 public java.lang.String getName() { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/javax/crypto/Cipher.annotated.java b/ojluni/annotations/mmodule/javax/crypto/Cipher.annotated.java
index bafd946..220eef6 100644
--- a/ojluni/annotations/mmodule/javax/crypto/Cipher.annotated.java
+++ b/ojluni/annotations/mmodule/javax/crypto/Cipher.annotated.java
@@ -27,6 +27,10 @@
 
 package javax.crypto;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import java.util.*;
 import java.util.regex.*;
 import java.security.*;
@@ -116,7 +120,8 @@
 
 public final void updateAAD(java.nio.ByteBuffer src) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = MODULE_LIBRARIES)
 public javax.crypto.CipherSpi getCurrentSpi() { throw new RuntimeException("Stub!"); }
 
 public static final int DECRYPT_MODE = 2; // 0x2
diff --git a/ojluni/annotations/mmodule/javax/crypto/Mac.annotated.java b/ojluni/annotations/mmodule/javax/crypto/Mac.annotated.java
index a7e1995..26bbeff 100644
--- a/ojluni/annotations/mmodule/javax/crypto/Mac.annotated.java
+++ b/ojluni/annotations/mmodule/javax/crypto/Mac.annotated.java
@@ -27,6 +27,10 @@
 
 package javax.crypto;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
+
 import java.util.*;
 import java.security.*;
 import sun.security.jca.*;
@@ -72,7 +76,8 @@
 
 public final java.lang.Object clone() throws java.lang.CloneNotSupportedException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = MODULE_LIBRARIES)
 public javax.crypto.MacSpi getCurrentSpi() { throw new RuntimeException("Stub!"); }
 }
 
diff --git a/ojluni/annotations/mmodule/javax/net/ssl/HttpsURLConnection.annotated.java b/ojluni/annotations/mmodule/javax/net/ssl/HttpsURLConnection.annotated.java
new file mode 100644
index 0000000..5177ba3
--- /dev/null
+++ b/ojluni/annotations/mmodule/javax/net/ssl/HttpsURLConnection.annotated.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javax.net.ssl;
+
+abstract public class HttpsURLConnection extends HttpURLConnection {
+    public abstract java.lang.String getCipherSuite();
+
+    public abstract java.security.cert.Certificate [] getLocalCertificates();
+
+    public abstract java.security.cert.Certificate [] getServerCertificates() throws javax.net.ssl.SSLPeerUnverifiedException;
+
+    public java.security.Principal getPeerPrincipal() throws javax.net.ssl.SSLPeerUnverifiedException { throw new RuntimeException("Stub!"); }
+
+    public java.security.Principal getLocalPrincipal() { throw new RuntimeException("Stub!"); }
+
+    public static void setDefaultHostnameVerifier(javax.net.ssl.HostnameVerifier v) { throw new RuntimeException("Stub!"); }
+
+    public static javax.net.ssl.HostnameVerifier getDefaultHostnameVerifier() { throw new RuntimeException("Stub!"); }
+
+    public void setHostnameVerifier(javax.net.ssl.HostnameVerifier v) { throw new RuntimeException("Stub!"); }
+
+    @android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+    public static javax.net.ssl.HostnameVerifier getStrictHostnameVerifier() { throw new RuntimeException("Stub!"); }
+
+    public javax.net.ssl.HostnameVerifier getHostnameVerifier() { throw new RuntimeException("Stub!"); }
+
+    public static void setDefaultSSLSocketFactory(javax.net.ssl.SSLSocketFactory sf) { throw new RuntimeException("Stub!"); }
+
+    public static javax.net.ssl.SSLSocketFactory getDefaultSSLSocketFactory() { throw new RuntimeException("Stub!"); }
+
+    public void setSSLSocketFactory(javax.net.ssl.SSLSocketFactory sf) { throw new RuntimeException("Stub!"); }
+
+    public javax.net.ssl.SSLSocketFactory getSSLSocketFactory() { throw new RuntimeException("Stub!"); }
+}
diff --git a/ojluni/annotations/mmodule/sun/misc/Cleaner.annotated.java b/ojluni/annotations/mmodule/sun/misc/Cleaner.annotated.java
index b09384a..06dddd4 100644
--- a/ojluni/annotations/mmodule/sun/misc/Cleaner.annotated.java
+++ b/ojluni/annotations/mmodule/sun/misc/Cleaner.annotated.java
@@ -28,16 +28,22 @@
 
 import java.lang.ref.*;
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.Hide
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class Cleaner extends java.lang.ref.PhantomReference<java.lang.Object> {
 
+@libcore.api.Hide
 Cleaner(java.lang.Object referent, java.lang.Runnable thunk) { super(null, null); throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public static sun.misc.Cleaner create(java.lang.Object ob, java.lang.Runnable thunk) { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public void clean() { throw new RuntimeException("Stub!"); }
 }
diff --git a/ojluni/annotations/mmodule/sun/misc/Unsafe.annotated.java b/ojluni/annotations/mmodule/sun/misc/Unsafe.annotated.java
index a6711e0..f6e755e 100644
--- a/ojluni/annotations/mmodule/sun/misc/Unsafe.annotated.java
+++ b/ojluni/annotations/mmodule/sun/misc/Unsafe.annotated.java
@@ -23,99 +23,278 @@
  * questions.
  */
 
+
 package sun.misc;
 
-// sun.misc.Unsafe is part of the Core Platform API as platform uses protobuf and protobuf uses
-// this API for fast structure updates from native code (b/144502743).
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.Hide
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class Unsafe {
 
-    private Unsafe() {
-        throw new RuntimeException("Stub!");
-    }
+private Unsafe() { throw new RuntimeException("Stub!"); }
 
-    @libcore.api.CorePlatformApi
-    public static sun.misc.Unsafe getUnsafe() {
-        throw new RuntimeException("Stub!");
-    }
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public static sun.misc.Unsafe getUnsafe() { throw new RuntimeException("Stub!"); }
 
-    @libcore.api.CorePlatformApi
-    public int arrayBaseOffset(Class clazz) {
-        throw new RuntimeException("Stub!");
-    }
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public long objectFieldOffset(java.lang.reflect.Field field) { throw new RuntimeException("Stub!"); }
 
-    @libcore.api.CorePlatformApi
-    public int arrayIndexScale(Class clazz) {
-        throw new RuntimeException("Stub!");
-    }
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public int arrayBaseOffset(java.lang.Class clazz) { throw new RuntimeException("Stub!"); }
 
-    @libcore.api.CorePlatformApi
-    public native void copyMemory(long srcAddr, long destAddr, long bytes);
 
-    @libcore.api.CorePlatformApi
-    public native boolean getBoolean(Object obj, long offset);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public int arrayIndexScale(java.lang.Class clazz) { throw new RuntimeException("Stub!"); }
 
-    @libcore.api.CorePlatformApi
-    public native byte getByte(long address);
+@libcore.api.Hide
+public native boolean compareAndSwapInt(java.lang.Object obj, long offset, int expectedValue, int newValue);
 
-    @libcore.api.CorePlatformApi
-    public native byte getByte(Object obj, long offset);
+@libcore.api.Hide
+public native boolean compareAndSwapLong(java.lang.Object obj, long offset, long expectedValue, long newValue);
 
-    @libcore.api.CorePlatformApi
-    public native double getDouble(Object obj, long offset);
+@libcore.api.Hide
+public native boolean compareAndSwapObject(java.lang.Object obj, long offset, java.lang.Object expectedValue, java.lang.Object newValue);
 
-    @libcore.api.CorePlatformApi
-    public native float getFloat(Object obj, long offset);
+@libcore.api.Hide
+public native int getIntVolatile(java.lang.Object obj, long offset);
 
-    @libcore.api.CorePlatformApi
-    public native int getInt(long address);
+@libcore.api.Hide
+public native void putIntVolatile(java.lang.Object obj, long offset, int newValue);
 
-    @libcore.api.CorePlatformApi
-    public native int getInt(Object obj, long offset);
+@libcore.api.Hide
+public native long getLongVolatile(java.lang.Object obj, long offset);
 
-    @libcore.api.CorePlatformApi
-    public native long getLong(long address);
+@libcore.api.Hide
+public native void putLongVolatile(java.lang.Object obj, long offset, long newValue);
 
-    @libcore.api.CorePlatformApi
-    public native long getLong(Object obj, long offset);
+@libcore.api.Hide
+public native void putObjectVolatile(java.lang.Object obj, long offset, java.lang.Object newValue);
 
-    @libcore.api.CorePlatformApi
-    public native Object getObject(Object obj, long offset);
+@libcore.api.Hide
+public native java.lang.Object getObjectVolatile(java.lang.Object obj, long offset);
 
-    @libcore.api.CorePlatformApi
-    public long objectFieldOffset(java.lang.reflect.Field field) {
-        throw new RuntimeException("Stub!");
-    }
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native int getInt(java.lang.Object obj, long offset);
 
-    @libcore.api.CorePlatformApi
-    public native void putBoolean(Object obj, long offset, boolean newValue);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putInt(java.lang.Object obj, long offset, int newValue);
 
-    @libcore.api.CorePlatformApi
-    public native void putByte(long address, byte newValue);
+@libcore.api.Hide
+public native void putOrderedInt(java.lang.Object obj, long offset, int newValue);
 
-    @libcore.api.CorePlatformApi
-    public native void putByte(Object obj, long offset, byte newValue);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native long getLong(java.lang.Object obj, long offset);
 
-    @libcore.api.CorePlatformApi
-    public native void putDouble(Object obj, long offset, double newValue);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putLong(java.lang.Object obj, long offset, long newValue);
 
-    @libcore.api.CorePlatformApi
-    public native void putFloat(Object obj, long offset, float newValue);
+@libcore.api.Hide
+public native void putOrderedLong(java.lang.Object obj, long offset, long newValue);
 
-    @libcore.api.CorePlatformApi
-    public native void putInt(long address, int newValue);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native java.lang.Object getObject(java.lang.Object obj, long offset);
 
-    @libcore.api.CorePlatformApi
-    public native void putInt(Object obj, long offset, int newValue);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putObject(java.lang.Object obj, long offset, java.lang.Object newValue);
 
-    @libcore.api.CorePlatformApi
-    public native void putLong(long address, long newValue);
+@libcore.api.Hide
+public native void putOrderedObject(java.lang.Object obj, long offset, java.lang.Object newValue);
 
-    @libcore.api.CorePlatformApi
-    public native void putLong(Object obj, long offset, long newValue);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native boolean getBoolean(java.lang.Object obj, long offset);
 
-    @libcore.api.CorePlatformApi
-    public native void putObject(Object obj, long offset, Object newValue);
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putBoolean(java.lang.Object obj, long offset, boolean newValue);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native byte getByte(java.lang.Object obj, long offset);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putByte(java.lang.Object obj, long offset, byte newValue);
+
+@libcore.api.Hide
+public native char getChar(java.lang.Object obj, long offset);
+
+@libcore.api.Hide
+public native void putChar(java.lang.Object obj, long offset, char newValue);
+
+@libcore.api.Hide
+public native short getShort(java.lang.Object obj, long offset);
+
+@libcore.api.Hide
+public native void putShort(java.lang.Object obj, long offset, short newValue);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native float getFloat(java.lang.Object obj, long offset);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putFloat(java.lang.Object obj, long offset, float newValue);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native double getDouble(java.lang.Object obj, long offset);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putDouble(java.lang.Object obj, long offset, double newValue);
+
+@libcore.api.Hide
+public native void park(boolean absolute, long time);
+
+@libcore.api.Hide
+public native void unpark(java.lang.Object obj);
+
+@libcore.api.Hide
+public native java.lang.Object allocateInstance(java.lang.Class<?> c);
+
+@libcore.api.Hide
+public native int addressSize();
+
+@libcore.api.Hide
+public native int pageSize();
+
+@libcore.api.Hide
+public native long allocateMemory(long bytes);
+
+@libcore.api.Hide
+public native void freeMemory(long address);
+
+@libcore.api.Hide
+public native void setMemory(long address, long bytes, byte value);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native byte getByte(long address);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putByte(long address, byte x);
+
+@libcore.api.Hide
+public native short getShort(long address);
+
+@libcore.api.Hide
+public native void putShort(long address, short x);
+
+@libcore.api.Hide
+public native char getChar(long address);
+
+@libcore.api.Hide
+public native void putChar(long address, char x);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native int getInt(long address);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putInt(long address, int x);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native long getLong(long address);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putLong(long address, long x);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native float getFloat(long address);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putFloat(long address, float x);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native double getDouble(long address);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void putDouble(long address, double x);
+
+@libcore.api.Hide
+public native void copyMemoryToPrimitiveArray(long srcAddr, java.lang.Object dst, long dstOffset, long bytes);
+
+@libcore.api.Hide
+public native void copyMemoryFromPrimitiveArray(java.lang.Object src, long srcOffset, long dstAddr, long bytes);
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public native void copyMemory(long srcAddr, long dstAddr, long bytes);
+
+@libcore.api.Hide
+public int getAndAddInt(java.lang.Object o, long offset, int delta) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public long getAndAddLong(java.lang.Object o, long offset, long delta) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public int getAndSetInt(java.lang.Object o, long offset, int newValue) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public long getAndSetLong(java.lang.Object o, long offset, long newValue) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public java.lang.Object getAndSetObject(java.lang.Object o, long offset, java.lang.Object newValue) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public native void loadFence();
+
+@libcore.api.Hide
+public native void storeFence();
+
+@libcore.api.Hide
+public native void fullFence();
+
+@libcore.api.Hide
+public static final int INVALID_FIELD_OFFSET = -1; // 0xffffffff
 }
+
diff --git a/ojluni/annotations/mmodule/sun/security/jca/Providers.annotated.java b/ojluni/annotations/mmodule/sun/security/jca/Providers.annotated.java
index ca8716b..476ebb5 100644
--- a/ojluni/annotations/mmodule/sun/security/jca/Providers.annotated.java
+++ b/ojluni/annotations/mmodule/sun/security/jca/Providers.annotated.java
@@ -31,41 +31,58 @@
 import java.util.Set;
 import java.security.NoSuchAlgorithmException;
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.Hide
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class Providers {
 
+@libcore.api.Hide
 Providers() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static java.security.Provider getSunProvider() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public static java.lang.Object startJarVerification() { throw new RuntimeException("Stub!"); }
 
-
-  @libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public static void stopJarVerification(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static sun.security.jca.ProviderList getProviderList() { throw new RuntimeException("Stub!"); }
 
-public static void setProviderList(sun.security.jca.ProviderList newList) { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public static void setProviderList(ProviderList newList) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static sun.security.jca.ProviderList getFullProviderList() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static sun.security.jca.ProviderList getThreadProviderList() { throw new RuntimeException("Stub!"); }
 
-public static synchronized sun.security.jca.ProviderList beginThreadProviderList(sun.security.jca.ProviderList list) { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public static synchronized sun.security.jca.ProviderList beginThreadProviderList(ProviderList list) { throw new RuntimeException("Stub!"); }
 
-public static synchronized void endThreadProviderList(sun.security.jca.ProviderList list) { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public static synchronized void endThreadProviderList(ProviderList list) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static void setMaximumAllowableApiLevelForBcDeprecation(int targetApiLevel) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static int getMaximumAllowableApiLevelForBcDeprecation() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static synchronized void checkBouncyCastleDeprecation(java.lang.String provider, java.lang.String service, java.lang.String algorithm) throws java.security.NoSuchAlgorithmException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static synchronized void checkBouncyCastleDeprecation(java.security.Provider provider, java.lang.String service, java.lang.String algorithm) throws java.security.NoSuchAlgorithmException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static final int DEFAULT_MAXIMUM_ALLOWABLE_TARGET_API_LEVEL_FOR_BC_DEPRECATION = 27; // 0x1b
 }
diff --git a/ojluni/annotations/mmodule/sun/security/pkcs/ContentInfo.annotated.java b/ojluni/annotations/mmodule/sun/security/pkcs/ContentInfo.annotated.java
index c4a0a21..cc946ba 100644
--- a/ojluni/annotations/mmodule/sun/security/pkcs/ContentInfo.annotated.java
+++ b/ojluni/annotations/mmodule/sun/security/pkcs/ContentInfo.annotated.java
@@ -36,7 +36,6 @@
  */
 
 @SuppressWarnings({"unchecked", "deprecation", "all"})
-@libcore.api.CorePlatformApi
 @libcore.api.Hide
 public class ContentInfo {
 
@@ -80,7 +79,6 @@
  * the content field.
  */
 
-@libcore.api.CorePlatformApi
 public byte[] getContentBytes() throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
 public java.lang.String toString() { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/mmodule/sun/security/pkcs/PKCS7.annotated.java b/ojluni/annotations/mmodule/sun/security/pkcs/PKCS7.annotated.java
index 2d5cb84..3bb1a66 100644
--- a/ojluni/annotations/mmodule/sun/security/pkcs/PKCS7.annotated.java
+++ b/ojluni/annotations/mmodule/sun/security/pkcs/PKCS7.annotated.java
@@ -30,59 +30,86 @@
 import java.io.*;
 import java.util.*;
 import java.security.*;
+import java.security.cert.X509Certificate;
 import sun.security.util.*;
 import sun.security.x509.AlgorithmId;
 import sun.security.x509.X500Name;
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.Hide
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class PKCS7 {
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public PKCS7(java.io.InputStream in) throws java.io.IOException, sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
 
-public PKCS7(sun.security.util.DerInputStream derin) throws sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public PKCS7(DerInputStream derin) throws sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public PKCS7(byte[] bytes) throws sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public PKCS7(sun.security.x509.AlgorithmId[] digestAlgorithmIds, sun.security.pkcs.ContentInfo contentInfo, java.security.cert.X509Certificate[] certificates, java.security.cert.X509CRL[] crls, sun.security.pkcs.SignerInfo[] signerInfos) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public PKCS7(sun.security.x509.AlgorithmId[] digestAlgorithmIds, sun.security.pkcs.ContentInfo contentInfo, java.security.cert.X509Certificate[] certificates, sun.security.pkcs.SignerInfo[] signerInfos) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public void encodeSignedData(java.io.OutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
-public void encodeSignedData(sun.security.util.DerOutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public void encodeSignedData(DerOutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.pkcs.SignerInfo verify(sun.security.pkcs.SignerInfo info, byte[] bytes) throws java.security.NoSuchAlgorithmException, java.security.SignatureException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public sun.security.pkcs.SignerInfo verify(sun.security.pkcs.SignerInfo info, java.io.InputStream dataInputStream) throws java.io.IOException, java.security.NoSuchAlgorithmException, java.security.SignatureException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public sun.security.pkcs.SignerInfo[] verify(byte[] bytes) throws java.security.NoSuchAlgorithmException, java.security.SignatureException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.pkcs.SignerInfo[] verify() throws java.security.NoSuchAlgorithmException, java.security.SignatureException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.math.BigInteger getVersion() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.x509.AlgorithmId[] getDigestAlgorithmIds() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
 public sun.security.pkcs.ContentInfo getContentInfo() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@libcore.api.Hide
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public java.security.cert.X509Certificate[] getCertificates() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.security.cert.X509CRL[] getCRLs() { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public sun.security.pkcs.SignerInfo[] getSignerInfos() { throw new RuntimeException("Stub!"); }
 
-public java.security.cert.X509Certificate getCertificate(java.math.BigInteger serial, sun.security.x509.X500Name issuerName) { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public X509Certificate getCertificate(java.math.BigInteger serial, X500Name issuerName) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.lang.String toString() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public boolean isOldStyle() { throw new RuntimeException("Stub!"); }
 }
diff --git a/ojluni/annotations/mmodule/sun/security/pkcs/ParsingException.annotated.java b/ojluni/annotations/mmodule/sun/security/pkcs/ParsingException.annotated.java
index ab44b06..bdc70dc 100644
--- a/ojluni/annotations/mmodule/sun/security/pkcs/ParsingException.annotated.java
+++ b/ojluni/annotations/mmodule/sun/security/pkcs/ParsingException.annotated.java
@@ -32,12 +32,15 @@
 
 package sun.security.pkcs;
 
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.Hide
-@libcore.api.CorePlatformApi
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class ParsingException extends java.io.IOException {
 
+@libcore.api.Hide
 public ParsingException() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public ParsingException(java.lang.String s) { throw new RuntimeException("Stub!"); }
 }
diff --git a/ojluni/annotations/mmodule/sun/security/pkcs/SignerInfo.annotated.java b/ojluni/annotations/mmodule/sun/security/pkcs/SignerInfo.annotated.java
index b929e2e..6a85427 100644
--- a/ojluni/annotations/mmodule/sun/security/pkcs/SignerInfo.annotated.java
+++ b/ojluni/annotations/mmodule/sun/security/pkcs/SignerInfo.annotated.java
@@ -28,52 +28,77 @@
 package sun.security.pkcs;
 
 import sun.security.util.DerEncoder;
+import sun.security.util.DerInputStream;
+import sun.security.x509.X500Name;
 import java.io.IOException;
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.Hide
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public class SignerInfo implements sun.security.util.DerEncoder {
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public SignerInfo() { throw new RuntimeException("Stub!"); }
 
-public SignerInfo(sun.security.x509.X500Name issuerName, java.math.BigInteger serial, sun.security.x509.AlgorithmId digestAlgorithmId, sun.security.x509.AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest) { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public SignerInfo(X500Name issuerName, java.math.BigInteger serial, sun.security.x509.AlgorithmId digestAlgorithmId, sun.security.x509.AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest) { throw new RuntimeException("Stub!"); }
 
-public SignerInfo(sun.security.x509.X500Name issuerName, java.math.BigInteger serial, sun.security.x509.AlgorithmId digestAlgorithmId, sun.security.pkcs.PKCS9Attributes authenticatedAttributes, sun.security.x509.AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest, sun.security.pkcs.PKCS9Attributes unauthenticatedAttributes) { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public SignerInfo(X500Name issuerName, java.math.BigInteger serial, sun.security.x509.AlgorithmId digestAlgorithmId, PKCS9Attributes authenticatedAttributes, sun.security.x509.AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest, PKCS9Attributes unauthenticatedAttributes) { throw new RuntimeException("Stub!"); }
 
-public SignerInfo(sun.security.util.DerInputStream derin) throws java.io.IOException, sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public SignerInfo(DerInputStream derin) throws java.io.IOException, sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
 
-public SignerInfo(sun.security.util.DerInputStream derin, boolean oldStyle) throws java.io.IOException, sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public SignerInfo(DerInputStream derin, boolean oldStyle) throws java.io.IOException, sun.security.pkcs.ParsingException { throw new RuntimeException("Stub!"); }
 
-public void encode(sun.security.util.DerOutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public void encode(DerOutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public void derEncode(java.io.OutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.security.cert.X509Certificate getCertificate(sun.security.pkcs.PKCS7 block) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public java.util.ArrayList<java.security.cert.X509Certificate> getCertificateChain(sun.security.pkcs.PKCS7 block) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.math.BigInteger getVersion() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.x509.X500Name getIssuerName() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.math.BigInteger getCertificateSerialNumber() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.x509.AlgorithmId getDigestAlgorithmId() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.pkcs.PKCS9Attributes getAuthenticatedAttributes() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.x509.AlgorithmId getDigestEncryptionAlgorithmId() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public byte[] getEncryptedDigest() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.pkcs.PKCS9Attributes getUnauthenticatedAttributes() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public sun.security.pkcs.PKCS7 getTsToken() throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.security.Timestamp getTimestamp() throws java.security.cert.CertificateException, java.io.IOException, java.security.NoSuchAlgorithmException, java.security.SignatureException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.lang.String toString() { throw new RuntimeException("Stub!"); }
 }
diff --git a/ojluni/annotations/mmodule/java/sun/security/util/DerEncoder.annotated.java b/ojluni/annotations/mmodule/sun/security/util/DerEncoder.annotated.java
similarity index 100%
rename from ojluni/annotations/mmodule/java/sun/security/util/DerEncoder.annotated.java
rename to ojluni/annotations/mmodule/sun/security/util/DerEncoder.annotated.java
diff --git a/ojluni/annotations/mmodule/java/sun/security/util/ObjectIdentifier.annotated.java b/ojluni/annotations/mmodule/sun/security/util/ObjectIdentifier.annotated.java
similarity index 77%
rename from ojluni/annotations/mmodule/java/sun/security/util/ObjectIdentifier.annotated.java
rename to ojluni/annotations/mmodule/sun/security/util/ObjectIdentifier.annotated.java
index d1d5eac..2892b72 100644
--- a/ojluni/annotations/mmodule/java/sun/security/util/ObjectIdentifier.annotated.java
+++ b/ojluni/annotations/mmodule/sun/security/util/ObjectIdentifier.annotated.java
@@ -26,32 +26,40 @@
 
 package sun.security.util;
 
-import java.io.*;
-import java.math.BigInteger;
-
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 @libcore.api.Hide
 @SuppressWarnings({"unchecked", "deprecation", "all"})
 public final class ObjectIdentifier implements java.io.Serializable {
 
-@libcore.api.CorePlatformApi
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
 public ObjectIdentifier(java.lang.String oid) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public ObjectIdentifier(int[] values) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
-public ObjectIdentifier(sun.security.util.DerInputStream in) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+@libcore.api.Hide
+public ObjectIdentifier(DerInputStream in) throws java.io.IOException { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public static sun.security.util.ObjectIdentifier newInternal(int[] values) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 @Deprecated
 public boolean equals(sun.security.util.ObjectIdentifier other) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public boolean equals(java.lang.Object obj) { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public int hashCode() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public int[] toIntArray() { throw new RuntimeException("Stub!"); }
 
+@libcore.api.Hide
 public java.lang.String toString() { throw new RuntimeException("Stub!"); }
 }
 
diff --git a/ojluni/annotations/mmodule/sun/security/x509/AlgorithmId.annotated.java b/ojluni/annotations/mmodule/sun/security/x509/AlgorithmId.annotated.java
new file mode 100644
index 0000000..0399caa
--- /dev/null
+++ b/ojluni/annotations/mmodule/sun/security/x509/AlgorithmId.annotated.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package sun.security.x509;
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.IntraCoreApi
+@libcore.api.Hide
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class AlgorithmId implements java.io.Serializable {
+
+@libcore.api.Hide
+@Deprecated
+public AlgorithmId() { throw new RuntimeException("Stub!"); }
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.Hide
+public AlgorithmId(sun.security.util.ObjectIdentifier oid) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public AlgorithmId(sun.security.util.ObjectIdentifier oid, java.security.AlgorithmParameters algparams) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+protected void decodeParams() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public final void encode(DerOutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.IntraCoreApi
+@libcore.api.Hide
+public void derEncode(java.io.OutputStream out) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public final byte[] encode() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public final sun.security.util.ObjectIdentifier getOID() { throw new RuntimeException("Stub!"); }
+
+@android.annotation.SystemApi(client = android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@libcore.api.IntraCoreApi
+@libcore.api.Hide
+public java.lang.String getName() { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public java.security.AlgorithmParameters getParameters() { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public byte[] getEncodedParams() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public boolean equals(sun.security.x509.AlgorithmId other) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public boolean equals(java.lang.Object other) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public final boolean equals(sun.security.util.ObjectIdentifier id) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public int hashCode() { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+protected java.lang.String paramsToString() { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public static sun.security.x509.AlgorithmId parse(DerValue val) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+@Deprecated
+public static sun.security.x509.AlgorithmId getAlgorithmId(java.lang.String algname) throws java.security.NoSuchAlgorithmException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.IntraCoreApi
+@libcore.api.Hide
+public static sun.security.x509.AlgorithmId get(java.lang.String algname) throws java.security.NoSuchAlgorithmException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public static sun.security.x509.AlgorithmId get(java.security.AlgorithmParameters algparams) throws java.security.NoSuchAlgorithmException { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public static java.lang.String makeSigAlg(java.lang.String digAlg, java.lang.String encAlg) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public static java.lang.String getEncAlgFromSigAlg(java.lang.String signatureAlgorithm) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public static java.lang.String getDigAlgFromSigAlg(java.lang.String signatureAlgorithm) { throw new RuntimeException("Stub!"); }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier AES_oid;
+static { AES_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier DH_PKIX_oid;
+static { DH_PKIX_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier DH_oid;
+static { DH_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier DSA_OIW_oid;
+static { DSA_OIW_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier DSA_oid;
+static { DSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier ECDH_oid;
+static { ECDH_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier EC_oid;
+static { EC_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier MD2_oid;
+static { MD2_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier MD5_oid;
+static { MD5_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier RSAEncryption_oid;
+static { RSAEncryption_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier RSA_oid;
+static { RSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier SHA224_oid;
+static { SHA224_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier SHA256_oid;
+static { SHA256_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier SHA384_oid;
+static { SHA384_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier SHA512_oid;
+static { SHA512_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier SHA_oid;
+static { SHA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier md2WithRSAEncryption_oid;
+static { md2WithRSAEncryption_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier md5WithRSAEncryption_oid;
+static { md5WithRSAEncryption_oid = null; }
+
+@libcore.api.Hide
+protected DerValue params;
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier pbeWithMD5AndDES_oid;
+static { pbeWithMD5AndDES_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier pbeWithMD5AndRC2_oid;
+static { pbeWithMD5AndRC2_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier pbeWithSHA1AndDES_oid;
+static { pbeWithSHA1AndDES_oid = null; }
+
+@libcore.api.Hide
+public static sun.security.util.ObjectIdentifier pbeWithSHA1AndDESede_oid;
+
+@libcore.api.Hide
+public static sun.security.util.ObjectIdentifier pbeWithSHA1AndRC2_40_oid;
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier pbeWithSHA1AndRC2_oid;
+static { pbeWithSHA1AndRC2_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha1WithDSA_OIW_oid;
+static { sha1WithDSA_OIW_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha1WithDSA_oid;
+static { sha1WithDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha1WithECDSA_oid;
+static { sha1WithECDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha1WithRSAEncryption_OIW_oid;
+static { sha1WithRSAEncryption_OIW_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha1WithRSAEncryption_oid;
+static { sha1WithRSAEncryption_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha224WithDSA_oid;
+static { sha224WithDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha224WithECDSA_oid;
+static { sha224WithECDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha224WithRSAEncryption_oid;
+static { sha224WithRSAEncryption_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha256WithDSA_oid;
+static { sha256WithDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha256WithECDSA_oid;
+static { sha256WithECDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha256WithRSAEncryption_oid;
+static { sha256WithRSAEncryption_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha384WithECDSA_oid;
+static { sha384WithECDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha384WithRSAEncryption_oid;
+static { sha384WithRSAEncryption_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha512WithECDSA_oid;
+static { sha512WithECDSA_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier sha512WithRSAEncryption_oid;
+static { sha512WithRSAEncryption_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier shaWithDSA_OIW_oid;
+static { shaWithDSA_OIW_oid = null; }
+
+@libcore.api.Hide
+public static final sun.security.util.ObjectIdentifier specifiedWithECDSA_oid;
+static { specifiedWithECDSA_oid = null; }
+}
+
diff --git a/ojluni/annotations/mmodule/sun/util/locale/LanguageTag.annotated.java b/ojluni/annotations/mmodule/sun/util/locale/LanguageTag.annotated.java
deleted file mode 100644
index eaec071..0000000
--- a/ojluni/annotations/mmodule/sun/util/locale/LanguageTag.annotated.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- *******************************************************************************
- * Copyright (C) 2010, International Business Machines Corporation and         *
- * others. All Rights Reserved.                                                *
- *******************************************************************************
- */
-
-package sun.util.locale;
-
-@libcore.api.Hide
-@libcore.api.CorePlatformApi
-@SuppressWarnings({"unchecked", "deprecation", "all"})
-public class LanguageTag {
-
-LanguageTag() { throw new RuntimeException("Stub!"); }
-
-public static sun.util.locale.LanguageTag parse(java.lang.String languageTag, sun.util.locale.ParseStatus sts) { throw new RuntimeException("Stub!"); }
-
-public static sun.util.locale.LanguageTag parseLocale(sun.util.locale.BaseLocale baseLocale, sun.util.locale.LocaleExtensions localeExtensions) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getLanguage() { throw new RuntimeException("Stub!"); }
-
-public java.util.List<java.lang.String> getExtlangs() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getScript() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getRegion() { throw new RuntimeException("Stub!"); }
-
-public java.util.List<java.lang.String> getVariants() { throw new RuntimeException("Stub!"); }
-
-public java.util.List<java.lang.String> getExtensions() { throw new RuntimeException("Stub!"); }
-
-public java.lang.String getPrivateuse() { throw new RuntimeException("Stub!"); }
-
-@libcore.api.CorePlatformApi
-public static boolean isLanguage(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isExtlang(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isScript(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isRegion(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isVariant(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isExtensionSingleton(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isExtensionSingletonChar(char c) { throw new RuntimeException("Stub!"); }
-
-public static boolean isExtensionSubtag(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isPrivateusePrefix(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static boolean isPrivateusePrefixChar(char c) { throw new RuntimeException("Stub!"); }
-
-public static boolean isPrivateuseSubtag(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeLanguage(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeExtlang(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeScript(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeRegion(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeVariant(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeExtension(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeExtensionSingleton(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizeExtensionSubtag(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizePrivateuse(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public static java.lang.String canonicalizePrivateuseSubtag(java.lang.String s) { throw new RuntimeException("Stub!"); }
-
-public java.lang.String toString() { throw new RuntimeException("Stub!"); }
-
-public static final java.lang.String PRIVATEUSE = "x";
-
-public static final java.lang.String PRIVUSE_VARIANT_PREFIX = "lvariant";
-
-public static final java.lang.String SEP = "-";
-
-public static final java.lang.String UNDETERMINED = "und";
-}
-
diff --git a/ojluni/annotations/sdk/nullability/java/lang/Class.annotated.java b/ojluni/annotations/sdk/nullability/java/lang/Class.annotated.java
index cb99f54..ab0cc39 100644
--- a/ojluni/annotations/sdk/nullability/java/lang/Class.annotated.java
+++ b/ojluni/annotations/sdk/nullability/java/lang/Class.annotated.java
@@ -81,6 +81,8 @@
 
 @libcore.util.Nullable public java.lang.Package getPackage() { throw new RuntimeException("Stub!"); }
 
+@libcore.util.NonNull public java.lang.String getPackageName() { throw new RuntimeException("Stub!"); }
+
 public java.lang.@libcore.util.NonNull Class<?> @libcore.util.NonNull [] getInterfaces() { throw new RuntimeException("Stub!"); }
 
 public java.lang.reflect.@libcore.util.NonNull Type @libcore.util.NonNull [] getGenericInterfaces() { throw new RuntimeException("Stub!"); }
diff --git a/ojluni/annotations/sdk/nullability/java/net/InetAddress.annotated.java b/ojluni/annotations/sdk/nullability/java/net/InetAddress.annotated.java
new file mode 100644
index 0000000..967ea83
--- /dev/null
+++ b/ojluni/annotations/sdk/nullability/java/net/InetAddress.annotated.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package java.net;
+
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public class InetAddress implements java.io.Serializable {
+
+InetAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isMulticastAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isAnyLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLoopbackAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isLinkLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isSiteLocalAddress() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCGlobal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCNodeLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCLinkLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCSiteLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isMCOrgLocal() { throw new RuntimeException("Stub!"); }
+
+public boolean isReachable(int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+public boolean isReachable(@libcore.util.Nullable java.net.NetworkInterface netif, int ttl, int timeout) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public java.lang.String getHostName() { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public java.lang.String getCanonicalHostName() { throw new RuntimeException("Stub!"); }
+
+public byte[] getAddress() { throw new RuntimeException("Stub!"); }
+
+@libcore.util.Nullable public java.lang.String getHostAddress() { throw new RuntimeException("Stub!"); }
+
+public int hashCode() { throw new RuntimeException("Stub!"); }
+
+public boolean equals(@libcore.util.Nullable java.lang.Object obj) { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public java.lang.String toString() { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static java.net.InetAddress getByAddress(@libcore.util.Nullable java.lang.String host, byte[] addr) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static java.net.InetAddress getByName(@libcore.util.Nullable java.lang.String host) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+public static java.net.InetAddress[] getAllByName(@libcore.util.Nullable java.lang.String host) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static java.net.InetAddress getLoopbackAddress() { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static java.net.InetAddress getByAddress(byte[] addr) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static java.net.InetAddress getLocalHost() throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static java.net.InetAddress getByNameOnNet(@libcore.util.Nullable java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static java.net.InetAddress[] getAllByNameOnNet(@libcore.util.Nullable java.lang.String host, int netId) throws java.net.UnknownHostException { throw new RuntimeException("Stub!"); }
+}
+
diff --git a/ojluni/annotations/sdk/nullability/java/util/List.annotated.java b/ojluni/annotations/sdk/nullability/java/util/List.annotated.java
index 2ef4a6f..9583d18 100644
--- a/ojluni/annotations/sdk/nullability/java/util/List.annotated.java
+++ b/ojluni/annotations/sdk/nullability/java/util/List.annotated.java
@@ -111,4 +111,6 @@
 
 @java.lang.SafeVarargs
 @libcore.util.NonNull public static <E> java.util.List<@libcore.util.NonNull E> of(E @libcore.util.NonNull ... elements) { throw new RuntimeException("Stub!"); }
-}
+
+@libcore.util.NonNull public static <E> java.util.List<@libcore.util.NonNull E> copyOf(@libcore.util.NonNull java.util.Collection<? extends E> coll) { throw new RuntimeException("Stub!"); }
+}
\ No newline at end of file
diff --git a/ojluni/annotations/sdk/nullability/java/util/Map.annotated.java b/ojluni/annotations/sdk/nullability/java/util/Map.annotated.java
index c19add2..dafddd9 100644
--- a/ojluni/annotations/sdk/nullability/java/util/Map.annotated.java
+++ b/ojluni/annotations/sdk/nullability/java/util/Map.annotated.java
@@ -129,4 +129,6 @@
 @libcore.util.NonNull public static <K, V> java.util.Comparator<java.util.Map.@libcore.util.NonNull Entry<@libcore.util.Nullable K, @libcore.util.NullFromTypeParam V>> comparingByValue(@libcore.util.NonNull java.util.Comparator<? super @libcore.util.NullFromTypeParam V> cmp) { throw new RuntimeException("Stub!"); }
 }
 
+@libcore.util.NonNull public static <K, V> java.util.Map<K, V> copyOf(@libcore.util.NonNull java.util.Map<? extends K, ? extends V> map);
+
 }
diff --git a/ojluni/annotations/sdk/nullability/java/util/Set.annotated.java b/ojluni/annotations/sdk/nullability/java/util/Set.annotated.java
index bae8bdd..6f88740 100644
--- a/ojluni/annotations/sdk/nullability/java/util/Set.annotated.java
+++ b/ojluni/annotations/sdk/nullability/java/util/Set.annotated.java
@@ -87,4 +87,6 @@
 
 @java.lang.SafeVarargs
 @libcore.util.NonNull public static <E> java.util.Set<@libcore.util.NonNull E> of(E @libcore.util.NonNull ... elements) { throw new RuntimeException("Stub!"); }
+
+@libcore.util.NonNull public static <E> java.util.Set<E> copyOf(@libcore.util.NonNull java.util.Collection<? extends E> coll)  { throw new RuntimeException("Stub!"); }
 }
diff --git a/ojluni/src/main/java/java/awt/font/TEST_MAPPING b/ojluni/src/main/java/java/awt/font/TEST_MAPPING
index a23d298..8bc46cc 100644
--- a/ojluni/src/main/java/java/awt/font/TEST_MAPPING
+++ b/ojluni/src/main/java/java/awt/font/TEST_MAPPING
@@ -7,6 +7,14 @@
           "include-filter": "libcore.java.awt.font"
         }
       ]
+    },
+    {
+      "name": "CtsLibcoreOjTestCases",
+      "options": [
+        {
+          "include-filter": "test.java.awt.font"
+        }
+      ]
     }
   ]
 }
\ No newline at end of file
diff --git a/ojluni/src/main/java/java/io/File.java b/ojluni/src/main/java/java/io/File.java
index 98956ca..0037534 100644
--- a/ojluni/src/main/java/java/io/File.java
+++ b/ojluni/src/main/java/java/io/File.java
@@ -518,7 +518,7 @@
 
     /* -- Path operations -- */
 
-    // Android-changed: Android-specific path information
+    // Android-changed: Android-specific path information.
     /**
      * Tests whether this abstract pathname is absolute.  The definition of
      * absolute pathname is system dependent.  On Android, absolute paths start with
@@ -531,7 +531,7 @@
         return fs.isAbsolute(this);
     }
 
-    // Android-changed: Android-specific path information
+    // Android-changed: Android-specific path information.
     /**
      * Returns the absolute path of this file. An absolute path is a path that starts at a root
      * of the file system. On Android, there is only one root: {@code /}.
@@ -737,8 +737,7 @@
 
     /* -- Attribute accessors -- */
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on android
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * Tests whether the application can read the file denoted by this
      * abstract pathname.
@@ -763,8 +762,7 @@
         return fs.checkAccess(this, FileSystem.ACCESS_READ);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on android
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * Tests whether the application can modify the file denoted by this
      * abstract pathname.
@@ -1450,8 +1448,7 @@
         return fs.setLastModifiedTime(this, time);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * Marks the file or directory named by this abstract pathname so that
      * only read operations are allowed. After invoking this method the file
@@ -1480,8 +1477,7 @@
         return fs.setReadOnly(this);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * Sets the owner's or everybody's write permission for this abstract
      * pathname.
@@ -1523,8 +1519,7 @@
         return fs.setPermission(this, FileSystem.ACCESS_WRITE, writable, ownerOnly);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * A convenience method to set the owner's write permission for this abstract
      * pathname.
@@ -1554,8 +1549,7 @@
         return setWritable(writable, true);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * Sets the owner's or everybody's read permission for this abstract
      * pathname.
@@ -1600,8 +1594,7 @@
         return fs.setPermission(this, FileSystem.ACCESS_READ, readable, ownerOnly);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * A convenience method to set the owner's read permission for this abstract
      * pathname.
@@ -1634,8 +1627,7 @@
         return setReadable(readable, true);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * Sets the owner's or everybody's execute permission for this abstract
      * pathname.
@@ -1680,8 +1672,7 @@
         return fs.setPermission(this, FileSystem.ACCESS_EXECUTE, executable, ownerOnly);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * A convenience method to set the owner's execute permission for this
      * abstract pathname.
@@ -1714,8 +1705,7 @@
         return setExecutable(executable, true);
     }
 
-    // Android-changed. Removed javadoc comment about special privileges
-    // that doesn't make sense on Android.
+    // Android-changed: Removed inapplicable javadoc comment about special privileges.
     /**
      * Tests whether the application can execute the file denoted by this
      * abstract pathname.
@@ -1873,8 +1863,7 @@
     private static class TempDirectory {
         private TempDirectory() { }
 
-        // Android-changed: Don't cache java.io.tmpdir value
-        // temporary directory location.
+        // Android-changed: Don't cache java.io.tmpdir value temporary directory location.
         /*
         private static final File tmpdir = new File(AccessController
            .doPrivileged(new GetPropertyAction("java.io.tmpdir")));
@@ -1888,8 +1877,8 @@
         static File generateFile(String prefix, String suffix, File dir)
             throws IOException
         {
-            // Android-changed: Use Math.randomIntInternal. This (pseudo) random number
-            // is initialized post-fork
+            // Android-changed: Use Math.randomIntInternal.
+            // This (pseudo) random number is initialized post-fork.
 
             long n = Math.randomLongInternal();
             if (n == Long.MIN_VALUE) {
@@ -1898,7 +1887,7 @@
                 n = Math.abs(n);
             }
 
-            // Android-changed: Reject invalid file prefixes
+            // Android-changed: Reject invalid file prefixes.
             // Use only the file name from the supplied prefix
             // prefix = (new File(prefix)).getName();
 
diff --git a/ojluni/src/main/java/java/io/FileDescriptor.java b/ojluni/src/main/java/java/io/FileDescriptor.java
index 98db443..90b97b5 100644
--- a/ojluni/src/main/java/java/io/FileDescriptor.java
+++ b/ojluni/src/main/java/java/io/FileDescriptor.java
@@ -47,11 +47,11 @@
  * @since   JDK1.0
  */
 public final class FileDescriptor {
-    // Android-changed: Removed parent reference counting. Creator is responsible for closing
-    // the file descriptor.
+    // Android-changed: Removed parent reference counting.
+    // The creator is responsible for closing the file descriptor.
 
-    // Android-changed: Renamed fd to descriptor to avoid issues with JNI/reflection
-    // fetching the descriptor value.
+    // Android-changed: Renamed fd to descriptor.
+    // Renaming is to avoid issues with JNI/reflection fetching the descriptor value.
     private int descriptor;
 
     // Android-added: Track fd owner to guard against accidental closure. http://b/110100358
@@ -63,6 +63,9 @@
     /** @hide */
     public static final long NO_OWNER = 0L;
 
+    // Android-added: lock for release$.
+    private final Object releaseLock = new Object();
+
     /**
      * Constructs an (invalid) FileDescriptor
      * object.
@@ -142,11 +145,11 @@
      */
     public native void sync() throws SyncFailedException;
 
-    // Android-removed: initIDs not used to allow compile-time intialization
+    // Android-removed: initIDs not used to allow compile-time initialization.
     /* This routine initializes JNI field offsets for the class */
     //private static native void initIDs();
 
-    // Android-added: Needed for framework to access descriptor value
+    // Android-added: Needed for framework to access descriptor value.
     /**
      * Returns the int descriptor. It's highly unlikely you should be calling this. Please discuss
      * your needs with a libcore maintainer before using this method.
@@ -156,7 +159,7 @@
         return descriptor;
     }
 
-    // Android-added: Needed for framework to access descriptor value
+    // Android-added: Needed for framework to access descriptor value.
     /**
      * Sets the int descriptor. It's highly unlikely you should be calling this. Please discuss
      * your needs with a libcore maintainer before using this method.
@@ -211,19 +214,26 @@
 
     /**
      * Returns a copy of this FileDescriptor, and sets this to an invalid state.
+     *
+     * The returned instance is not necessarily {@code valid()}, if the original FileDescriptor
+     * was invalid, or if another thread concurrently calls {@code release$()}.
+     *
      * @hide internal use only
      */
     public FileDescriptor release$() {
       FileDescriptor result = new FileDescriptor();
-      result.descriptor = this.descriptor;
-      result.ownerId = this.ownerId;
-      this.descriptor = -1;
-      this.ownerId = FileDescriptor.NO_OWNER;
+      synchronized (releaseLock) {
+          result.descriptor = this.descriptor;
+          result.ownerId = this.ownerId;
+          this.descriptor = -1;
+          this.ownerId = FileDescriptor.NO_OWNER;
+      }
+
       return result;
     }
     // END Android-added: Methods to enable ownership enforcement of Unix file descriptors.
 
-    // Android-added: Needed for framework to test if it's a socket
+    // Android-added: Needed for framework to test if it's a socket.
     /**
      * @hide internal use only
      */
@@ -254,5 +264,5 @@
             }
         );
     }
-// Android-removed: Removed method required for parents reference counting
+// Android-removed: Removed method required for parents reference counting.
 }
diff --git a/ojluni/src/main/java/java/io/FileInputStream.java b/ojluni/src/main/java/java/io/FileInputStream.java
index 38230bb..9ce75ff 100755
--- a/ojluni/src/main/java/java/io/FileInputStream.java
+++ b/ojluni/src/main/java/java/io/FileInputStream.java
@@ -222,6 +222,9 @@
         fd.attach(this);
         */
         this.isFdOwner = isFdOwner;
+        if (isFdOwner) {
+            IoUtils.setFdOwner(this.fd, this);
+        }
     }
 
     // BEGIN Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
diff --git a/ojluni/src/main/java/java/io/FileOutputStream.java b/ojluni/src/main/java/java/io/FileOutputStream.java
index d0d0d40..66f4c05 100755
--- a/ojluni/src/main/java/java/io/FileOutputStream.java
+++ b/ojluni/src/main/java/java/io/FileOutputStream.java
@@ -299,6 +299,9 @@
         // Android-changed: FileDescriptor ownership tracking mechanism.
         // fd.attach(this);
         this.isFdOwner = isFdOwner;
+        if (isFdOwner) {
+            IoUtils.setFdOwner(this.fd, this);
+        }
     }
 
     // BEGIN Android-changed: Open files using IoBridge to share BlockGuard & StrictMode logic.
diff --git a/ojluni/src/main/java/java/io/ObjectOutputStream.java b/ojluni/src/main/java/java/io/ObjectOutputStream.java
index ccec944..1f7f901 100644
--- a/ojluni/src/main/java/java/io/ObjectOutputStream.java
+++ b/ojluni/src/main/java/java/io/ObjectOutputStream.java
@@ -1223,7 +1223,7 @@
                 writeClass((Class) obj, unshared);
             } else if (obj instanceof ObjectStreamClass) {
                 writeClassDesc((ObjectStreamClass) obj, unshared);
-            // END Android-changed:  Make Class and ObjectStreamClass replaceable.
+            // END Android-changed: Make Class and ObjectStreamClass replaceable.
             } else if (obj instanceof String) {
                 writeString((String) obj, unshared);
             } else if (cl.isArray()) {
@@ -1865,7 +1865,7 @@
             return blkmode;
         }
 
-        // BEGIN Android-added: Warning about writing to closed ObjectOutputStream
+        // BEGIN Android-added: Warning about writing to closed ObjectOutputStream.
         /**
          * Warns if the stream has been closed.
          *
@@ -1884,7 +1884,7 @@
                 warnOnceWhenWriting = false;
             }
         }
-        // END Android-added: Warning about writing to closed ObjectOutputStream
+        // END Android-added: Warning about writing to closed ObjectOutputStream.
 
         /* ----------------- generic output stream methods ----------------- */
         /*
@@ -1916,7 +1916,7 @@
         public void close() throws IOException {
             flush();
             out.close();
-            // Android-added: Warning about writing to closed ObjectOutputStream
+            // Android-added: Warning about writing to closed ObjectOutputStream.
             warnOnceWhenWriting = true;
         }
 
@@ -1932,7 +1932,7 @@
             if (!(copy || blkmode)) {           // write directly
                 drain();
                 out.write(b, off, len);
-                // Android-added: Warning about writing to closed ObjectOutputStream
+                // Android-added: Warning about writing to closed ObjectOutputStream.
                 warnIfClosed();
                 return;
             }
@@ -1955,7 +1955,7 @@
                     len -= wlen;
                 }
             }
-            // Android-added: Warning about writing to closed ObjectOutputStream
+            // Android-added: Warning about writing to closed ObjectOutputStream.
             warnIfClosed();
         }
 
@@ -1972,7 +1972,7 @@
             }
             out.write(buf, 0, pos);
             pos = 0;
-            // Android-added: Warning about writing to closed ObjectOutputStream
+            // Android-added: Warning about writing to closed ObjectOutputStream.
             warnIfClosed();
         }
 
@@ -1991,7 +1991,7 @@
                 Bits.putInt(hbuf, 1, len);
                 out.write(hbuf, 0, 5);
             }
-            // Android-added: Warning about writing to closed ObjectOutputStream
+            // Android-added: Warning about writing to closed ObjectOutputStream.
             warnIfClosed();
         }
 
diff --git a/ojluni/src/main/java/java/io/ObjectStreamClass.java b/ojluni/src/main/java/java/io/ObjectStreamClass.java
index a88ad63..9a1a48f 100644
--- a/ojluni/src/main/java/java/io/ObjectStreamClass.java
+++ b/ojluni/src/main/java/java/io/ObjectStreamClass.java
@@ -1425,12 +1425,12 @@
             {
                 return null;
             }
-            // BEGIN Android-changed: Serialization constructor obtained differently
+            // BEGIN Android-changed: Serialization constructor obtained differently.
             // cons = reflFactory.newConstructorForSerialization(cl, cons);
             if (cons.getDeclaringClass() != cl) {
                 cons = cons.serializationCopy(cons.getDeclaringClass(), cl);
             }
-            // END Android-changed: Serialization constructor obtained differently
+            // END Android-changed: Serialization constructor obtained differently.
             cons.setAccessible(true);
             return cons;
         } catch (NoSuchMethodException ex) {
@@ -1797,7 +1797,7 @@
                 }
             }
 
-            // BEGIN Android-changed: Fix/log clinit serialization workaround b/29064453
+            // BEGIN Android-changed: Fix/log clinit serialization workaround. b/29064453
             // Prior to SDK 24 hasStaticInitializer() would return true if the superclass had a
             // static initializer, that was contrary to the specification. In SDK 24 the default
             // behavior was corrected but the old behavior was preserved for apps that targeted 23
@@ -1817,7 +1817,7 @@
                     // instructions to the developer on how to fix the problems.
                     warnIncompatibleSUIDChange = true;
                 }
-                // END Android-changed: Fix/log clinit serialization workaround b/29064453
+            // END Android-changed: Fix/log clinit serialization workaround. b/29064453
                 dout.writeUTF("<clinit>");
                 dout.writeInt(Modifier.STATIC);
                 dout.writeUTF("()V");
@@ -1882,14 +1882,14 @@
             for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
                 hash = (hash << 8) | (hashBytes[i] & 0xFF);
             }
-            // BEGIN Android-added: Fix/log clinit serialization workaround b/29064453
+            // BEGIN Android-added: Fix/log clinit serialization workaround. b/29064453
             // ObjectStreamClass instances are cached per Class and caches its default
             // serialVersionUID so it will only log one message per class per app process
             // irrespective of the number of times the class is serialized.
             if (warnIncompatibleSUIDChange) {
                 suidCompatibilityListener.warnDefaultSUIDTargetVersionDependent(cl, hash);
             }
-            // END Android-added: Fix/log clinit serialization workaround b/29064453
+            // END Android-added: Fix/log clinit serialization workaround. b/29064453
             return hash;
         } catch (IOException ex) {
             throw new InternalError(ex);
@@ -1898,7 +1898,7 @@
         }
     }
 
-    // BEGIN Android-changed: Fix/log clinit serialization workaround b/29064453
+    // BEGIN Android-changed: Fix/log clinit serialization workaround. b/29064453
     /**
      * Created for testing as there is no nice way to detect when a message is logged.
      *
@@ -1943,7 +1943,7 @@
      */
     private native static boolean hasStaticInitializer(
         Class<?> cl, boolean inheritStaticInitializer);
-    // END Android-changed: Fix/log clinit serialization workaround b/29064453
+    // END Android-changed: Fix/log clinit serialization workaround. b/29064453
 
     /**
      * Class for computing and caching field/constructor/method signatures
diff --git a/ojluni/src/main/java/java/lang/Byte.java b/ojluni/src/main/java/java/lang/Byte.java
index deb4ecb..bc397f9 100644
--- a/ojluni/src/main/java/java/lang/Byte.java
+++ b/ojluni/src/main/java/java/lang/Byte.java
@@ -25,6 +25,8 @@
 
 package java.lang;
 
+// Android-removed: Unsupported @HotSpotIntrinsicCandidate annotation.
+// import jdk.internal.HotSpotIntrinsicCandidate;
 import libcore.util.HexEncoding;
 
 /**
@@ -41,7 +43,7 @@
  * @author  Nakul Saraiya
  * @author  Joseph D. Darcy
  * @see     java.lang.Number
- * @since   JDK1.1
+ * @since   1.1
  */
 public final class Byte extends Number implements Comparable<Byte> {
 
@@ -100,6 +102,8 @@
      * @return a {@code Byte} instance representing {@code b}.
      * @since  1.5
      */
+    // Android-removed: Unsupported @HotSpotIntrinsicCandidate annotation.
+    // @HotSpotIntrinsicCandidate
     public static Byte valueOf(byte b) {
         final int offset = 128;
         return ByteCache.cache[(int)b + offset];
@@ -296,7 +300,13 @@
      *
      * @param value     the value to be represented by the
      *                  {@code Byte}.
+     *
+     * @deprecated
+     * It is rarely appropriate to use this constructor. The static factory
+     * {@link #valueOf(byte)} is generally a better choice, as it is
+     * likely to yield significantly better space and time performance.
      */
+    @Deprecated(since="9")
     public Byte(byte value) {
         this.value = value;
     }
@@ -310,10 +320,16 @@
      *
      * @param s         the {@code String} to be converted to a
      *                  {@code Byte}
-     * @throws           NumberFormatException If the {@code String}
+     * @throws          NumberFormatException if the {@code String}
      *                  does not contain a parsable {@code byte}.
-     * @see        java.lang.Byte#parseByte(java.lang.String, int)
+     *
+     * @deprecated
+     * It is rarely appropriate to use this constructor.
+     * Use {@link #parseByte(String)} to convert a string to a
+     * {@code byte} primitive, or use {@link #valueOf(String)}
+     * to convert a string to a {@code Byte} object.
      */
+    @Deprecated(since="9")
     public Byte(String s) throws NumberFormatException {
         this.value = parseByte(s, 10);
     }
@@ -322,6 +338,8 @@
      * Returns the value of this {@code Byte} as a
      * {@code byte}.
      */
+    // Android-removed: Unsupported @HotSpotIntrinsicCandidate annotation.
+    // @HotSpotIntrinsicCandidate
     public byte byteValue() {
         return value;
     }
@@ -461,6 +479,22 @@
     }
 
     /**
+     * Compares two {@code byte} values numerically treating the values
+     * as unsigned.
+     *
+     * @param  x the first {@code byte} to compare
+     * @param  y the second {@code byte} to compare
+     * @return the value {@code 0} if {@code x == y}; a value less
+     *         than {@code 0} if {@code x < y} as unsigned values; and
+     *         a value greater than {@code 0} if {@code x > y} as
+     *         unsigned values
+     * @since 9
+     */
+    public static int compareUnsigned(byte x, byte y) {
+        return Byte.toUnsignedInt(x) - Byte.toUnsignedInt(y);
+    }
+
+    /**
      * Converts the argument to an {@code int} by an unsigned
      * conversion.  In an unsigned conversion to an {@code int}, the
      * high-order 24 bits of the {@code int} are zero and the
diff --git a/ojluni/src/main/java/java/lang/Class.java b/ojluni/src/main/java/java/lang/Class.java
index f792248..7925408 100644
--- a/ojluni/src/main/java/java/lang/Class.java
+++ b/ojluni/src/main/java/java/lang/Class.java
@@ -896,22 +896,53 @@
     public Package getPackage() {
         ClassLoader loader = getClassLoader();
         if (loader != null) {
-            String packageName = getPackageName$();
+            String packageName = getPackageName();
             return packageName != null ? loader.getPackage(packageName) : null;
         }
         return null;
     }
 
     /**
-     * Returns the package name of this class. This returns null for classes in
-     * the default package.
+     * Returns the fully qualified package name.
      *
-     * @hide
+     * <p> If this class is a top level class, then this method returns the fully
+     * qualified name of the package that the class is a member of, or the
+     * empty string if the class is in an unnamed package.
+     *
+     * <p> If this class is a member class, then this method is equivalent to
+     * invoking {@code getPackageName()} on the {@linkplain #getEnclosingClass
+     * enclosing class}.
+     *
+     * <p> If this class is a {@linkplain #isLocalClass local class} or an {@linkplain
+     * #isAnonymousClass() anonymous class}, then this method is equivalent to
+     * invoking {@code getPackageName()} on the {@linkplain #getDeclaringClass
+     * declaring class} of the {@linkplain #getEnclosingMethod enclosing method} or
+     * {@linkplain #getEnclosingConstructor enclosing constructor}.
+     *
+     * <p> If this class represents an array type then this method returns the
+     * package name of the element type. If this class represents a primitive
+     * type or void then the package name "{@code java.lang}" is returned.
+     *
+     * @return the fully qualified package name
+     *
+     * @since 9
+     * @spec JPMS
+     * @jls 6.7  Fully Qualified Names
      */
-    public String getPackageName$() {
-        String name = getName();
-        int last = name.lastIndexOf('.');
-        return last == -1 ? null : name.substring(0, last);
+    public String getPackageName() {
+            // BEGIN Android-changed: Don't use a private field as a cache.
+            Class<?> c = this;
+            while (c.isArray()) {
+                c = c.getComponentType();
+            }
+            if (c.isPrimitive()) {
+                return "java.lang";
+            } else {
+                String cn = c.getName();
+                int dot = cn.lastIndexOf('.');
+                return (dot != -1) ? cn.substring(0, dot).intern() : "";
+            }
+            // END Android-changed: Don't use a private field as a cache.
     }
 
 
@@ -1135,7 +1166,7 @@
      *     that class is a local or anonymous class; otherwise {@code null}.
      * @since 1.5
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     public Method getEnclosingMethod() {
         if (classNameImpliesTopLevel()) {
             return null;
@@ -1157,7 +1188,7 @@
      *     that class is a local or anonymous class; otherwise {@code null}.
      * @since 1.5
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     public Constructor<?> getEnclosingConstructor() {
         if (classNameImpliesTopLevel()) {
             return null;
@@ -1184,7 +1215,7 @@
      * @return the declaring class for this class
      * @since JDK1.1
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     @FastNative
     public native Class<?> getDeclaringClass();
 
@@ -1195,7 +1226,7 @@
      * @return the immediately enclosing class of the underlying class
      * @since 1.5
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     @FastNative
     public native Class<?> getEnclosingClass();
 
@@ -1593,7 +1624,7 @@
      * @jls 8.2 Class Members
      * @jls 8.3 Field Declarations
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     public Field getField(String name)
         throws NoSuchFieldException {
         if (name == null) {
@@ -1764,7 +1795,7 @@
      *
      * @since JDK1.1
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     @FastNative
     public native Class<?>[] getDeclaredClasses();
 
@@ -1810,7 +1841,7 @@
      * @jls 8.2 Class Members
      * @jls 8.3 Field Declarations
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     @FastNative
     public native Field[] getDeclaredFields();
 
@@ -1986,7 +2017,7 @@
      * @jls 8.2 Class Members
      * @jls 8.3 Field Declarations
      */
-    // Android-changed: Removed SecurityException
+    // Android-changed: Removed SecurityException.
     @FastNative
     public native Field getDeclaredField(String name) throws NoSuchFieldException;
 
diff --git a/ojluni/src/main/java/java/lang/Comparable.java b/ojluni/src/main/java/java/lang/Comparable.java
index a88cf16..f0e4b38 100644
--- a/ojluni/src/main/java/java/lang/Comparable.java
+++ b/ojluni/src/main/java/java/lang/Comparable.java
@@ -84,7 +84,7 @@
  *     {(x, y) such that x.equals(y)}. </pre><p>
  *
  * This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <T> the type of objects that this object may be compared to
diff --git a/ojluni/src/main/java/java/lang/Deprecated.java b/ojluni/src/main/java/java/lang/Deprecated.java
index 58a0691..3735f59 100644
--- a/ojluni/src/main/java/java/lang/Deprecated.java
+++ b/ojluni/src/main/java/java/lang/Deprecated.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,17 +29,75 @@
 import static java.lang.annotation.ElementType.*;
 
 /**
- * A program element annotated &#64;Deprecated is one that programmers
- * are discouraged from using, typically because it is dangerous,
- * or because a better alternative exists.  Compilers warn when a
- * deprecated program element is used or overridden in non-deprecated code.
+ * A program element annotated {@code @Deprecated} is one that programmers
+ * are discouraged from using. An element may be deprecated for any of several
+ * reasons, for example, its usage is likely to lead to errors; it may
+ * be changed incompatibly or removed in a future version; it has been
+ * superseded by a newer, usually preferable alternative; or it is obsolete.
+ *
+ * <p>Compilers issue warnings when a deprecated program element is used or
+ * overridden in non-deprecated code. Use of the {@code @Deprecated}
+ * annotation on a local variable declaration or on a parameter declaration
+ * or a package declaration has no effect on the warnings issued by a compiler.
+ *
+ * <p>When a module is deprecated, the use of that module in {@code
+ * requires}, but not in {@code exports} or {@code opens} clauses causes
+ * a warning to be issued. A module being deprecated does <em>not</em> cause
+ * warnings to be issued for uses of types within the module.
+ *
+ * <p>This annotation type has a string-valued element {@code since}. The value
+ * of this element indicates the version in which the annotated program element
+ * was first deprecated.
+ *
+ * <p>This annotation type has a boolean-valued element {@code forRemoval}.
+ * A value of {@code true} indicates intent to remove the annotated program
+ * element in a future version. A value of {@code false} indicates that use of
+ * the annotated program element is discouraged, but at the time the program
+ * element was annotated, there was no specific intent to remove it.
+ *
+ * @apiNote
+ * It is strongly recommended that the reason for deprecating a program element
+ * be explained in the documentation, using the {@code &#64;deprecated}
+ * javadoc tag. The documentation should also suggest and link to a
+ * recommended replacement API, if applicable. A replacement API often
+ * has subtly different semantics, so such issues should be discussed as
+ * well.
+ *
+ * <p>It is recommended that a {@code since} value be provided with all newly
+ * annotated program elements. Note that {@code since} cannot be mandatory,
+ * as there are many existing annotations that lack this element value.
+ *
+ * <p>There is no defined order among annotation elements. As a matter of
+ * style, the {@code since} element should be placed first.
+ *
+ * <p>The {@code @Deprecated} annotation should always be present if
+ * the {@code &#64;deprecated} javadoc tag is present, and vice-versa.
  *
  * @author  Neal Gafter
  * @since 1.5
- * @jls 9.6.3.6 @Deprecated
+ * @jls 9.6.4.6 @Deprecated
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
-@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
+@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
 public @interface Deprecated {
+    /**
+     * Returns the version in which the annotated element became deprecated.
+     * The version string is in the same format and namespace as the value of
+     * the {@code @since} javadoc tag. The default value is the empty
+     * string.
+     *
+     * @return the version string
+     * @since 9
+     */
+    String since() default "";
+
+    /**
+     * Indicates whether the annotated element is subject to removal in a
+     * future version. The default value is {@code false}.
+     *
+     * @return whether the element is subject to removal
+     * @since 9
+     */
+    boolean forRemoval() default false;
 }
diff --git a/ojluni/src/main/java/java/lang/Enum.java b/ojluni/src/main/java/java/lang/Enum.java
index 988c133..6b37aea 100644
--- a/ojluni/src/main/java/java/lang/Enum.java
+++ b/ojluni/src/main/java/java/lang/Enum.java
@@ -239,7 +239,7 @@
     public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                 String name) {
         Objects.requireNonNull(enumType, "enumType == null");
-        Objects.requireNonNull(enumType, "name == null");
+        Objects.requireNonNull(name, "name == null");
         T[] values = getSharedConstants(enumType);
         if (values == null) {
             throw new IllegalArgumentException(enumType.toString() + " is not an enum type.");
diff --git a/ojluni/src/main/java/java/lang/Iterable.java b/ojluni/src/main/java/java/lang/Iterable.java
index eed3938..0928c81 100644
--- a/ojluni/src/main/java/java/lang/Iterable.java
+++ b/ojluni/src/main/java/java/lang/Iterable.java
@@ -34,7 +34,7 @@
  * Implementing this interface allows an object to be the target of
  * the "for-each loop" statement. See
  * <strong>
- * <a href="{@docRoot}/../technotes/guides/language/foreach.html">For-each Loop</a>
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/language/foreach.html">For-each Loop</a>
  * </strong>
  *
  * @param <T> the type of elements returned by the iterator
diff --git a/ojluni/src/main/java/java/lang/Math.java b/ojluni/src/main/java/java/lang/Math.java
index 17cbf6d..a04b951 100644
--- a/ojluni/src/main/java/java/lang/Math.java
+++ b/ojluni/src/main/java/java/lang/Math.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
  */
 
 package java.lang;
+
 import dalvik.annotation.optimization.CriticalNative;
 import java.util.Random;
 
@@ -99,13 +100,13 @@
  * {@code subtractExact}, {@code multiplyExact}, and {@code toIntExact}
  * throw an {@code ArithmeticException} when the results overflow.
  * For other arithmetic operations such as divide, absolute value,
- * increment, decrement, and negation overflow occurs only with
+ * increment by one, decrement by one, and negation, overflow occurs only with
  * a specific minimum or maximum value and should be checked against
  * the minimum or maximum as appropriate.
  *
  * @author  unascribed
  * @author  Joseph D. Darcy
- * @since   JDK1.0
+ * @since   1.0
  */
 
 public final class Math {
@@ -132,6 +133,18 @@
     public static final double PI = 3.14159265358979323846;
 
     /**
+     * Constant by which to multiply an angular value in degrees to obtain an
+     * angular value in radians.
+     */
+    private static final double DEGREES_TO_RADIANS = 0.017453292519943295;
+
+    /**
+     * Constant by which to multiply an angular value in radians to obtain an
+     * angular value in degrees.
+     */
+    private static final double RADIANS_TO_DEGREES = 57.29577951308232;
+
+    /**
      * Returns the trigonometric sine of an angle.  Special cases:
      * <ul><li>If the argument is NaN or an infinity, then the
      * result is NaN.
@@ -236,7 +249,7 @@
      * @since   1.2
      */
     public static double toRadians(double angdeg) {
-        return angdeg / 180.0 * PI;
+        return angdeg * DEGREES_TO_RADIANS;
     }
 
     /**
@@ -252,7 +265,7 @@
      * @since   1.2
      */
     public static double toDegrees(double angrad) {
-        return angrad * 180.0 / PI;
+        return angrad * RADIANS_TO_DEGREES;
     }
 
     /**
@@ -893,6 +906,20 @@
     }
 
     /**
+     * Returns the product of the arguments, throwing an exception if the result
+     * overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     * @since 9
+     */
+    public static long multiplyExact(long x, int y) {
+        return multiplyExact(x, (long)y);
+    }
+
+    /**
      * Returns the product of the arguments,
      * throwing an exception if the result overflows a {@code long}.
      *
@@ -1037,17 +1064,66 @@
     }
 
     /**
+     * Returns the exact mathematical product of the arguments.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @since 9
+     */
+    public static long multiplyFull(int x, int y) {
+        return (long)x * (long)y;
+    }
+
+    /**
+     * Returns as a {@code long} the most significant 64 bits of the 128-bit
+     * product of two 64-bit factors.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @since 9
+     */
+    public static long multiplyHigh(long x, long y) {
+        if (x < 0 || y < 0) {
+            // Use technique from section 8-2 of Henry S. Warren, Jr.,
+            // Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 173-174.
+            long x1 = x >> 32;
+            long x2 = x & 0xFFFFFFFFL;
+            long y1 = y >> 32;
+            long y2 = y & 0xFFFFFFFFL;
+            long z2 = x2 * y2;
+            long t = x1 * y2 + (z2 >>> 32);
+            long z1 = t & 0xFFFFFFFFL;
+            long z0 = t >> 32;
+            z1 += x2 * y1;
+            return x1 * y1 + z0 + (z1 >> 32);
+        } else {
+            // Use Karatsuba technique with two base 2^32 digits.
+            long x1 = x >>> 32;
+            long y1 = y >>> 32;
+            long x2 = x & 0xFFFFFFFFL;
+            long y2 = y & 0xFFFFFFFFL;
+            long A = x1 * y1;
+            long B = x2 * y2;
+            long C = (x1 + x2) * (y1 + y2);
+            long K = C - A - B;
+            return (((B >>> 32) + K) >>> 32) + A;
+        }
+    }
+
+    /**
      * Returns the largest (closest to positive infinity)
      * {@code int} value that is less than or equal to the algebraic quotient.
      * There is one special case, if the dividend is the
      * {@linkplain Integer#MIN_VALUE Integer.MIN_VALUE} and the divisor is {@code -1},
      * then integer overflow occurs and
-     * the result is equal to the {@code Integer.MIN_VALUE}.
+     * the result is equal to {@code Integer.MIN_VALUE}.
      * <p>
      * Normal integer division operates under the round to zero rounding mode
      * (truncation).  This operation instead acts under the round toward
      * negative infinity (floor) rounding mode.
-     * The floor rounding mode gives different results than truncation
+     * The floor rounding mode gives different results from truncation
      * when the exact result is negative.
      * <ul>
      *   <li>If the signs of the arguments are the same, the results of
@@ -1086,12 +1162,41 @@
      * There is one special case, if the dividend is the
      * {@linkplain Long#MIN_VALUE Long.MIN_VALUE} and the divisor is {@code -1},
      * then integer overflow occurs and
-     * the result is equal to the {@code Long.MIN_VALUE}.
+     * the result is equal to {@code Long.MIN_VALUE}.
      * <p>
      * Normal integer division operates under the round to zero rounding mode
      * (truncation).  This operation instead acts under the round toward
      * negative infinity (floor) rounding mode.
-     * The floor rounding mode gives different results than truncation
+     * The floor rounding mode gives different results from truncation
+     * when the exact result is negative.
+     * <p>
+     * For examples, see {@link #floorDiv(int, int)}.
+     *
+     * @param x the dividend
+     * @param y the divisor
+     * @return the largest (closest to positive infinity)
+     * {@code int} value that is less than or equal to the algebraic quotient.
+     * @throws ArithmeticException if the divisor {@code y} is zero
+     * @see #floorMod(long, int)
+     * @see #floor(double)
+     * @since 9
+     */
+    public static long floorDiv(long x, int y) {
+        return floorDiv(x, (long)y);
+    }
+
+    /**
+     * Returns the largest (closest to positive infinity)
+     * {@code long} value that is less than or equal to the algebraic quotient.
+     * There is one special case, if the dividend is the
+     * {@linkplain Long#MIN_VALUE Long.MIN_VALUE} and the divisor is {@code -1},
+     * then integer overflow occurs and
+     * the result is equal to {@code Long.MIN_VALUE}.
+     * <p>
+     * Normal integer division operates under the round to zero rounding mode
+     * (truncation).  This operation instead acts under the round toward
+     * negative infinity (floor) rounding mode.
+     * The floor rounding mode gives different results from truncation
      * when the exact result is negative.
      * <p>
      * For examples, see {@link #floorDiv(int, int)}.
@@ -1159,8 +1264,34 @@
      * @since 1.8
      */
     public static int floorMod(int x, int y) {
-        int r = x - floorDiv(x, y) * y;
-        return r;
+        return x - floorDiv(x, y) * y;
+    }
+
+    /**
+     * Returns the floor modulus of the {@code long} and {@code int} arguments.
+     * <p>
+     * The floor modulus is {@code x - (floorDiv(x, y) * y)},
+     * has the same sign as the divisor {@code y}, and
+     * is in the range of {@code -abs(y) < r < +abs(y)}.
+     *
+     * <p>
+     * The relationship between {@code floorDiv} and {@code floorMod} is such that:
+     * <ul>
+     *   <li>{@code floorDiv(x, y) * y + floorMod(x, y) == x}
+     * </ul>
+     * <p>
+     * For examples, see {@link #floorMod(int, int)}.
+     *
+     * @param x the dividend
+     * @param y the divisor
+     * @return the floor modulus {@code x - (floorDiv(x, y) * y)}
+     * @throws ArithmeticException if the divisor {@code y} is zero
+     * @see #floorDiv(long, int)
+     * @since 9
+     */
+    public static int floorMod(long x, int y) {
+        // Result cannot overflow the range of int.
+        return (int)(x - floorDiv(x, y) * y);
     }
 
     /**
diff --git a/ojluni/src/main/java/java/lang/Runtime.java b/ojluni/src/main/java/java/lang/Runtime.java
index f5c52e7..b9baf65 100644
--- a/ojluni/src/main/java/java/lang/Runtime.java
+++ b/ojluni/src/main/java/java/lang/Runtime.java
@@ -78,11 +78,6 @@
      */
     private boolean shuttingDown;
 
-    /**
-     * Reflects whether we are tracing method calls.
-     */
-    private boolean tracingMethods;
-
     private static native void nativeExit(int code);
 
     /**
@@ -845,13 +840,8 @@
      *               <code>false</code> to disable this feature.
      */
     public void traceMethodCalls(boolean on) {
-        if (on != tracingMethods) {
-            if (on) {
-                VMDebug.startMethodTracing();
-            } else {
-                VMDebug.stopMethodTracing();
-            }
-            tracingMethods = on;
+        if (on) {
+            throw new UnsupportedOperationException();
         }
     }
 
diff --git a/ojluni/src/main/java/java/lang/Short.java b/ojluni/src/main/java/java/lang/Short.java
index 9fb3913..dd6e2e2 100644
--- a/ojluni/src/main/java/java/lang/Short.java
+++ b/ojluni/src/main/java/java/lang/Short.java
@@ -25,6 +25,9 @@
 
 package java.lang;
 
+// Android-removed: unsupported @HotSpotIntrinsicCandidate annotation.
+// import jdk.internal.HotSpotIntrinsicCandidate;
+
 /**
  * The {@code Short} class wraps a value of primitive type {@code
  * short} in an object.  An object of type {@code Short} contains a
@@ -38,7 +41,7 @@
  * @author  Nakul Saraiya
  * @author  Joseph D. Darcy
  * @see     java.lang.Number
- * @since   JDK1.1
+ * @since   1.1
  */
 public final class Short extends Number implements Comparable<Short> {
 
@@ -227,6 +230,8 @@
      * @return a {@code Short} instance representing {@code s}.
      * @since  1.5
      */
+    // Android-removed: unsupported @HotSpotIntrinsicCandidate annotation.
+    // @HotSpotIntrinsicCandidate
     public static Short valueOf(short s) {
         final int offset = 128;
         int sAsInt = s;
@@ -299,7 +304,13 @@
      *
      * @param value     the value to be represented by the
      *                  {@code Short}.
+     *
+     * @deprecated
+     * It is rarely appropriate to use this constructor. The static factory
+     * {@link #valueOf(short)} is generally a better choice, as it is
+     * likely to yield significantly better space and time performance.
      */
+    @Deprecated(since="9")
     public Short(short value) {
         this.value = value;
     }
@@ -315,8 +326,14 @@
      *          {@code Short}
      * @throws  NumberFormatException If the {@code String}
      *          does not contain a parsable {@code short}.
-     * @see     java.lang.Short#parseShort(java.lang.String, int)
+     *
+     * @deprecated
+     * It is rarely appropriate to use this constructor.
+     * Use {@link #parseShort(String)} to convert a string to a
+     * {@code short} primitive, or use {@link #valueOf(String)}
+     * to convert a string to a {@code Short} object.
      */
+    @Deprecated(since="9")
     public Short(String s) throws NumberFormatException {
         this.value = parseShort(s, 10);
     }
@@ -334,6 +351,8 @@
      * Returns the value of this {@code Short} as a
      * {@code short}.
      */
+    // Android-removed: unsupported @HotSpotIntrinsicCandidate annotation.
+    // @HotSpotIntrinsicCandidate
     public short shortValue() {
         return value;
     }
@@ -464,6 +483,22 @@
     }
 
     /**
+     * Compares two {@code short} values numerically treating the values
+     * as unsigned.
+     *
+     * @param  x the first {@code short} to compare
+     * @param  y the second {@code short} to compare
+     * @return the value {@code 0} if {@code x == y}; a value less
+     *         than {@code 0} if {@code x < y} as unsigned values; and
+     *         a value greater than {@code 0} if {@code x > y} as
+     *         unsigned values
+     * @since 9
+     */
+    public static int compareUnsigned(short x, short y) {
+        return Short.toUnsignedInt(x) - Short.toUnsignedInt(y);
+    }
+
+    /**
      * The number of bits used to represent a {@code short} value in two's
      * complement binary form.
      * @since 1.5
@@ -487,6 +522,8 @@
      *     the bytes in the specified {@code short} value.
      * @since 1.5
      */
+    // Android-removed: unsupported @HotSpotIntrinsicCandidate annotation.
+    // @HotSpotIntrinsicCandidate
     public static short reverseBytes(short i) {
         return (short) (((i & 0xFF00) >> 8) | (i << 8));
     }
diff --git a/ojluni/src/main/java/java/lang/StrictMath.java b/ojluni/src/main/java/java/lang/StrictMath.java
index ae4af2b..c0a41b9 100644
--- a/ojluni/src/main/java/java/lang/StrictMath.java
+++ b/ojluni/src/main/java/java/lang/StrictMath.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
  */
 
 package java.lang;
+
 import java.util.Random;
 import sun.misc.DoubleConsts;
 
@@ -68,7 +69,7 @@
  * {@code subtractExact}, {@code multiplyExact}, and {@code toIntExact}
  * throw an {@code ArithmeticException} when the results overflow.
  * For other arithmetic operations such as divide, absolute value,
- * increment, decrement, and negation overflow occurs only with
+ * increment by one, decrement by one, and negation overflow occurs only with
  * a specific minimum or maximum value and should be checked against
  * the minimum or maximum as appropriate.
  *
@@ -98,6 +99,19 @@
     public static final double PI = 3.14159265358979323846;
 
     /**
+     * Constant by which to multiply an angular value in degrees to obtain an
+     * angular value in radians.
+     */
+    private static final double DEGREES_TO_RADIANS = 0.017453292519943295;
+
+    /**
+     * Constant by which to multiply an angular value in radians to obtain an
+     * angular value in degrees.
+     */
+
+    private static final double RADIANS_TO_DEGREES = 57.29577951308232;
+
+    /**
      * Returns the trigonometric sine of an angle. Special cases:
      * <ul><li>If the argument is NaN or an infinity, then the
      * result is NaN.
@@ -179,7 +193,7 @@
     public static strictfp double toRadians(double angdeg) {
         // Do not delegate to Math.toRadians(angdeg) because
         // this method has the strictfp modifier.
-        return angdeg / 180.0 * PI;
+        return angdeg * DEGREES_TO_RADIANS;
     }
 
     /**
@@ -196,7 +210,7 @@
     public static strictfp double toDegrees(double angrad) {
         // Do not delegate to Math.toDegrees(angrad) because
         // this method has the strictfp modifier.
-        return angrad * 180.0 / PI;
+        return angrad * RADIANS_TO_DEGREES;
     }
 
     /**
@@ -785,6 +799,21 @@
     }
 
     /**
+     * Returns the product of the arguments, throwing an exception if the result
+     * overflows a {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
+     * @see Math#multiplyExact(long,int)
+     * @since 9
+     */
+    public static long multiplyExact(long x, int y) {
+        return Math.multiplyExact(x, y);
+    }
+
+    /**
      * Returns the product of the arguments,
      * throwing an exception if the result overflows a {@code long}.
      *
@@ -814,6 +843,33 @@
     }
 
     /**
+     * Returns the exact mathematical product of the arguments.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @see Math#multiplyFull(int,int)
+     * @since 9
+     */
+    public static long multiplyFull(int x, int y) {
+        return Math.multiplyFull(x, y);
+    }
+
+    /**
+     * Returns as a {@code long} the most significant 64 bits of the 128-bit
+     * product of two 64-bit factors.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @see Math#multiplyHigh(long,long)
+     * @since 9
+     */
+    public static long multiplyHigh(long x, long y) {
+        return Math.multiplyHigh(x, y);
+    }
+
+    /**
      * Returns the largest (closest to positive infinity)
      * {@code int} value that is less than or equal to the algebraic quotient.
      * There is one special case, if the dividend is the
@@ -843,6 +899,30 @@
      * There is one special case, if the dividend is the
      * {@linkplain Long#MIN_VALUE Long.MIN_VALUE} and the divisor is {@code -1},
      * then integer overflow occurs and
+     * the result is equal to {@code Long.MIN_VALUE}.
+     * <p>
+     * See {@link Math#floorDiv(int, int) Math.floorDiv} for examples and
+     * a comparison to the integer division {@code /} operator.
+     *
+     * @param x the dividend
+     * @param y the divisor
+     * @return the largest (closest to positive infinity)
+     * {@code int} value that is less than or equal to the algebraic quotient.
+     * @throws ArithmeticException if the divisor {@code y} is zero
+     * @see Math#floorDiv(long, int)
+     * @see Math#floor(double)
+     * @since 9
+     */
+    public static long floorDiv(long x, int y) {
+        return Math.floorDiv(x, y);
+    }
+
+    /**
+     * Returns the largest (closest to positive infinity)
+     * {@code long} value that is less than or equal to the algebraic quotient.
+     * There is one special case, if the dividend is the
+     * {@linkplain Long#MIN_VALUE Long.MIN_VALUE} and the divisor is {@code -1},
+     * then integer overflow occurs and
      * the result is equal to the {@code Long.MIN_VALUE}.
      * <p>
      * See {@link Math#floorDiv(int, int) Math.floorDiv} for examples and
@@ -887,6 +967,35 @@
     public static int floorMod(int x, int y) {
         return Math.floorMod(x , y);
     }
+
+    /**
+     * Returns the floor modulus of the {@code long} and {@code int} arguments.
+     * <p>
+     * The floor modulus is {@code x - (floorDiv(x, y) * y)},
+     * has the same sign as the divisor {@code y}, and
+     * is in the range of {@code -abs(y) < r < +abs(y)}.
+     *
+     * <p>
+     * The relationship between {@code floorDiv} and {@code floorMod} is such that:
+     * <ul>
+     *   <li>{@code floorDiv(x, y) * y + floorMod(x, y) == x}
+     * </ul>
+     * <p>
+     * See {@link Math#floorMod(int, int) Math.floorMod} for examples and
+     * a comparison to the {@code %} operator.
+     *
+     * @param x the dividend
+     * @param y the divisor
+     * @return the floor modulus {@code x - (floorDiv(x, y) * y)}
+     * @throws ArithmeticException if the divisor {@code y} is zero
+     * @see Math#floorMod(long, int)
+     * @see StrictMath#floorDiv(long, int)
+     * @since 9
+     */
+    public static int floorMod(long x, int y) {
+        return Math.floorMod(x , y);
+    }
+
     /**
      * Returns the floor modulus of the {@code long} arguments.
      * <p>
diff --git a/ojluni/src/main/java/java/lang/String.java b/ojluni/src/main/java/java/lang/String.java
index 2ba7b1f..58207eb 100644
--- a/ojluni/src/main/java/java/lang/String.java
+++ b/ojluni/src/main/java/java/lang/String.java
@@ -144,7 +144,7 @@
      * Class String is special cased within the Serialization Stream Protocol.
      *
      * A String instance is written into an ObjectOutputStream according to
-     * <a href="{@docRoot}/../platform/serialization/spec/output.html">
+     * <a href="https://docs.oracle.com/javase/8/docs/platform/serialization/spec/output.html">
      * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
      */
     private static final ObjectStreamField[] serialPersistentFields =
diff --git a/ojluni/src/main/java/java/lang/System.java b/ojluni/src/main/java/java/lang/System.java
index 352d9ec..4abba4d 100644
--- a/ojluni/src/main/java/java/lang/System.java
+++ b/ojluni/src/main/java/java/lang/System.java
@@ -25,11 +25,9 @@
  */
 package java.lang;
 
-import com.android.icu.util.Icu4cMetadata;
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 import android.system.ErrnoException;
-import android.system.StructPasswd;
 import android.system.StructUtsname;
 import dalvik.system.VMRuntime;
 import java.io.*;
@@ -38,8 +36,8 @@
 import java.util.Locale;
 import java.util.Properties;
 import java.util.PropertyPermission;
+import libcore.icu.ICU;
 import libcore.io.Libcore;
-import libcore.timezone.TimeZoneDataFiles;
 
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
@@ -982,12 +980,13 @@
 
         p.put("java.vm.version", runtime.vmVersion());
 
+        String userName;
         try {
-            StructPasswd passwd = Libcore.os.getpwuid(Libcore.os.getuid());
-            p.put("user.name", passwd.pw_name);
+            userName = Libcore.os.getpwuid(Libcore.os.getuid()).pw_name;
         } catch (ErrnoException exception) {
-            throw new AssertionError(exception);
+            userName = "unknown";
         }
+        p.put("user.name", userName);
 
         StructUtsname info = Libcore.os.uname();
         p.put("os.arch", info.machine);
@@ -997,16 +996,9 @@
         p.put("os.version", info.release);
 
         // Android-added: Undocumented properties that exist only on Android.
-        p.put("android.icu.library.version", Icu4cMetadata.getIcuVersion());
-        p.put("android.icu.unicode.version", Icu4cMetadata.getUnicodeVersion());
-        p.put("android.icu.cldr.version", Icu4cMetadata.getCldrVersion());
-
-        // Property override for ICU4J : this is the location of the ICU4C data. This
-        // is prioritized over the properties in ICUConfig.properties. The issue with using
-        // that is that it doesn't play well with jarjar and it needs complicated build rules
-        // to change its default value.
-        String icuDataPath = TimeZoneDataFiles.generateIcuDataPath();
-        p.put("android.icu.impl.ICUBinary.dataPath", icuDataPath);
+        p.put("android.icu.library.version", ICU.getIcuVersion());
+        p.put("android.icu.unicode.version", ICU.getUnicodeVersion());
+        p.put("android.icu.cldr.version", ICU.getCldrVersion());
 
         parsePropertyAssignments(p, specialProperties());
 
diff --git a/ojluni/src/main/java/java/lang/Thread.java b/ojluni/src/main/java/java/lang/Thread.java
index 699a1ac..ca66ef3 100644
--- a/ojluni/src/main/java/java/lang/Thread.java
+++ b/ojluni/src/main/java/java/lang/Thread.java
@@ -40,8 +40,6 @@
 import java.util.concurrent.locks.LockSupport;
 import sun.nio.ch.Interruptible;
 import sun.reflect.CallerSensitive;
-import dalvik.system.RuntimeHooks;
-import dalvik.system.ThreadPrioritySetter;
 import dalvik.system.VMStack;
 import libcore.util.EmptyArray;
 
@@ -430,28 +428,27 @@
         }
 
         final int nanosPerMilli = 1000000;
-        long start = System.nanoTime();
-        long duration = (millis * nanosPerMilli) + nanos;
+        final long durationNanos;
+        if (millis >= Long.MAX_VALUE / nanosPerMilli - 1L) {
+          // > 292 years. Avoid overflow by capping it at roughly 292 years.
+          durationNanos = Long.MAX_VALUE;
+        } else {
+          durationNanos = (millis * nanosPerMilli) + nanos;
+        }
+        long startNanos = System.nanoTime();
 
         Object lock = currentThread().lock;
 
-        // The native sleep(...) method actually performs a special type of wait, which may return
-        // early, so loop until sleep duration passes.
+        // The native sleep(...) method actually does a monitor wait, which may return
+        // early, so loop until sleep duration passes. The monitor is only notified when
+        // we exit, which can't happen while we're sleeping.
         synchronized (lock) {
-            while (true) {
+            for (long elapsed = 0L; elapsed < durationNanos;
+                    elapsed = System.nanoTime() - startNanos) {
+                final long remaining = durationNanos - elapsed;
+                millis = remaining / nanosPerMilli;
+                nanos = (int) (remaining % nanosPerMilli);
                 sleep(lock, millis, nanos);
-
-                long now = System.nanoTime();
-                long elapsed = now - start;
-
-                if (elapsed >= duration) {
-                    break;
-                }
-
-                duration -= elapsed;
-                start = now;
-                millis = duration / nanosPerMilli;
-                nanos = (int) (duration % nanosPerMilli);
             }
         }
         // END Android-changed: Implement sleep() methods using a shared native implementation.
@@ -486,10 +483,10 @@
         this.name = name;
 
         Thread parent = currentThread();
-        // Android-removed: SecurityManager stubbed out on Android
+        // Android-removed: SecurityManager stubbed out on Android.
         // SecurityManager security = System.getSecurityManager();
         if (g == null) {
-            // Android-changed: SecurityManager stubbed out on Android
+            // Android-changed: SecurityManager stubbed out on Android.
             /*
             /* Determine if it's an applet or not *
 
@@ -507,7 +504,7 @@
             // }
         }
 
-        // Android-removed: SecurityManager stubbed out on Android
+        // Android-removed: SecurityManager stubbed out on Android.
         /*
         /* checkAccess regardless of whether or not threadgroup is
            explicitly passed in. *
@@ -965,7 +962,7 @@
      *       for example), the <code>interrupt</code> method should be used to
      *       interrupt the wait.
      *       For more information, see
-     *       <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
+     *       <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
      *       are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
      */
     @Deprecated
@@ -1001,7 +998,7 @@
      *        could be used to generate exceptions that the target thread was
      *        not prepared to handle.
      *        For more information, see
-     *        <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
+     *        <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
      *        are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
      */
     @Deprecated
@@ -1140,7 +1137,7 @@
      *     If another thread ever attempted to lock this resource, deadlock
      *     would result. Such deadlocks typically manifest themselves as
      *     "frozen" processes. For more information, see
-     *     <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">
+     *     <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html">
      *     Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
      * @throws UnsupportedOperationException always
      */
@@ -1174,7 +1171,7 @@
      *   monitor prior to calling <code>resume</code>, deadlock results.  Such
      *   deadlocks typically manifest themselves as "frozen" processes.
      *   For more information, see
-     *   <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
+     *   <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
      *   are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
      * @throws UnsupportedOperationException always
      */
@@ -1194,7 +1191,7 @@
      * @deprecated This method exists solely for use with {@link #suspend},
      *     which has been deprecated because it is deadlock-prone.
      *     For more information, see
-     *     <a href="{@docRoot}/../technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
+     *     <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html">Why
      *     are Thread.stop, Thread.suspend and Thread.resume Deprecated?</a>.
      * @throws UnsupportedOperationException always
      */
@@ -1246,18 +1243,7 @@
             synchronized(this) {
                 this.priority = newPriority;
                 if (isAlive()) {
-                    // BEGIN Android-added: Customize behavior of Thread.setPriority().
-                    // http://b/139521784
-                    // setPriority0(newPriority);
-                    ThreadPrioritySetter threadPrioritySetter =
-                        RuntimeHooks.getThreadPrioritySetter();
-                    int nativeTid = this.getNativeTid();
-                    if (threadPrioritySetter != null && nativeTid != 0) {
-                        threadPrioritySetter.setPriority(nativeTid, newPriority);
-                    } else {
-                        setPriority0(newPriority);
-                    }
-                    // END Android-added: Customize behavior of Thread.setPriority().
+                    setPriority0(newPriority);
                 }
             }
         }
@@ -1422,6 +1408,8 @@
      *          cleared when this exception is thrown.
      */
     // BEGIN Android-changed: Synchronize on separate lock object not this Thread.
+    // nativePeer and hence isAlive() can change asynchronously, but Thread::Destroy
+    // will always acquire and notify lock after isAlive() changes to false.
     // public final synchronized void join(long millis)
     public final void join(long millis)
     throws InterruptedException {
@@ -1578,7 +1566,7 @@
      * @see        SecurityManager#checkAccess(Thread)
      */
     public final void checkAccess() {
-        // Android-removed: SecurityManager stubbed out on Android
+        // Android-removed: SecurityManager stubbed out on Android.
         // SecurityManager security = System.getSecurityManager();
         // if (security != null) {
         //     security.checkAccess(this);
@@ -1630,7 +1618,7 @@
      */
     @CallerSensitive
     public ClassLoader getContextClassLoader() {
-        // Android-removed: SecurityManager stubbed out on Android
+        // Android-removed: SecurityManager stubbed out on Android.
         /*
         if (contextClassLoader == null)
             return null;
@@ -1666,7 +1654,7 @@
      * @since 1.2
      */
     public void setContextClassLoader(ClassLoader cl) {
-        // Android-removed: SecurityManager stubbed out on Android
+        // Android-removed: SecurityManager stubbed out on Android.
         // SecurityManager sm = System.getSecurityManager();
         // if (sm != null) {
         //     sm.checkPermission(new RuntimePermission("setContextClassLoader"));
@@ -1773,7 +1761,7 @@
      * @since 1.5
      */
     public static Map<Thread, StackTraceElement[]> getAllStackTraces() {
-        // Android-removed: SecurityManager stubbed out on Android
+        // Android-removed: SecurityManager stubbed out on Android.
         /*
         // check for getStackTrace permission
         SecurityManager security = System.getSecurityManager();
@@ -2097,7 +2085,7 @@
      * @since 1.5
      */
     public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
-        // Android-removed: SecurityManager stubbed out on Android
+        // Android-removed: SecurityManager stubbed out on Android.
         /*
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
@@ -2141,7 +2129,16 @@
         uncaughtExceptionPreHandler = eh;
     }
 
-    /** @hide */
+    /**
+     * Gets an {@link UncaughtExceptionHandler} that will be called before any
+     * returned by {@link #getUncaughtExceptionHandler()}. Can be {@code null} if
+     * was not explicitly set with
+     * {@link #setUncaughtExceptionPreHandler(UncaughtExceptionHandler)}.
+     *
+     * @return the uncaught exception prehandler for this thread
+     *
+     * @hide
+     */
     public static UncaughtExceptionHandler getUncaughtExceptionPreHandler() {
         return uncaughtExceptionPreHandler;
     }
@@ -2340,14 +2337,4 @@
 
     // Android-added: Android specific nativeGetStatus() method.
     private native int nativeGetStatus(boolean hasBeenStarted);
-
-    // BEGIN Android-added: Customize behavior of Thread.setPriority(). http://b/139521784
-    /**
-     * Returns the thread ID of the underlying native thread -- which is different from
-     * the {@link #getId() managed thread ID} -- or 0 if the native thread is not
-     * started or has stopped.
-     */
-    @FastNative
-    private native int getNativeTid();
-    // END Android-added: Customize behavior of Thread.setPriority(). http://b/139521784
 }
diff --git a/ojluni/src/main/java/java/lang/ThreadLocal.java b/ojluni/src/main/java/java/lang/ThreadLocal.java
index c29ce7e..1422044 100644
--- a/ojluni/src/main/java/java/lang/ThreadLocal.java
+++ b/ojluni/src/main/java/java/lang/ThreadLocal.java
@@ -413,7 +413,8 @@
         private Entry getEntry(ThreadLocal<?> key) {
             int i = key.threadLocalHashCode & (table.length - 1);
             Entry e = table[i];
-            if (e != null && e.get() == key)
+            // Android-changed: Use refersTo()
+            if (e != null && e.refersTo(key))
                 return e;
             else
                 return getEntryAfterMiss(key, i, e);
@@ -433,10 +434,10 @@
             int len = tab.length;
 
             while (e != null) {
-                ThreadLocal<?> k = e.get();
-                if (k == key)
+                // Android-changed: Use refersTo()
+                if (e.refersTo(key))
                     return e;
-                if (k == null)
+                if (e.refersTo(null))
                     expungeStaleEntry(i);
                 else
                     i = nextIndex(i, len);
diff --git a/ojluni/src/main/java/java/lang/Throwable.java b/ojluni/src/main/java/java/lang/Throwable.java
index 5a3f8a8..7596681 100644
--- a/ojluni/src/main/java/java/lang/Throwable.java
+++ b/ojluni/src/main/java/java/lang/Throwable.java
@@ -155,7 +155,7 @@
             new StackTraceElement[] {STACK_TRACE_ELEMENT_SENTINEL};
     }
 
-    // Android-removed: Use libcore.util.EmptyArray for the empty stack trace
+    // Android-removed: Use libcore.util.EmptyArray for the empty stack trace.
     // Adding the constant UNASSIGNED_STACK breaks serialization of some subclasses
     // /**
     //  * A shared value for an empty stack.
@@ -211,11 +211,11 @@
      * @serial
      * @since 1.4
      */
-    // Android-changed: Use libcore.util.EmptyArray for the empty stack trace
+    // Android-changed: Use libcore.util.EmptyArray for the empty stack trace.
     // private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
     private StackTraceElement[] stackTrace = libcore.util.EmptyArray.STACK_TRACE_ELEMENT;
 
-    // Android-removed: Use empty collection in place of SUPPRESSED_SENTINEL
+    // Android-removed: Use empty collection in place of SUPPRESSED_SENTINEL.
     // Adding this constant breaks serialization of some subclasses
     /*
     // Setting this static field introduces an acceptable
@@ -234,7 +234,7 @@
      * @serial
      * @since 1.7
      */
-    // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL
+    // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL.
     // private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
     private List<Throwable> suppressedExceptions = Collections.emptyList();
 
@@ -689,7 +689,7 @@
                                          String caption,
                                          String prefix,
                                          Set<Throwable> dejaVu) {
-        // Android-removed: Use of assert keyword which breaks serialization of some subclasses
+        // Android-removed: Use of assert keyword which breaks serialization of some subclasses.
         // (Using assert adds a static field that determines whether assertions are enabled.)
         // assert Thread.holdsLock(s.lock());
         if (dejaVu.contains(this)) {
@@ -794,17 +794,17 @@
     public synchronized Throwable fillInStackTrace() {
         if (stackTrace != null ||
             backtrace != null /* Out of protocol state */ ) {
-            // Android-changed: Use Android-specific nativeFillInStackTrace
+            // Android-changed: Use Android-specific nativeFillInStackTrace.
             // fillInStackTrace(0);
             backtrace = nativeFillInStackTrace();
-            // Android-changed: Use libcore.util.EmptyArray for the empty stack trace
+            // Android-changed: Use libcore.util.EmptyArray for the empty stack trace.
             // stackTrace = UNASSIGNED_STACK;
             stackTrace = libcore.util.EmptyArray.STACK_TRACE_ELEMENT;
         }
         return this;
     }
 
-    // Android-changed: Use Android-specific nativeFillInStackTrace
+    // Android-changed: Use Android-specific nativeFillInStackTrace.
     // private native Throwable fillInStackTrace(int dummy);
     @FastNative
     private static native Object nativeFillInStackTrace();
@@ -840,11 +840,11 @@
     private synchronized StackTraceElement[] getOurStackTrace() {
         // Initialize stack trace field with information from
         // backtrace if this is the first call to this method
-        // Android-changed: Use libcore.util.EmptyArray for the empty stack trace
+        // Android-changed: Use libcore.util.EmptyArray for the empty stack trace.
         // if (stackTrace == UNASSIGNED_STACK ||
         if (stackTrace == libcore.util.EmptyArray.STACK_TRACE_ELEMENT ||
             (stackTrace == null && backtrace != null) /* Out of protocol state */) {
-            // BEGIN Android-changed: Use Android-specific nativeGetStackTrace
+            // BEGIN Android-changed: Use Android-specific nativeGetStackTrace.
             // int depth = getStackTraceDepth();
             // stackTrace = new StackTraceElement[depth];
             // for (int i=0; i < depth; i++)
@@ -854,9 +854,9 @@
             if (stackTrace == null) {
                 return libcore.util.EmptyArray.STACK_TRACE_ELEMENT;
             }
-            // END Android-changed: Use Android-specific nativeGetStackTrace
+            // END Android-changed: Use Android-specific nativeGetStackTrace.
         } else if (stackTrace == null) {
-            // Android-changed: Use libcore.util.EmptyArray for the empty stack trace
+            // Android-changed: Use libcore.util.EmptyArray for the empty stack trace.
             // return UNASSIGNED_STACK;
             return libcore.util.EmptyArray.STACK_TRACE_ELEMENT;
         }
@@ -907,7 +907,7 @@
         }
     }
 
-    // Android-removed: Unused native method getStackTraceDepth()
+    // Android-removed: Unused native method getStackTraceDepth().
     // /**
     //  * Returns the number of elements in the stack trace (or 0 if the stack
     //  * trace is unavailable).
@@ -925,7 +925,7 @@
      * @throws IndexOutOfBoundsException if {@code index < 0 ||
      *         index >= getStackTraceDepth() }
      */
-    // Android-changed: Use Android-specific nativeGetStackTrace
+    // Android-changed: Use Android-specific nativeGetStackTrace.
     // native StackTraceElement getStackTraceElement(int index);
     @FastNative
     private static native StackTraceElement[] nativeGetStackTrace(Object stackState);
@@ -953,7 +953,7 @@
             List<Throwable> suppressed = null;
             if (suppressedExceptions.isEmpty()) {
                 // Use the sentinel for a zero-length list
-                // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL
+                // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL.
                 // suppressed = SUPPRESSED_SENTINEL;
                 suppressed = Collections.emptyList();
             } else { // Copy Throwables to new list
@@ -982,7 +982,7 @@
          */
         if (stackTrace != null) {
             if (stackTrace.length == 0) {
-                // Android-removed: clone() call not needed because of libcore.util.EmptyArray usage
+                // Android-removed: clone() call unneeded because of libcore.util.EmptyArray usage.
                 // stackTrace = UNASSIGNED_STACK.clone();
             }  else if (stackTrace.length == 1 &&
                         // Check for the marker of an immutable stack trace
@@ -999,7 +999,7 @@
             // from an exception serialized without that field in
             // older JDK releases; treat such exceptions as having
             // empty stack traces.
-            // Android-changed: Directly create empty array instead of cloning UNASSIGNED_STACK
+            // Android-changed: Directly create empty array instead of cloning UNASSIGNED_STACK.
             // stackTrace = UNASSIGNED_STACK.clone();
             stackTrace = new StackTraceElement[0];
         }
@@ -1090,7 +1090,7 @@
         if (suppressedExceptions == null) // Suppressed exceptions not recorded
             return;
 
-        // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL
+        // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL.
         // if (suppressedExceptions == SUPPRESSED_SENTINEL)
         if (suppressedExceptions.isEmpty())
             suppressedExceptions = new ArrayList<>(1);
@@ -1098,7 +1098,7 @@
         suppressedExceptions.add(exception);
     }
 
-    // Android-changed: Lazily initialize EMPTY_THROWABLE_ARRAY
+    // Android-changed: Lazily initialize EMPTY_THROWABLE_ARRAY.
     // private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];
     private static Throwable[] EMPTY_THROWABLE_ARRAY;
 
@@ -1118,12 +1118,12 @@
      * @since 1.7
      */
     public final synchronized Throwable[] getSuppressed() {
-        // Android-added: Lazily initialize EMPTY_THROWABLE_ARRAY
+        // Android-added: Lazily initialize EMPTY_THROWABLE_ARRAY.
         if (EMPTY_THROWABLE_ARRAY == null) {
             EMPTY_THROWABLE_ARRAY = new Throwable[0];
         }
 
-        // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL
+        // Android-changed: Use empty collection in place of SUPPRESSED_SENTINEL.
         // if (suppressedExceptions == SUPPRESSED_SENTINEL ||
         //    suppressedExceptions == null)
         if (suppressedExceptions == null || suppressedExceptions.isEmpty())
diff --git a/ojluni/src/main/java/java/lang/UnsupportedOperationException.java b/ojluni/src/main/java/java/lang/UnsupportedOperationException.java
index fcb355c..1de2508 100644
--- a/ojluni/src/main/java/java/lang/UnsupportedOperationException.java
+++ b/ojluni/src/main/java/java/lang/UnsupportedOperationException.java
@@ -29,7 +29,7 @@
  * Thrown to indicate that the requested operation is not supported.<p>
  *
  * This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/lang/annotation/Annotation.java b/ojluni/src/main/java/java/lang/annotation/Annotation.java
index 7de6746..efa0246 100644
--- a/ojluni/src/main/java/java/lang/annotation/Annotation.java
+++ b/ojluni/src/main/java/java/lang/annotation/Annotation.java
@@ -50,28 +50,28 @@
      * to the corresponding member of this annotation, as defined below:
      * <ul>
      *    <li>Two corresponding primitive typed members whose values are
-     *    <tt>x</tt> and <tt>y</tt> are considered equal if <tt>x == y</tt>,
-     *    unless their type is <tt>float</tt> or <tt>double</tt>.
+     *    {@code x} and {@code y} are considered equal if {@code x == y},
+     *    unless their type is {@code float} or {@code double}.
      *
-     *    <li>Two corresponding <tt>float</tt> members whose values
-     *    are <tt>x</tt> and <tt>y</tt> are considered equal if
-     *    <tt>Float.valueOf(x).equals(Float.valueOf(y))</tt>.
-     *    (Unlike the <tt>==</tt> operator, NaN is considered equal
-     *    to itself, and <tt>0.0f</tt> unequal to <tt>-0.0f</tt>.)
+     *    <li>Two corresponding {@code float} members whose values
+     *    are {@code x} and {@code y} are considered equal if
+     *    {@code Float.valueOf(x).equals(Float.valueOf(y))}.
+     *    (Unlike the {@code ==} operator, NaN is considered equal
+     *    to itself, and {@code 0.0f} unequal to {@code -0.0f}.)
      *
-     *    <li>Two corresponding <tt>double</tt> members whose values
-     *    are <tt>x</tt> and <tt>y</tt> are considered equal if
-     *    <tt>Double.valueOf(x).equals(Double.valueOf(y))</tt>.
-     *    (Unlike the <tt>==</tt> operator, NaN is considered equal
-     *    to itself, and <tt>0.0</tt> unequal to <tt>-0.0</tt>.)
+     *    <li>Two corresponding {@code double} members whose values
+     *    are {@code x} and {@code y} are considered equal if
+     *    {@code Double.valueOf(x).equals(Double.valueOf(y))}.
+     *    (Unlike the {@code ==} operator, NaN is considered equal
+     *    to itself, and {@code 0.0} unequal to {@code -0.0}.)
      *
-     *    <li>Two corresponding <tt>String</tt>, <tt>Class</tt>, enum, or
-     *    annotation typed members whose values are <tt>x</tt> and <tt>y</tt>
-     *    are considered equal if <tt>x.equals(y)</tt>.  (Note that this
+     *    <li>Two corresponding {@code String}, {@code Class}, enum, or
+     *    annotation typed members whose values are {@code x} and {@code y}
+     *    are considered equal if {@code x.equals(y)}.  (Note that this
      *    definition is recursive for annotation typed members.)
      *
-     *    <li>Two corresponding array typed members <tt>x</tt> and <tt>y</tt>
-     *    are considered equal if <tt>Arrays.equals(x, y)</tt>, for the
+     *    <li>Two corresponding array typed members {@code x} and {@code y}
+     *    are considered equal if {@code Arrays.equals(x, y)}, for the
      *    appropriate overloading of {@link java.util.Arrays#equals}.
      * </ul>
      *
@@ -93,16 +93,16 @@
      *
      * <p>The hash code of a member-value depends on its type:
      * <ul>
-     * <li>The hash code of a primitive value <tt><i>v</i></tt> is equal to
-     *     <tt><i>WrapperType</i>.valueOf(<i>v</i>).hashCode()</tt>, where
-     *     <tt><i>WrapperType</i></tt> is the wrapper type corresponding
-     *     to the primitive type of <tt><i>v</i></tt> ({@link Byte},
+     * <li>The hash code of a primitive value <i>{@code v}</i> is equal to
+     *     <code><i>WrapperType</i>.valueOf(<i>v</i>).hashCode()</code>, where
+     *     <i>{@code WrapperType}</i> is the wrapper type corresponding
+     *     to the primitive type of <i>{@code v}</i> ({@link Byte},
      *     {@link Character}, {@link Double}, {@link Float}, {@link Integer},
      *     {@link Long}, {@link Short}, or {@link Boolean}).
      *
      * <li>The hash code of a string, enum, class, or annotation member-value
-     I     <tt><i>v</i></tt> is computed as by calling
-     *     <tt><i>v</i>.hashCode()</tt>.  (In the case of annotation
+     I     <i>{@code v}</i> is computed as by calling
+     *     <code><i>v</i>.hashCode()</code>.  (In the case of annotation
      *     member values, this is a recursive definition.)
      *
      * <li>The hash code of an array member-value is computed by calling
diff --git a/ojluni/src/main/java/java/lang/annotation/AnnotationFormatError.java b/ojluni/src/main/java/java/lang/annotation/AnnotationFormatError.java
index f02104f..c296ced 100644
--- a/ojluni/src/main/java/java/lang/annotation/AnnotationFormatError.java
+++ b/ojluni/src/main/java/java/lang/annotation/AnnotationFormatError.java
@@ -40,7 +40,7 @@
     private static final long serialVersionUID = -4256701562333669892L;
 
     /**
-     * Constructs a new <tt>AnnotationFormatError</tt> with the specified
+     * Constructs a new {@code AnnotationFormatError} with the specified
      * detail message.
      *
      * @param   message   the detail message.
@@ -50,13 +50,13 @@
     }
 
     /**
-     * Constructs a new <tt>AnnotationFormatError</tt> with the specified
+     * Constructs a new {@code AnnotationFormatError} with the specified
      * detail message and cause.  Note that the detail message associated
-     * with <code>cause</code> is <i>not</i> automatically incorporated in
+     * with {@code cause} is <i>not</i> automatically incorporated in
      * this error's detail message.
      *
      * @param  message the detail message
-     * @param  cause the cause (A <tt>null</tt> value is permitted, and
+     * @param  cause the cause (A {@code null} value is permitted, and
      *     indicates that the cause is nonexistent or unknown.)
      */
     public AnnotationFormatError(String message, Throwable cause) {
@@ -65,12 +65,12 @@
 
 
     /**
-     * Constructs a new <tt>AnnotationFormatError</tt> with the specified
+     * Constructs a new {@code AnnotationFormatError} with the specified
      * cause and a detail message of
-     * <tt>(cause == null ? null : cause.toString())</tt> (which
-     * typically contains the class and detail message of <tt>cause</tt>).
+     * {@code (cause == null ? null : cause.toString())} (which
+     * typically contains the class and detail message of {@code cause}).
      *
-     * @param  cause the cause (A <tt>null</tt> value is permitted, and
+     * @param  cause the cause (A {@code null} value is permitted, and
      *     indicates that the cause is nonexistent or unknown.)
      */
     public AnnotationFormatError(Throwable cause) {
diff --git a/ojluni/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java b/ojluni/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java
index 88e2712..5152da2 100644
--- a/ojluni/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java
+++ b/ojluni/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -42,9 +42,9 @@
     private static final long serialVersionUID = 8125925355765570191L;
 
     /**
-     * The <tt>Method</tt> object for the annotation element.
+     * The {@code Method} object for the annotation element.
      */
-    private final Method element;
+    private final transient Method element;
 
     /**
      * The (erroneous) type of data found in the annotation.  This string
@@ -57,10 +57,12 @@
      * Constructs an AnnotationTypeMismatchException for the specified
      * annotation type element and found data type.
      *
-     * @param element the <tt>Method</tt> object for the annotation element
+     * @param element the {@code Method} object for the annotation
+     * element, may be {@code null}
      * @param foundType the (erroneous) type of data found in the annotation.
      *        This string may, but is not required to, contain the value
-     *        as well.  The exact format of the string is unspecified.
+     *        as well.  The exact format of the string is unspecified,
+     *        may be {@code null}.
      */
     public AnnotationTypeMismatchException(Method element, String foundType) {
         super("Incorrectly typed data found for annotation element " + element
@@ -70,9 +72,12 @@
     }
 
     /**
-     * Returns the <tt>Method</tt> object for the incorrectly typed element.
+     * Returns the {@code Method} object for the incorrectly typed element.
+     * The value may be unavailable if this exception has been
+     * serialized and then read back in.
      *
-     * @return the <tt>Method</tt> object for the incorrectly typed element
+     * @return the {@code Method} object for the incorrectly typed
+     * element, or {@code null} if unavailable
      */
     public Method element() {
         return this.element;
@@ -81,7 +86,8 @@
     /**
      * Returns the type of data found in the incorrectly typed element.
      * The returned string may, but is not required to, contain the value
-     * as well.  The exact format of the string is unspecified.
+     * as well.  The exact format of the string is unspecified and the string
+     * may be {@code null}.
      *
      * @return the type of data found in the incorrectly typed element
      */
diff --git a/ojluni/src/main/java/java/lang/annotation/Documented.java b/ojluni/src/main/java/java/lang/annotation/Documented.java
index 348a312..be263c7 100644
--- a/ojluni/src/main/java/java/lang/annotation/Documented.java
+++ b/ojluni/src/main/java/java/lang/annotation/Documented.java
@@ -26,12 +26,24 @@
 package java.lang.annotation;
 
 /**
- * Indicates that annotations with a type are to be documented by javadoc
- * and similar tools by default.  This type should be used to annotate the
- * declarations of types whose annotations affect the use of annotated
- * elements by their clients.  If a type declaration is annotated with
- * Documented, its annotations become part of the public API
- * of the annotated elements.
+ * If the annotation {@code @Documented} is present on the declaration
+ * of an annotation type <i>A</i>, then any {@code @A} annotation on
+ * an element is considered part of the element's public contract.
+ *
+ * In more detail, when an annotation type <i>A</i> is annotated with
+ * {@code Documented}, the presence and value of annotations of type
+ * <i>A</i> are a part of the public contract of the elements <i>A</i>
+ * annotates.
+ *
+ * Conversely, if an annotation type <i>B</i> is <em>not</em>
+ * annotated with {@code Documented}, the presence and value of
+ * <i>B</i> annotations are <em>not</em> part of the public contract
+ * of the elements <i>B</i> annotates.
+ *
+ * Concretely, if an annotation type is annotated with {@code
+ * Documented}, by default a tool like javadoc will display
+ * annotations of that type in its output while annotations of
+ * annotation types without {@code Documented} will not be displayed.
  *
  * @author  Joshua Bloch
  * @since 1.5
diff --git a/ojluni/src/main/java/java/lang/annotation/ElementType.java b/ojluni/src/main/java/java/lang/annotation/ElementType.java
index 4590f39..bbab9cd 100644
--- a/ojluni/src/main/java/java/lang/annotation/ElementType.java
+++ b/ojluni/src/main/java/java/lang/annotation/ElementType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,7 @@
 /**
  * The constants of this enumerated type provide a simple classification of the
  * syntactic locations where annotations may appear in a Java program. These
- * constants are used in {@link Target java.lang.annotation.Target}
+ * constants are used in {@link java.lang.annotation.Target Target}
  * meta-annotations to specify where it is legal to write annotations of a
  * given type.
  *
@@ -37,16 +37,16 @@
  * <em>type contexts</em> , where annotations apply to types used in
  * declarations and expressions.
  *
- * <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
- * #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
- * {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
- * to the declaration contexts in JLS 9.6.4.1.
+ * <p>The constants {@link #ANNOTATION_TYPE}, {@link #CONSTRUCTOR}, {@link
+ * #FIELD}, {@link #LOCAL_VARIABLE}, {@link #METHOD}, {@link #PACKAGE}, {@link
+ * #MODULE}, {@link #PARAMETER}, {@link #TYPE}, and {@link #TYPE_PARAMETER}
+ * correspond to the declaration contexts in JLS 9.6.4.1.
  *
  * <p>For example, an annotation whose type is meta-annotated with
  * {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
  * field declaration.
  *
- * <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS
+ * <p>The constant {@link #TYPE_USE} corresponds to the type contexts in JLS
  * 4.11, as well as to two declaration contexts: type declarations (including
  * annotation type declarations) and type parameter declarations.
  *
@@ -107,5 +107,12 @@
      *
      * @since 1.8
      */
-    TYPE_USE
+    TYPE_USE,
+
+    /**
+     * Module declaration.
+     *
+     * @since 9
+     */
+    MODULE
 }
diff --git a/ojluni/src/main/java/java/lang/annotation/Inherited.java b/ojluni/src/main/java/java/lang/annotation/Inherited.java
index 83391e2..9b4080c 100644
--- a/ojluni/src/main/java/java/lang/annotation/Inherited.java
+++ b/ojluni/src/main/java/java/lang/annotation/Inherited.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -44,7 +44,7 @@
  *
  * @author  Joshua Bloch
  * @since 1.5
- * @jls 9.6.3.3 @Inherited
+ * @jls 9.6.4.3 @Inherited
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/ojluni/src/main/java/java/lang/annotation/Repeatable.java b/ojluni/src/main/java/java/lang/annotation/Repeatable.java
index 7a2daa8..f9dc170 100644
--- a/ojluni/src/main/java/java/lang/annotation/Repeatable.java
+++ b/ojluni/src/main/java/java/lang/annotation/Repeatable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,8 +33,8 @@
  * type</em> for the repeatable annotation type.
  *
  * @since 1.8
- * @jls 9.6 Annotation Types
- * @jls 9.7 Annotations
+ * @jls 9.6.3 Repeatable Annotation Types
+ * @jls 9.7.5 Multiple Annotations of the Same Type
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/ojluni/src/main/java/java/lang/annotation/Retention.java b/ojluni/src/main/java/java/lang/annotation/Retention.java
index 5efa43f..6c2a604 100644
--- a/ojluni/src/main/java/java/lang/annotation/Retention.java
+++ b/ojluni/src/main/java/java/lang/annotation/Retention.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,7 +38,7 @@
  *
  * @author  Joshua Bloch
  * @since 1.5
- * @jls 9.6.3.2 @Retention
+ * @jls 9.6.4.2 @Retention
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/ojluni/src/main/java/java/lang/annotation/Target.java b/ojluni/src/main/java/java/lang/annotation/Target.java
index 1ad4056..fabb062 100644
--- a/ojluni/src/main/java/java/lang/annotation/Target.java
+++ b/ojluni/src/main/java/java/lang/annotation/Target.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -72,6 +72,7 @@
  * @since 1.5
  * @jls 9.6.4.1 @Target
  * @jls 9.7.4 Where Annotations May Appear
+ * @jls 9.7.5 Multiple Annotations of the Same Type
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/ojluni/src/main/java/java/lang/invoke/CallSite.java b/ojluni/src/main/java/java/lang/invoke/CallSite.java
index 85b4bb9..069b2fd 100644
--- a/ojluni/src/main/java/java/lang/invoke/CallSite.java
+++ b/ojluni/src/main/java/java/lang/invoke/CallSite.java
@@ -25,7 +25,7 @@
 
 package java.lang.invoke;
 
-// Android-changed: Not using Empty
+// Android-changed: Not using Empty.
 //import sun.invoke.empty.Empty;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
@@ -104,9 +104,10 @@
      */
     /*package-private*/
     CallSite(MethodType type) {
-        // Android-changed: No cache for these so create uninitializedCallSite target here using
-        // method handle transformations to create a method handle that has the expected method
-        // type but throws an IllegalStateException.
+        // Android-changed: No cache for these.
+        // Instead create uninitializedCallSite target here using method handle transformations
+        // to create a method handle that has the expected method type but throws an
+        // IllegalStateException.
         // target = makeUninitializedCallSite(type);
         this.target = MethodHandles.throwException(type.returnType(), IllegalStateException.class);
         this.target = MethodHandles.insertArguments(
@@ -115,8 +116,7 @@
             this.target = MethodHandles.dropArguments(this.target, 0, type.ptypes());
         }
 
-        // Android-changed: Using initializer method for GET_TARGET
-        // rather than complex static initializer.
+        // Android-changed: Using initializer method for GET_TARGET instead of static initializer.
         initializeGetTarget();
     }
 
@@ -130,8 +130,7 @@
         target.type();  // null check
         this.target = target;
 
-        // Android-changed: Using initializer method for GET_TARGET
-        // rather than complex static initializer.
+        // Android-changed: Using initializer method for GET_TARGET instead of static initializer.
         initializeGetTarget();
     }
 
@@ -153,8 +152,7 @@
         checkTargetChange(this.target, boundTarget);
         this.target = boundTarget;
 
-        // Android-changed: Using initializer method for GET_TARGET
-        // rather than complex static initializer.
+        // Android-changed: Using initializer method for GET_TARGET instead of static initializer.
         initializeGetTarget();
     }
 
@@ -244,9 +242,9 @@
     private static MethodHandle GET_TARGET = null;
 
     private void initializeGetTarget() {
-        // Android-changed: moved from static initializer for
-        // GET_TARGET to avoid issues with running early. Called from
-        // constructors. CallSite creation is not performance critical.
+        // Android-changed: moved from static initializer for GET_TARGET.
+        // This avoids issues with running early. Called from constructors.
+        // CallSite creation is not performance critical.
         synchronized (CallSite.class) {
             if (GET_TARGET == null) {
                 try {
diff --git a/ojluni/src/main/java/java/lang/ref/Reference.java b/ojluni/src/main/java/java/lang/ref/Reference.java
index e5126b5..faaddd7 100644
--- a/ojluni/src/main/java/java/lang/ref/Reference.java
+++ b/ojluni/src/main/java/java/lang/ref/Reference.java
@@ -42,6 +42,7 @@
 public abstract class Reference<T> {
     // BEGIN Android-changed: Reimplemented to accommodate a different GC and compiler.
     // ClassLinker knows about the fields of this class.
+    // Backported refersTo() from OpenJDK 16.
 
     /**
      * Forces JNI path.
@@ -56,11 +57,16 @@
      * Used by the reference processor to determine whether or not the referent
      * can be immediately returned. Because the referent might get swept during
      * GC, the slow path, which passes through JNI, must be taken.
+     * This is only modified with mutators suspended, and hence does not need to
+     * be volatile.
      */
     private static boolean slowPathEnabled = false;
 
     // Treated specially by GC. ART's ClassLinker::LinkFields() knows this is the
     // alphabetically last non-static field.
+    // We assume that Reference.get() and Reference.clear() are intended to be
+    // callable concurrently, and thus referent accesses should be treated as
+    // volatile everywhere.
     volatile T referent;
 
     final ReferenceQueue<? super T> queue;
@@ -108,6 +114,23 @@
     private final native T getReferent();
 
     /**
+     * Tests if the referent of this reference object is {@code obj}.
+     * Using a {@code null} {@code obj} returns {@code true} if the
+     * reference object has been cleared.
+     *
+     * @param  obj the object to compare with this reference object's referent
+     * @return {@code true} if {@code obj} is the referent of this reference object
+     * @hide
+     */
+    public final boolean refersTo(T obj) {
+        return refersTo0(obj);
+    }
+
+    /* Implementation of refersTo(). */
+    @FastNative
+    private final native boolean refersTo0(Object o);
+
+    /**
      * Clears this reference object.  Invoking this method will not cause this
      * object to be enqueued.
      *
@@ -125,15 +148,38 @@
 
     /* -- Queue operations -- */
 
+    // Android-changed: deprecate since 9.
+    // @Deprecated(since="16")
     /**
-     * Tells whether or not this reference object has been enqueued, either by
-     * the program or by the garbage collector.  If this reference object was
-     * not registered with a queue when it was created, then this method will
-     * always return <code>false</code>.
+     * Tests if this reference object is in its associated queue, if any.
+     * This method returns {@code true} only if all of the following conditions
+     * are met:
+     * <ul>
+     * <li>this reference object was registered with a queue when it was created; and
+     * <li>the garbage collector has added this reference object to the queue
+     *     or {@link #enqueue()} is called; and
+     * <li>this reference object is not yet removed from the queue.
+     * </ul>
+     * Otherwise, this method returns {@code false}.
+     * This method may return {@code false} if this reference object has been cleared
+     * but not enqueued due to the race condition.
      *
-     * @return   <code>true</code> if and only if this reference object has
-     *           been enqueued
+     * @deprecated
+     * This method was never implemented to test if a reference object has
+     * been cleared and enqueued as it was previously specified since 1.2.
+     * This method could be misused due to the inherent race condition
+     * or without an associated {@code ReferenceQueue}.
+     * An application relying on this method to release critical resources
+     * could cause serious performance issue.
+     * An application should use {@link ReferenceQueue} to reliably determine
+     * what reference objects that have been enqueued or
+     * {@code refersTo(null)} to determine if this reference
+     * object has been cleared.
+     *
+     * @return   {@code true} if and only if this reference object is
+     *           in its associated queue (if any).
      */
+    @Deprecated(since="9")
     public boolean isEnqueued() {
         // Contrary to what the documentation says, this method returns false
         // after this reference object has been removed from its queue
diff --git a/ojluni/src/main/java/java/lang/reflect/AccessibleObject.java b/ojluni/src/main/java/java/lang/reflect/AccessibleObject.java
index 18442fa..2ba6276 100644
--- a/ojluni/src/main/java/java/lang/reflect/AccessibleObject.java
+++ b/ojluni/src/main/java/java/lang/reflect/AccessibleObject.java
@@ -146,7 +146,7 @@
             // }
 
             Class<?> clazz = c.getDeclaringClass();
-            if (c.getDeclaringClass() == Class.class) {
+            if (clazz == Class.class) {
                 throw new SecurityException("Can not make a java.lang.Class" +
                                             " constructor accessible");
             } else if (clazz == Method.class) {
diff --git a/ojluni/src/main/java/java/lang/reflect/Array.java b/ojluni/src/main/java/java/lang/reflect/Array.java
index 95a8091..db7e9a2 100644
--- a/ojluni/src/main/java/java/lang/reflect/Array.java
+++ b/ojluni/src/main/java/java/lang/reflect/Array.java
@@ -111,7 +111,7 @@
      */
     public static Object newInstance(Class<?> componentType, int... dimensions)
         throws IllegalArgumentException, NegativeArraySizeException {
-        // Android-changed: New implementation of newInstance(Class, int...)
+        // Android-changed: New implementation of newInstance(Class, int...).
         if (dimensions.length <= 0 || dimensions.length > 255) {
             throw new IllegalArgumentException("Bad number of dimensions: " + dimensions.length);
         }
@@ -132,7 +132,7 @@
      * @exception IllegalArgumentException if the object argument is not
      * an array
      */
-    // Android-changed: Non-native implementation of getLength(Object)
+    // Android-changed: Non-native implementation of getLength(Object).
     // Android-changed: Removal of explicit throws IllegalArgumentException from method signature.
     public static int getLength(Object array)
         /* throws IllegalArgumentException */ {
@@ -174,7 +174,7 @@
      * argument is negative, or if it is greater than or equal to the
      * length of the specified array
      */
-    // Android-changed: Non-native implementation of get(Object, int)
+    // Android-changed: Non-native implementation of get(Object, int).
     public static Object get(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof Object[]) {
@@ -226,7 +226,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getBoolean(Object, int)
+    // Android-changed: Non-native implementation of getBoolean(Object, int).
     public static boolean getBoolean(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof boolean[]) {
@@ -251,7 +251,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getByte(Object, int)
+    // Android-changed: Non-native implementation of getByte(Object, int).
     public static byte getByte(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof byte[]) {
@@ -276,7 +276,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getChar(Object, int)
+    // Android-changed: Non-native implementation of getChar(Object, int).
     public static char getChar(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof char[]) {
@@ -301,7 +301,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getShort(Object, int)
+    // Android-changed: Non-native implementation of getShort(Object, int).
     public static short getShort(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof short[]) {
@@ -328,7 +328,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getInt(Object, int)
+    // Android-changed: Non-native implementation of getInt(Object, int).
     public static int getInt(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof int[]) {
@@ -359,7 +359,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getLong(Object, int)
+    // Android-changed: Non-native implementation of getLong(Object, int).
     public static long getLong(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof long[]) {
@@ -392,7 +392,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getFloat(Object, int)
+    // Android-changed: Non-native implementation of getFloat(Object, int).
     public static float getFloat(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof float[]) {
@@ -427,7 +427,7 @@
      * length of the specified array
      * @see Array#get
      */
-    // Android-changed: Non-native implementation of getDouble(Object, int)
+    // Android-changed: Non-native implementation of getDouble(Object, int).
     public static double getDouble(Object array, int index)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof double[]) {
@@ -465,7 +465,7 @@
      * argument is negative, or if it is greater than or equal to
      * the length of the specified array
      */
-    // Android-changed: Non-native implementation of set(Object, int, Object)
+    // Android-changed: Non-native implementation of set(Object, int, Object).
     public static void set(Object array, int index, Object value)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (!array.getClass().isArray()) {
@@ -518,8 +518,8 @@
      * the length of the specified array
      * @see Array#set
      */
-    // Android-changed: Non-native implementation of setBoolean(Object, int, boolean)
-    // Android-changed: Removal of explicit runtime exceptions throws clause
+    // Android-changed: Non-native implementation of setBoolean(Object, int, boolean).
+    // Android-changed: Removal of explicit runtime exceptions throws clause.
     public static void setBoolean(Object array, int index, boolean z)
         /* throws IllegalArgumentException, ArrayIndexOutOfBoundsException */ {
         if (array instanceof boolean[]) {
@@ -546,7 +546,7 @@
      * the length of the specified array
      * @see Array#set
      */
-    // Android-changed: Non-native implementation of setByte(Object, int, byte)
+    // Android-changed: Non-native implementation of setByte(Object, int, byte).
     public static void setByte(Object array, int index, byte b)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof byte[]) {
@@ -583,7 +583,7 @@
      * the length of the specified array
      * @see Array#set
      */
-    // Android-changed: Non-native implementation of setChar(Object, int, char)
+    // Android-changed: Non-native implementation of setChar(Object, int, char).
     public static void setChar(Object array, int index, char c)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof char[]) {
@@ -618,7 +618,7 @@
      * the length of the specified array
      * @see Array#set
      */
-    // Android-changed: Non-native implementation of setShort(Object, int, short)
+    // Android-changed: Non-native implementation of setShort(Object, int, short).
     public static void setShort(Object array, int index, short s)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof short[]) {
@@ -653,7 +653,7 @@
      * the length of the specified array
      * @see Array#set
      */
-    // Android-changed: Non-native implementation of setInt(Object, int, int)
+    // Android-changed: Non-native implementation of setInt(Object, int, int).
     public static void setInt(Object array, int index, int i)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof int[]) {
@@ -686,7 +686,7 @@
      * the length of the specified array
      * @see Array#set
      */
-    // Android-changed: Non-native implementation of setBoolean(Object, int, long)
+    // Android-changed: Non-native implementation of setBoolean(Object, int, long).
     public static void setLong(Object array, int index, long l)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof long[]) {
@@ -745,7 +745,7 @@
      * the length of the specified array
      * @see Array#set
      */
-    // Android-changed: Non-native implementation of setDouble(Object, int, double)
+    // Android-changed: Non-native implementation of setDouble(Object, int, double).
     public static void setDouble(Object array, int index, double d)
         throws IllegalArgumentException, ArrayIndexOutOfBoundsException {
         if (array instanceof double[]) {
@@ -759,7 +759,7 @@
      * Private
      */
 
-    // Android-added: Added javadocs for newArray(Class, int)
+    // Android-added: Added javadocs for newArray(Class, int).
     /**
      * Returns a new array of the specified component type and length.
      * Equivalent to {@code new componentType[size]}.
@@ -769,7 +769,7 @@
      * @throws NegativeArraySizeException
      *             if {@code size < 0}
      */
-    // Android-changed: Non-native implementation of newArray(Class, int)
+    // Android-changed: Non-native implementation of newArray(Class, int).
     private static Object newArray(Class<?> componentType, int length)
         throws NegativeArraySizeException {
         if (!componentType.isPrimitive()) {
@@ -803,7 +803,7 @@
         throws IllegalArgumentException, NegativeArraySizeException;
     */
 
-    // Android-added: createMultiArray(Class, int[]) method. Used instead of multiNewArray
+    // Android-added: createMultiArray(Class, int[]) method. Used instead of multiNewArray.
     /*
      * Create a multi-dimensional array of objects with the specified type.
      */
diff --git a/ojluni/src/main/java/java/math/BigDecimal.java b/ojluni/src/main/java/java/math/BigDecimal.java
new file mode 100644
index 0000000..9e012a6
--- /dev/null
+++ b/ojluni/src/main/java/java/math/BigDecimal.java
@@ -0,0 +1,5285 @@
+/*
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Portions Copyright IBM Corporation, 2001. All Rights Reserved.
+ */
+
+package java.math;
+
+import static java.math.BigInteger.LONG_MASK;
+import java.util.Arrays;
+
+/**
+ * Immutable, arbitrary-precision signed decimal numbers.  A
+ * {@code BigDecimal} consists of an arbitrary precision integer
+ * <i>unscaled value</i> and a 32-bit integer <i>scale</i>.  If zero
+ * or positive, the scale is the number of digits to the right of the
+ * decimal point.  If negative, the unscaled value of the number is
+ * multiplied by ten to the power of the negation of the scale.  The
+ * value of the number represented by the {@code BigDecimal} is
+ * therefore <tt>(unscaledValue &times; 10<sup>-scale</sup>)</tt>.
+ *
+ * <p>The {@code BigDecimal} class provides operations for
+ * arithmetic, scale manipulation, rounding, comparison, hashing, and
+ * format conversion.  The {@link #toString} method provides a
+ * canonical representation of a {@code BigDecimal}.
+ *
+ * <p>The {@code BigDecimal} class gives its user complete control
+ * over rounding behavior.  If no rounding mode is specified and the
+ * exact result cannot be represented, an exception is thrown;
+ * otherwise, calculations can be carried out to a chosen precision
+ * and rounding mode by supplying an appropriate {@link MathContext}
+ * object to the operation.  In either case, eight <em>rounding
+ * modes</em> are provided for the control of rounding.  Using the
+ * integer fields in this class (such as {@link #ROUND_HALF_UP}) to
+ * represent rounding mode is largely obsolete; the enumeration values
+ * of the {@code RoundingMode} {@code enum}, (such as {@link
+ * RoundingMode#HALF_UP}) should be used instead.
+ *
+ * <p>When a {@code MathContext} object is supplied with a precision
+ * setting of 0 (for example, {@link MathContext#UNLIMITED}),
+ * arithmetic operations are exact, as are the arithmetic methods
+ * which take no {@code MathContext} object.  (This is the only
+ * behavior that was supported in releases prior to 5.)  As a
+ * corollary of computing the exact result, the rounding mode setting
+ * of a {@code MathContext} object with a precision setting of 0 is
+ * not used and thus irrelevant.  In the case of divide, the exact
+ * quotient could have an infinitely long decimal expansion; for
+ * example, 1 divided by 3.  If the quotient has a nonterminating
+ * decimal expansion and the operation is specified to return an exact
+ * result, an {@code ArithmeticException} is thrown.  Otherwise, the
+ * exact result of the division is returned, as done for other
+ * operations.
+ *
+ * <p>When the precision setting is not 0, the rules of
+ * {@code BigDecimal} arithmetic are broadly compatible with selected
+ * modes of operation of the arithmetic defined in ANSI X3.274-1996
+ * and ANSI X3.274-1996/AM 1-2000 (section 7.4).  Unlike those
+ * standards, {@code BigDecimal} includes many rounding modes, which
+ * were mandatory for division in {@code BigDecimal} releases prior
+ * to 5.  Any conflicts between these ANSI standards and the
+ * {@code BigDecimal} specification are resolved in favor of
+ * {@code BigDecimal}.
+ *
+ * <p>Since the same numerical value can have different
+ * representations (with different scales), the rules of arithmetic
+ * and rounding must specify both the numerical result and the scale
+ * used in the result's representation.
+ *
+ *
+ * <p>In general the rounding modes and precision setting determine
+ * how operations return results with a limited number of digits when
+ * the exact result has more digits (perhaps infinitely many in the
+ * case of division) than the number of digits returned.
+ *
+ * First, the
+ * total number of digits to return is specified by the
+ * {@code MathContext}'s {@code precision} setting; this determines
+ * the result's <i>precision</i>.  The digit count starts from the
+ * leftmost nonzero digit of the exact result.  The rounding mode
+ * determines how any discarded trailing digits affect the returned
+ * result.
+ *
+ * <p>For all arithmetic operators , the operation is carried out as
+ * though an exact intermediate result were first calculated and then
+ * rounded to the number of digits specified by the precision setting
+ * (if necessary), using the selected rounding mode.  If the exact
+ * result is not returned, some digit positions of the exact result
+ * are discarded.  When rounding increases the magnitude of the
+ * returned result, it is possible for a new digit position to be
+ * created by a carry propagating to a leading {@literal "9"} digit.
+ * For example, rounding the value 999.9 to three digits rounding up
+ * would be numerically equal to one thousand, represented as
+ * 100&times;10<sup>1</sup>.  In such cases, the new {@literal "1"} is
+ * the leading digit position of the returned result.
+ *
+ * <p>Besides a logical exact result, each arithmetic operation has a
+ * preferred scale for representing a result.  The preferred
+ * scale for each operation is listed in the table below.
+ *
+ * <table border>
+ * <caption><b>Preferred Scales for Results of Arithmetic Operations
+ * </b></caption>
+ * <tr><th>Operation</th><th>Preferred Scale of Result</th></tr>
+ * <tr><td>Add</td><td>max(addend.scale(), augend.scale())</td>
+ * <tr><td>Subtract</td><td>max(minuend.scale(), subtrahend.scale())</td>
+ * <tr><td>Multiply</td><td>multiplier.scale() + multiplicand.scale()</td>
+ * <tr><td>Divide</td><td>dividend.scale() - divisor.scale()</td>
+ * </table>
+ *
+ * These scales are the ones used by the methods which return exact
+ * arithmetic results; except that an exact divide may have to use a
+ * larger scale since the exact result may have more digits.  For
+ * example, {@code 1/32} is {@code 0.03125}.
+ *
+ * <p>Before rounding, the scale of the logical exact intermediate
+ * result is the preferred scale for that operation.  If the exact
+ * numerical result cannot be represented in {@code precision}
+ * digits, rounding selects the set of digits to return and the scale
+ * of the result is reduced from the scale of the intermediate result
+ * to the least scale which can represent the {@code precision}
+ * digits actually returned.  If the exact result can be represented
+ * with at most {@code precision} digits, the representation
+ * of the result with the scale closest to the preferred scale is
+ * returned.  In particular, an exactly representable quotient may be
+ * represented in fewer than {@code precision} digits by removing
+ * trailing zeros and decreasing the scale.  For example, rounding to
+ * three digits using the {@linkplain RoundingMode#FLOOR floor}
+ * rounding mode, <br>
+ *
+ * {@code 19/100 = 0.19   // integer=19,  scale=2} <br>
+ *
+ * but<br>
+ *
+ * {@code 21/110 = 0.190  // integer=190, scale=3} <br>
+ *
+ * <p>Note that for add, subtract, and multiply, the reduction in
+ * scale will equal the number of digit positions of the exact result
+ * which are discarded. If the rounding causes a carry propagation to
+ * create a new high-order digit position, an additional digit of the
+ * result is discarded than when no new digit position is created.
+ *
+ * <p>Other methods may have slightly different rounding semantics.
+ * For example, the result of the {@code pow} method using the
+ * {@linkplain #pow(int, MathContext) specified algorithm} can
+ * occasionally differ from the rounded mathematical result by more
+ * than one unit in the last place, one <i>{@linkplain #ulp() ulp}</i>.
+ *
+ * <p>Two types of operations are provided for manipulating the scale
+ * of a {@code BigDecimal}: scaling/rounding operations and decimal
+ * point motion operations.  Scaling/rounding operations ({@link
+ * #setScale setScale} and {@link #round round}) return a
+ * {@code BigDecimal} whose value is approximately (or exactly) equal
+ * to that of the operand, but whose scale or precision is the
+ * specified value; that is, they increase or decrease the precision
+ * of the stored number with minimal effect on its value.  Decimal
+ * point motion operations ({@link #movePointLeft movePointLeft} and
+ * {@link #movePointRight movePointRight}) return a
+ * {@code BigDecimal} created from the operand by moving the decimal
+ * point a specified distance in the specified direction.
+ *
+ * <p>For the sake of brevity and clarity, pseudo-code is used
+ * throughout the descriptions of {@code BigDecimal} methods.  The
+ * pseudo-code expression {@code (i + j)} is shorthand for "a
+ * {@code BigDecimal} whose value is that of the {@code BigDecimal}
+ * {@code i} added to that of the {@code BigDecimal}
+ * {@code j}." The pseudo-code expression {@code (i == j)} is
+ * shorthand for "{@code true} if and only if the
+ * {@code BigDecimal} {@code i} represents the same value as the
+ * {@code BigDecimal} {@code j}." Other pseudo-code expressions
+ * are interpreted similarly.  Square brackets are used to represent
+ * the particular {@code BigInteger} and scale pair defining a
+ * {@code BigDecimal} value; for example [19, 2] is the
+ * {@code BigDecimal} numerically equal to 0.19 having a scale of 2.
+ *
+ * <p>Note: care should be exercised if {@code BigDecimal} objects
+ * are used as keys in a {@link java.util.SortedMap SortedMap} or
+ * elements in a {@link java.util.SortedSet SortedSet} since
+ * {@code BigDecimal}'s <i>natural ordering</i> is <i>inconsistent
+ * with equals</i>.  See {@link Comparable}, {@link
+ * java.util.SortedMap} or {@link java.util.SortedSet} for more
+ * information.
+ *
+ * <p>All methods and constructors for this class throw
+ * {@code NullPointerException} when passed a {@code null} object
+ * reference for any input parameter.
+ *
+ * @see     BigInteger
+ * @see     MathContext
+ * @see     RoundingMode
+ * @see     java.util.SortedMap
+ * @see     java.util.SortedSet
+ * @author  Josh Bloch
+ * @author  Mike Cowlishaw
+ * @author  Joseph D. Darcy
+ * @author  Sergey V. Kuksenko
+ */
+public class BigDecimal extends Number implements Comparable<BigDecimal> {
+    /**
+     * The unscaled value of this BigDecimal, as returned by {@link
+     * #unscaledValue}.
+     *
+     * @serial
+     * @see #unscaledValue
+     */
+    private final BigInteger intVal;
+
+    /**
+     * The scale of this BigDecimal, as returned by {@link #scale}.
+     *
+     * @serial
+     * @see #scale
+     */
+    private final int scale;  // Note: this may have any value, so
+                              // calculations must be done in longs
+
+    /**
+     * The number of decimal digits in this BigDecimal, or 0 if the
+     * number of digits are not known (lookaside information).  If
+     * nonzero, the value is guaranteed correct.  Use the precision()
+     * method to obtain and set the value if it might be 0.  This
+     * field is mutable until set nonzero.
+     *
+     * @since  1.5
+     */
+    private transient int precision;
+
+    /**
+     * Used to store the canonical string representation, if computed.
+     */
+    private transient String stringCache;
+
+    /**
+     * Sentinel value for {@link #intCompact} indicating the
+     * significand information is only available from {@code intVal}.
+     */
+    static final long INFLATED = Long.MIN_VALUE;
+
+    private static final BigInteger INFLATED_BIGINT = BigInteger.valueOf(INFLATED);
+
+    /**
+     * If the absolute value of the significand of this BigDecimal is
+     * less than or equal to {@code Long.MAX_VALUE}, the value can be
+     * compactly stored in this field and used in computations.
+     */
+    private final transient long intCompact;
+
+    // All 18-digit base ten strings fit into a long; not all 19-digit
+    // strings will
+    private static final int MAX_COMPACT_DIGITS = 18;
+
+    /* Appease the serialization gods */
+    private static final long serialVersionUID = 6108874887143696463L;
+
+    private static final ThreadLocal<StringBuilderHelper>
+        threadLocalStringBuilderHelper = new ThreadLocal<StringBuilderHelper>() {
+        @Override
+        protected StringBuilderHelper initialValue() {
+            return new StringBuilderHelper();
+        }
+    };
+
+    // Cache of common small BigDecimal values.
+    private static final BigDecimal zeroThroughTen[] = {
+        new BigDecimal(BigInteger.ZERO,       0,  0, 1),
+        new BigDecimal(BigInteger.ONE,        1,  0, 1),
+        new BigDecimal(BigInteger.valueOf(2), 2,  0, 1),
+        new BigDecimal(BigInteger.valueOf(3), 3,  0, 1),
+        new BigDecimal(BigInteger.valueOf(4), 4,  0, 1),
+        new BigDecimal(BigInteger.valueOf(5), 5,  0, 1),
+        new BigDecimal(BigInteger.valueOf(6), 6,  0, 1),
+        new BigDecimal(BigInteger.valueOf(7), 7,  0, 1),
+        new BigDecimal(BigInteger.valueOf(8), 8,  0, 1),
+        new BigDecimal(BigInteger.valueOf(9), 9,  0, 1),
+        new BigDecimal(BigInteger.TEN,        10, 0, 2),
+    };
+
+    // Cache of zero scaled by 0 - 15
+    private static final BigDecimal[] ZERO_SCALED_BY = {
+        zeroThroughTen[0],
+        new BigDecimal(BigInteger.ZERO, 0, 1, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 2, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 3, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 4, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 5, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 6, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 7, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 8, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 9, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 10, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 11, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 12, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 13, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 14, 1),
+        new BigDecimal(BigInteger.ZERO, 0, 15, 1),
+    };
+
+    // Half of Long.MIN_VALUE & Long.MAX_VALUE.
+    private static final long HALF_LONG_MAX_VALUE = Long.MAX_VALUE / 2;
+    private static final long HALF_LONG_MIN_VALUE = Long.MIN_VALUE / 2;
+
+    // Constants
+    /**
+     * The value 0, with a scale of 0.
+     *
+     * @since  1.5
+     */
+    public static final BigDecimal ZERO =
+        zeroThroughTen[0];
+
+    /**
+     * The value 1, with a scale of 0.
+     *
+     * @since  1.5
+     */
+    public static final BigDecimal ONE =
+        zeroThroughTen[1];
+
+    /**
+     * The value 10, with a scale of 0.
+     *
+     * @since  1.5
+     */
+    public static final BigDecimal TEN =
+        zeroThroughTen[10];
+
+    // Constructors
+
+    /**
+     * Trusted package private constructor.
+     * Trusted simply means if val is INFLATED, intVal could not be null and
+     * if intVal is null, val could not be INFLATED.
+     */
+    BigDecimal(BigInteger intVal, long val, int scale, int prec) {
+        this.scale = scale;
+        this.precision = prec;
+        this.intCompact = val;
+        this.intVal = intVal;
+    }
+
+    /**
+     * Translates a character array representation of a
+     * {@code BigDecimal} into a {@code BigDecimal}, accepting the
+     * same sequence of characters as the {@link #BigDecimal(String)}
+     * constructor, while allowing a sub-array to be specified.
+     *
+     * <p>Note that if the sequence of characters is already available
+     * within a character array, using this constructor is faster than
+     * converting the {@code char} array to string and using the
+     * {@code BigDecimal(String)} constructor .
+     *
+     * @param  in {@code char} array that is the source of characters.
+     * @param  offset first character in the array to inspect.
+     * @param  len number of characters to consider.
+     * @throws NumberFormatException if {@code in} is not a valid
+     *         representation of a {@code BigDecimal} or the defined subarray
+     *         is not wholly within {@code in}.
+     * @since  1.5
+     */
+    public BigDecimal(char[] in, int offset, int len) {
+        this(in,offset,len,MathContext.UNLIMITED);
+    }
+
+    /**
+     * Translates a character array representation of a
+     * {@code BigDecimal} into a {@code BigDecimal}, accepting the
+     * same sequence of characters as the {@link #BigDecimal(String)}
+     * constructor, while allowing a sub-array to be specified and
+     * with rounding according to the context settings.
+     *
+     * <p>Note that if the sequence of characters is already available
+     * within a character array, using this constructor is faster than
+     * converting the {@code char} array to string and using the
+     * {@code BigDecimal(String)} constructor .
+     *
+     * @param  in {@code char} array that is the source of characters.
+     * @param  offset first character in the array to inspect.
+     * @param  len number of characters to consider..
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @throws NumberFormatException if {@code in} is not a valid
+     *         representation of a {@code BigDecimal} or the defined subarray
+     *         is not wholly within {@code in}.
+     * @since  1.5
+     */
+    public BigDecimal(char[] in, int offset, int len, MathContext mc) {
+        // protect against huge length, negative values, and integer overflow
+        if ((in.length | len | offset) < 0 || len > in.length - offset) {
+            throw new NumberFormatException
+                ("Bad offset or len arguments for char[] input.");
+        }
+
+        // This is the primary string to BigDecimal constructor; all
+        // incoming strings end up here; it uses explicit (inline)
+        // parsing for speed and generates at most one intermediate
+        // (temporary) object (a char[] array) for non-compact case.
+
+        // Use locals for all fields values until completion
+        int prec = 0;                 // record precision value
+        int scl = 0;                  // record scale value
+        long rs = 0;                  // the compact value in long
+        BigInteger rb = null;         // the inflated value in BigInteger
+        // use array bounds checking to handle too-long, len == 0,
+        // bad offset, etc.
+        try {
+            // handle the sign
+            boolean isneg = false;          // assume positive
+            if (in[offset] == '-') {
+                isneg = true;               // leading minus means negative
+                offset++;
+                len--;
+            } else if (in[offset] == '+') { // leading + allowed
+                offset++;
+                len--;
+            }
+
+            // should now be at numeric part of the significand
+            boolean dot = false;             // true when there is a '.'
+            long exp = 0;                    // exponent
+            char c;                          // current character
+            boolean isCompact = (len <= MAX_COMPACT_DIGITS);
+            // integer significand array & idx is the index to it. The array
+            // is ONLY used when we can't use a compact representation.
+            int idx = 0;
+            if (isCompact) {
+                // First compact case, we need not to preserve the character
+                // and we can just compute the value in place.
+                for (; len > 0; offset++, len--) {
+                    c = in[offset];
+                    if ((c == '0')) { // have zero
+                        if (prec == 0)
+                            prec = 1;
+                        else if (rs != 0) {
+                            rs *= 10;
+                            ++prec;
+                        } // else digit is a redundant leading zero
+                        if (dot)
+                            ++scl;
+                    } else if ((c >= '1' && c <= '9')) { // have digit
+                        int digit = c - '0';
+                        if (prec != 1 || rs != 0)
+                            ++prec; // prec unchanged if preceded by 0s
+                        rs = rs * 10 + digit;
+                        if (dot)
+                            ++scl;
+                    } else if (c == '.') {   // have dot
+                        // have dot
+                        if (dot) // two dots
+                            throw new NumberFormatException();
+                        dot = true;
+                    } else if (Character.isDigit(c)) { // slow path
+                        int digit = Character.digit(c, 10);
+                        if (digit == 0) {
+                            if (prec == 0)
+                                prec = 1;
+                            else if (rs != 0) {
+                                rs *= 10;
+                                ++prec;
+                            } // else digit is a redundant leading zero
+                        } else {
+                            if (prec != 1 || rs != 0)
+                                ++prec; // prec unchanged if preceded by 0s
+                            rs = rs * 10 + digit;
+                        }
+                        if (dot)
+                            ++scl;
+                    } else if ((c == 'e') || (c == 'E')) {
+                        exp = parseExp(in, offset, len);
+                        // Next test is required for backwards compatibility
+                        if ((int) exp != exp) // overflow
+                            throw new NumberFormatException();
+                        break; // [saves a test]
+                    } else {
+                        throw new NumberFormatException();
+                    }
+                }
+                if (prec == 0) // no digits found
+                    throw new NumberFormatException();
+                // Adjust scale if exp is not zero.
+                if (exp != 0) { // had significant exponent
+                    scl = adjustScale(scl, exp);
+                }
+                rs = isneg ? -rs : rs;
+                int mcp = mc.precision;
+                int drop = prec - mcp; // prec has range [1, MAX_INT], mcp has range [0, MAX_INT];
+                                       // therefore, this subtract cannot overflow
+                if (mcp > 0 && drop > 0) {  // do rounding
+                    while (drop > 0) {
+                        scl = checkScaleNonZero((long) scl - drop);
+                        rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                        prec = longDigitLength(rs);
+                        drop = prec - mcp;
+                    }
+                }
+            } else {
+                char coeff[] = new char[len];
+                for (; len > 0; offset++, len--) {
+                    c = in[offset];
+                    // have digit
+                    if ((c >= '0' && c <= '9') || Character.isDigit(c)) {
+                        // First compact case, we need not to preserve the character
+                        // and we can just compute the value in place.
+                        if (c == '0' || Character.digit(c, 10) == 0) {
+                            if (prec == 0) {
+                                coeff[idx] = c;
+                                prec = 1;
+                            } else if (idx != 0) {
+                                coeff[idx++] = c;
+                                ++prec;
+                            } // else c must be a redundant leading zero
+                        } else {
+                            if (prec != 1 || idx != 0)
+                                ++prec; // prec unchanged if preceded by 0s
+                            coeff[idx++] = c;
+                        }
+                        if (dot)
+                            ++scl;
+                        continue;
+                    }
+                    // have dot
+                    if (c == '.') {
+                        // have dot
+                        if (dot) // two dots
+                            throw new NumberFormatException();
+                        dot = true;
+                        continue;
+                    }
+                    // exponent expected
+                    if ((c != 'e') && (c != 'E'))
+                        throw new NumberFormatException();
+                    exp = parseExp(in, offset, len);
+                    // Next test is required for backwards compatibility
+                    if ((int) exp != exp) // overflow
+                        throw new NumberFormatException();
+                    break; // [saves a test]
+                }
+                // here when no characters left
+                if (prec == 0) // no digits found
+                    throw new NumberFormatException();
+                // Adjust scale if exp is not zero.
+                if (exp != 0) { // had significant exponent
+                    scl = adjustScale(scl, exp);
+                }
+                // Remove leading zeros from precision (digits count)
+                rb = new BigInteger(coeff, isneg ? -1 : 1, prec);
+                rs = compactValFor(rb);
+                int mcp = mc.precision;
+                if (mcp > 0 && (prec > mcp)) {
+                    if (rs == INFLATED) {
+                        int drop = prec - mcp;
+                        while (drop > 0) {
+                            scl = checkScaleNonZero((long) scl - drop);
+                            rb = divideAndRoundByTenPow(rb, drop, mc.roundingMode.oldMode);
+                            rs = compactValFor(rb);
+                            if (rs != INFLATED) {
+                                prec = longDigitLength(rs);
+                                break;
+                            }
+                            prec = bigDigitLength(rb);
+                            drop = prec - mcp;
+                        }
+                    }
+                    if (rs != INFLATED) {
+                        int drop = prec - mcp;
+                        while (drop > 0) {
+                            scl = checkScaleNonZero((long) scl - drop);
+                            rs = divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                            prec = longDigitLength(rs);
+                            drop = prec - mcp;
+                        }
+                        rb = null;
+                    }
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            throw new NumberFormatException();
+        } catch (NegativeArraySizeException e) {
+            throw new NumberFormatException();
+        }
+        this.scale = scl;
+        this.precision = prec;
+        this.intCompact = rs;
+        this.intVal = rb;
+    }
+
+    private int adjustScale(int scl, long exp) {
+        long adjustedScale = scl - exp;
+        if (adjustedScale > Integer.MAX_VALUE || adjustedScale < Integer.MIN_VALUE)
+            throw new NumberFormatException("Scale out of range.");
+        scl = (int) adjustedScale;
+        return scl;
+    }
+
+    /*
+     * parse exponent
+     */
+    private static long parseExp(char[] in, int offset, int len){
+        long exp = 0;
+        offset++;
+        char c = in[offset];
+        len--;
+        boolean negexp = (c == '-');
+        // optional sign
+        if (negexp || c == '+') {
+            offset++;
+            c = in[offset];
+            len--;
+        }
+        if (len <= 0) // no exponent digits
+            throw new NumberFormatException();
+        // skip leading zeros in the exponent
+        while (len > 10 && (c=='0' || (Character.digit(c, 10) == 0))) {
+            offset++;
+            c = in[offset];
+            len--;
+        }
+        if (len > 10) // too many nonzero exponent digits
+            throw new NumberFormatException();
+        // c now holds first digit of exponent
+        for (;; len--) {
+            int v;
+            if (c >= '0' && c <= '9') {
+                v = c - '0';
+            } else {
+                v = Character.digit(c, 10);
+                if (v < 0) // not a digit
+                    throw new NumberFormatException();
+            }
+            exp = exp * 10 + v;
+            if (len == 1)
+                break; // that was final character
+            offset++;
+            c = in[offset];
+        }
+        if (negexp) // apply sign
+            exp = -exp;
+        return exp;
+    }
+
+    /**
+     * Translates a character array representation of a
+     * {@code BigDecimal} into a {@code BigDecimal}, accepting the
+     * same sequence of characters as the {@link #BigDecimal(String)}
+     * constructor.
+     *
+     * <p>Note that if the sequence of characters is already available
+     * as a character array, using this constructor is faster than
+     * converting the {@code char} array to string and using the
+     * {@code BigDecimal(String)} constructor .
+     *
+     * @param in {@code char} array that is the source of characters.
+     * @throws NumberFormatException if {@code in} is not a valid
+     *         representation of a {@code BigDecimal}.
+     * @since  1.5
+     */
+    public BigDecimal(char[] in) {
+        this(in, 0, in.length);
+    }
+
+    /**
+     * Translates a character array representation of a
+     * {@code BigDecimal} into a {@code BigDecimal}, accepting the
+     * same sequence of characters as the {@link #BigDecimal(String)}
+     * constructor and with rounding according to the context
+     * settings.
+     *
+     * <p>Note that if the sequence of characters is already available
+     * as a character array, using this constructor is faster than
+     * converting the {@code char} array to string and using the
+     * {@code BigDecimal(String)} constructor .
+     *
+     * @param  in {@code char} array that is the source of characters.
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @throws NumberFormatException if {@code in} is not a valid
+     *         representation of a {@code BigDecimal}.
+     * @since  1.5
+     */
+    public BigDecimal(char[] in, MathContext mc) {
+        this(in, 0, in.length, mc);
+    }
+
+    /**
+     * Translates the string representation of a {@code BigDecimal}
+     * into a {@code BigDecimal}.  The string representation consists
+     * of an optional sign, {@code '+'} (<tt> '&#92;u002B'</tt>) or
+     * {@code '-'} (<tt>'&#92;u002D'</tt>), followed by a sequence of
+     * zero or more decimal digits ("the integer"), optionally
+     * followed by a fraction, optionally followed by an exponent.
+     *
+     * <p>The fraction consists of a decimal point followed by zero
+     * or more decimal digits.  The string must contain at least one
+     * digit in either the integer or the fraction.  The number formed
+     * by the sign, the integer and the fraction is referred to as the
+     * <i>significand</i>.
+     *
+     * <p>The exponent consists of the character {@code 'e'}
+     * (<tt>'&#92;u0065'</tt>) or {@code 'E'} (<tt>'&#92;u0045'</tt>)
+     * followed by one or more decimal digits.  The value of the
+     * exponent must lie between -{@link Integer#MAX_VALUE} ({@link
+     * Integer#MIN_VALUE}+1) and {@link Integer#MAX_VALUE}, inclusive.
+     *
+     * <p>More formally, the strings this constructor accepts are
+     * described by the following grammar:
+     * <blockquote>
+     * <dl>
+     * <dt><i>BigDecimalString:</i>
+     * <dd><i>Sign<sub>opt</sub> Significand Exponent<sub>opt</sub></i>
+     * <dt><i>Sign:</i>
+     * <dd>{@code +}
+     * <dd>{@code -}
+     * <dt><i>Significand:</i>
+     * <dd><i>IntegerPart</i> {@code .} <i>FractionPart<sub>opt</sub></i>
+     * <dd>{@code .} <i>FractionPart</i>
+     * <dd><i>IntegerPart</i>
+     * <dt><i>IntegerPart:</i>
+     * <dd><i>Digits</i>
+     * <dt><i>FractionPart:</i>
+     * <dd><i>Digits</i>
+     * <dt><i>Exponent:</i>
+     * <dd><i>ExponentIndicator SignedInteger</i>
+     * <dt><i>ExponentIndicator:</i>
+     * <dd>{@code e}
+     * <dd>{@code E}
+     * <dt><i>SignedInteger:</i>
+     * <dd><i>Sign<sub>opt</sub> Digits</i>
+     * <dt><i>Digits:</i>
+     * <dd><i>Digit</i>
+     * <dd><i>Digits Digit</i>
+     * <dt><i>Digit:</i>
+     * <dd>any character for which {@link Character#isDigit}
+     * returns {@code true}, including 0, 1, 2 ...
+     * </dl>
+     * </blockquote>
+     *
+     * <p>The scale of the returned {@code BigDecimal} will be the
+     * number of digits in the fraction, or zero if the string
+     * contains no decimal point, subject to adjustment for any
+     * exponent; if the string contains an exponent, the exponent is
+     * subtracted from the scale.  The value of the resulting scale
+     * must lie between {@code Integer.MIN_VALUE} and
+     * {@code Integer.MAX_VALUE}, inclusive.
+     *
+     * <p>The character-to-digit mapping is provided by {@link
+     * java.lang.Character#digit} set to convert to radix 10.  The
+     * String may not contain any extraneous characters (whitespace,
+     * for example).
+     *
+     * <p><b>Examples:</b><br>
+     * The value of the returned {@code BigDecimal} is equal to
+     * <i>significand</i> &times; 10<sup>&nbsp;<i>exponent</i></sup>.
+     * For each string on the left, the resulting representation
+     * [{@code BigInteger}, {@code scale}] is shown on the right.
+     * <pre>
+     * "0"            [0,0]
+     * "0.00"         [0,2]
+     * "123"          [123,0]
+     * "-123"         [-123,0]
+     * "1.23E3"       [123,-1]
+     * "1.23E+3"      [123,-1]
+     * "12.3E+7"      [123,-6]
+     * "12.0"         [120,1]
+     * "12.3"         [123,1]
+     * "0.00123"      [123,5]
+     * "-1.23E-12"    [-123,14]
+     * "1234.5E-4"    [12345,5]
+     * "0E+7"         [0,-7]
+     * "-0"           [0,0]
+     * </pre>
+     *
+     * <p>Note: For values other than {@code float} and
+     * {@code double} NaN and &plusmn;Infinity, this constructor is
+     * compatible with the values returned by {@link Float#toString}
+     * and {@link Double#toString}.  This is generally the preferred
+     * way to convert a {@code float} or {@code double} into a
+     * BigDecimal, as it doesn't suffer from the unpredictability of
+     * the {@link #BigDecimal(double)} constructor.
+     *
+     * @param val String representation of {@code BigDecimal}.
+     *
+     * @throws NumberFormatException if {@code val} is not a valid
+     *         representation of a {@code BigDecimal}.
+     */
+    public BigDecimal(String val) {
+        this(val.toCharArray(), 0, val.length());
+    }
+
+    /**
+     * Translates the string representation of a {@code BigDecimal}
+     * into a {@code BigDecimal}, accepting the same strings as the
+     * {@link #BigDecimal(String)} constructor, with rounding
+     * according to the context settings.
+     *
+     * @param  val string representation of a {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @throws NumberFormatException if {@code val} is not a valid
+     *         representation of a BigDecimal.
+     * @since  1.5
+     */
+    public BigDecimal(String val, MathContext mc) {
+        this(val.toCharArray(), 0, val.length(), mc);
+    }
+
+    /**
+     * Translates a {@code double} into a {@code BigDecimal} which
+     * is the exact decimal representation of the {@code double}'s
+     * binary floating-point value.  The scale of the returned
+     * {@code BigDecimal} is the smallest value such that
+     * <tt>(10<sup>scale</sup> &times; val)</tt> is an integer.
+     * <p>
+     * <b>Notes:</b>
+     * <ol>
+     * <li>
+     * The results of this constructor can be somewhat unpredictable.
+     * One might assume that writing {@code new BigDecimal(0.1)} in
+     * Java creates a {@code BigDecimal} which is exactly equal to
+     * 0.1 (an unscaled value of 1, with a scale of 1), but it is
+     * actually equal to
+     * 0.1000000000000000055511151231257827021181583404541015625.
+     * This is because 0.1 cannot be represented exactly as a
+     * {@code double} (or, for that matter, as a binary fraction of
+     * any finite length).  Thus, the value that is being passed
+     * <i>in</i> to the constructor is not exactly equal to 0.1,
+     * appearances notwithstanding.
+     *
+     * <li>
+     * The {@code String} constructor, on the other hand, is
+     * perfectly predictable: writing {@code new BigDecimal("0.1")}
+     * creates a {@code BigDecimal} which is <i>exactly</i> equal to
+     * 0.1, as one would expect.  Therefore, it is generally
+     * recommended that the {@linkplain #BigDecimal(String)
+     * <tt>String</tt> constructor} be used in preference to this one.
+     *
+     * <li>
+     * When a {@code double} must be used as a source for a
+     * {@code BigDecimal}, note that this constructor provides an
+     * exact conversion; it does not give the same result as
+     * converting the {@code double} to a {@code String} using the
+     * {@link Double#toString(double)} method and then using the
+     * {@link #BigDecimal(String)} constructor.  To get that result,
+     * use the {@code static} {@link #valueOf(double)} method.
+     * </ol>
+     *
+     * @param val {@code double} value to be converted to
+     *        {@code BigDecimal}.
+     * @throws NumberFormatException if {@code val} is infinite or NaN.
+     */
+    public BigDecimal(double val) {
+        this(val,MathContext.UNLIMITED);
+    }
+
+    /**
+     * Translates a {@code double} into a {@code BigDecimal}, with
+     * rounding according to the context settings.  The scale of the
+     * {@code BigDecimal} is the smallest value such that
+     * <tt>(10<sup>scale</sup> &times; val)</tt> is an integer.
+     *
+     * <p>The results of this constructor can be somewhat unpredictable
+     * and its use is generally not recommended; see the notes under
+     * the {@link #BigDecimal(double)} constructor.
+     *
+     * @param  val {@code double} value to be converted to
+     *         {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         RoundingMode is UNNECESSARY.
+     * @throws NumberFormatException if {@code val} is infinite or NaN.
+     * @since  1.5
+     */
+    public BigDecimal(double val, MathContext mc) {
+        if (Double.isInfinite(val) || Double.isNaN(val))
+            throw new NumberFormatException("Infinite or NaN");
+        // Translate the double into sign, exponent and significand, according
+        // to the formulae in JLS, Section 20.10.22.
+        long valBits = Double.doubleToLongBits(val);
+        int sign = ((valBits >> 63) == 0 ? 1 : -1);
+        int exponent = (int) ((valBits >> 52) & 0x7ffL);
+        long significand = (exponent == 0
+                ? (valBits & ((1L << 52) - 1)) << 1
+                : (valBits & ((1L << 52) - 1)) | (1L << 52));
+        exponent -= 1075;
+        // At this point, val == sign * significand * 2**exponent.
+
+        /*
+         * Special case zero to suppress nonterminating normalization and bogus
+         * scale calculation.
+         */
+        if (significand == 0) {
+            this.intVal = BigInteger.ZERO;
+            this.scale = 0;
+            this.intCompact = 0;
+            this.precision = 1;
+            return;
+        }
+        // Normalize
+        while ((significand & 1) == 0) { // i.e., significand is even
+            significand >>= 1;
+            exponent++;
+        }
+        int scale = 0;
+        // Calculate intVal and scale
+        BigInteger intVal;
+        long compactVal = sign * significand;
+        if (exponent == 0) {
+            intVal = (compactVal == INFLATED) ? INFLATED_BIGINT : null;
+        } else {
+            if (exponent < 0) {
+                intVal = BigInteger.valueOf(5).pow(-exponent).multiply(compactVal);
+                scale = -exponent;
+            } else { //  (exponent > 0)
+                intVal = BigInteger.valueOf(2).pow(exponent).multiply(compactVal);
+            }
+            compactVal = compactValFor(intVal);
+        }
+        int prec = 0;
+        int mcp = mc.precision;
+        if (mcp > 0) { // do rounding
+            int mode = mc.roundingMode.oldMode;
+            int drop;
+            if (compactVal == INFLATED) {
+                prec = bigDigitLength(intVal);
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    compactVal = compactValFor(intVal);
+                    if (compactVal != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                }
+                intVal = null;
+            }
+        }
+        this.intVal = intVal;
+        this.intCompact = compactVal;
+        this.scale = scale;
+        this.precision = prec;
+    }
+
+    /**
+     * Translates a {@code BigInteger} into a {@code BigDecimal}.
+     * The scale of the {@code BigDecimal} is zero.
+     *
+     * @param val {@code BigInteger} value to be converted to
+     *            {@code BigDecimal}.
+     */
+    public BigDecimal(BigInteger val) {
+        scale = 0;
+        intVal = val;
+        intCompact = compactValFor(val);
+    }
+
+    /**
+     * Translates a {@code BigInteger} into a {@code BigDecimal}
+     * rounding according to the context settings.  The scale of the
+     * {@code BigDecimal} is zero.
+     *
+     * @param val {@code BigInteger} value to be converted to
+     *            {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal(BigInteger val, MathContext mc) {
+        this(val,0,mc);
+    }
+
+    /**
+     * Translates a {@code BigInteger} unscaled value and an
+     * {@code int} scale into a {@code BigDecimal}.  The value of
+     * the {@code BigDecimal} is
+     * <tt>(unscaledVal &times; 10<sup>-scale</sup>)</tt>.
+     *
+     * @param unscaledVal unscaled value of the {@code BigDecimal}.
+     * @param scale scale of the {@code BigDecimal}.
+     */
+    public BigDecimal(BigInteger unscaledVal, int scale) {
+        // Negative scales are now allowed
+        this.intVal = unscaledVal;
+        this.intCompact = compactValFor(unscaledVal);
+        this.scale = scale;
+    }
+
+    /**
+     * Translates a {@code BigInteger} unscaled value and an
+     * {@code int} scale into a {@code BigDecimal}, with rounding
+     * according to the context settings.  The value of the
+     * {@code BigDecimal} is <tt>(unscaledVal &times;
+     * 10<sup>-scale</sup>)</tt>, rounded according to the
+     * {@code precision} and rounding mode settings.
+     *
+     * @param  unscaledVal unscaled value of the {@code BigDecimal}.
+     * @param  scale scale of the {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) {
+        long compactVal = compactValFor(unscaledVal);
+        int mcp = mc.precision;
+        int prec = 0;
+        if (mcp > 0) { // do rounding
+            int mode = mc.roundingMode.oldMode;
+            if (compactVal == INFLATED) {
+                prec = bigDigitLength(unscaledVal);
+                int drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    unscaledVal = divideAndRoundByTenPow(unscaledVal, drop, mode);
+                    compactVal = compactValFor(unscaledVal);
+                    if (compactVal != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(unscaledVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                prec = longDigitLength(compactVal);
+                int drop = prec - mcp;     // drop can't be more than 18
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mode);
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                }
+                unscaledVal = null;
+            }
+        }
+        this.intVal = unscaledVal;
+        this.intCompact = compactVal;
+        this.scale = scale;
+        this.precision = prec;
+    }
+
+    /**
+     * Translates an {@code int} into a {@code BigDecimal}.  The
+     * scale of the {@code BigDecimal} is zero.
+     *
+     * @param val {@code int} value to be converted to
+     *            {@code BigDecimal}.
+     * @since  1.5
+     */
+    public BigDecimal(int val) {
+        this.intCompact = val;
+        this.scale = 0;
+        this.intVal = null;
+    }
+
+    /**
+     * Translates an {@code int} into a {@code BigDecimal}, with
+     * rounding according to the context settings.  The scale of the
+     * {@code BigDecimal}, before any rounding, is zero.
+     *
+     * @param  val {@code int} value to be converted to {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal(int val, MathContext mc) {
+        int mcp = mc.precision;
+        long compactVal = val;
+        int scale = 0;
+        int prec = 0;
+        if (mcp > 0) { // do rounding
+            prec = longDigitLength(compactVal);
+            int drop = prec - mcp; // drop can't be more than 18
+            while (drop > 0) {
+                scale = checkScaleNonZero((long) scale - drop);
+                compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;
+            }
+        }
+        this.intVal = null;
+        this.intCompact = compactVal;
+        this.scale = scale;
+        this.precision = prec;
+    }
+
+    /**
+     * Translates a {@code long} into a {@code BigDecimal}.  The
+     * scale of the {@code BigDecimal} is zero.
+     *
+     * @param val {@code long} value to be converted to {@code BigDecimal}.
+     * @since  1.5
+     */
+    public BigDecimal(long val) {
+        this.intCompact = val;
+        this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
+        this.scale = 0;
+    }
+
+    /**
+     * Translates a {@code long} into a {@code BigDecimal}, with
+     * rounding according to the context settings.  The scale of the
+     * {@code BigDecimal}, before any rounding, is zero.
+     *
+     * @param  val {@code long} value to be converted to {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal(long val, MathContext mc) {
+        int mcp = mc.precision;
+        int mode = mc.roundingMode.oldMode;
+        int prec = 0;
+        int scale = 0;
+        BigInteger intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
+        if (mcp > 0) { // do rounding
+            if (val == INFLATED) {
+                prec = 19;
+                int drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    val = compactValFor(intVal);
+                    if (val != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (val != INFLATED) {
+                prec = longDigitLength(val);
+                int drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    val = divideAndRound(val, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    prec = longDigitLength(val);
+                    drop = prec - mcp;
+                }
+                intVal = null;
+            }
+        }
+        this.intVal = intVal;
+        this.intCompact = val;
+        this.scale = scale;
+        this.precision = prec;
+    }
+
+    // Static Factory Methods
+
+    /**
+     * Translates a {@code long} unscaled value and an
+     * {@code int} scale into a {@code BigDecimal}.  This
+     * {@literal "static factory method"} is provided in preference to
+     * a ({@code long}, {@code int}) constructor because it
+     * allows for reuse of frequently used {@code BigDecimal} values..
+     *
+     * @param unscaledVal unscaled value of the {@code BigDecimal}.
+     * @param scale scale of the {@code BigDecimal}.
+     * @return a {@code BigDecimal} whose value is
+     *         <tt>(unscaledVal &times; 10<sup>-scale</sup>)</tt>.
+     */
+    public static BigDecimal valueOf(long unscaledVal, int scale) {
+        if (scale == 0)
+            return valueOf(unscaledVal);
+        else if (unscaledVal == 0) {
+            return zeroValueOf(scale);
+        }
+        return new BigDecimal(unscaledVal == INFLATED ?
+                              INFLATED_BIGINT : null,
+                              unscaledVal, scale, 0);
+    }
+
+    /**
+     * Translates a {@code long} value into a {@code BigDecimal}
+     * with a scale of zero.  This {@literal "static factory method"}
+     * is provided in preference to a ({@code long}) constructor
+     * because it allows for reuse of frequently used
+     * {@code BigDecimal} values.
+     *
+     * @param val value of the {@code BigDecimal}.
+     * @return a {@code BigDecimal} whose value is {@code val}.
+     */
+    public static BigDecimal valueOf(long val) {
+        if (val >= 0 && val < zeroThroughTen.length)
+            return zeroThroughTen[(int)val];
+        else if (val != INFLATED)
+            return new BigDecimal(null, val, 0, 0);
+        return new BigDecimal(INFLATED_BIGINT, val, 0, 0);
+    }
+
+    static BigDecimal valueOf(long unscaledVal, int scale, int prec) {
+        if (scale == 0 && unscaledVal >= 0 && unscaledVal < zeroThroughTen.length) {
+            return zeroThroughTen[(int) unscaledVal];
+        } else if (unscaledVal == 0) {
+            return zeroValueOf(scale);
+        }
+        return new BigDecimal(unscaledVal == INFLATED ? INFLATED_BIGINT : null,
+                unscaledVal, scale, prec);
+    }
+
+    static BigDecimal valueOf(BigInteger intVal, int scale, int prec) {
+        long val = compactValFor(intVal);
+        if (val == 0) {
+            return zeroValueOf(scale);
+        } else if (scale == 0 && val >= 0 && val < zeroThroughTen.length) {
+            return zeroThroughTen[(int) val];
+        }
+        return new BigDecimal(intVal, val, scale, prec);
+    }
+
+    static BigDecimal zeroValueOf(int scale) {
+        if (scale >= 0 && scale < ZERO_SCALED_BY.length)
+            return ZERO_SCALED_BY[scale];
+        else
+            return new BigDecimal(BigInteger.ZERO, 0, scale, 1);
+    }
+
+    /**
+     * Translates a {@code double} into a {@code BigDecimal}, using
+     * the {@code double}'s canonical string representation provided
+     * by the {@link Double#toString(double)} method.
+     *
+     * <p><b>Note:</b> This is generally the preferred way to convert
+     * a {@code double} (or {@code float}) into a
+     * {@code BigDecimal}, as the value returned is equal to that
+     * resulting from constructing a {@code BigDecimal} from the
+     * result of using {@link Double#toString(double)}.
+     *
+     * @param  val {@code double} to convert to a {@code BigDecimal}.
+     * @return a {@code BigDecimal} whose value is equal to or approximately
+     *         equal to the value of {@code val}.
+     * @throws NumberFormatException if {@code val} is infinite or NaN.
+     * @since  1.5
+     */
+    public static BigDecimal valueOf(double val) {
+        // Reminder: a zero double returns '0.0', so we cannot fastpath
+        // to use the constant ZERO.  This might be important enough to
+        // justify a factory approach, a cache, or a few private
+        // constants, later.
+        return new BigDecimal(Double.toString(val));
+    }
+
+    // Arithmetic Operations
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this +
+     * augend)}, and whose scale is {@code max(this.scale(),
+     * augend.scale())}.
+     *
+     * @param  augend value to be added to this {@code BigDecimal}.
+     * @return {@code this + augend}
+     */
+    public BigDecimal add(BigDecimal augend) {
+        if (this.intCompact != INFLATED) {
+            if ((augend.intCompact != INFLATED)) {
+                return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
+            } else {
+                return add(this.intCompact, this.scale, augend.intVal, augend.scale);
+            }
+        } else {
+            if ((augend.intCompact != INFLATED)) {
+                return add(augend.intCompact, augend.scale, this.intVal, this.scale);
+            } else {
+                return add(this.intVal, this.scale, augend.intVal, augend.scale);
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this + augend)},
+     * with rounding according to the context settings.
+     *
+     * If either number is zero and the precision setting is nonzero then
+     * the other number, rounded if necessary, is used as the result.
+     *
+     * @param  augend value to be added to this {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @return {@code this + augend}, rounded as necessary.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal add(BigDecimal augend, MathContext mc) {
+        if (mc.precision == 0)
+            return add(augend);
+        BigDecimal lhs = this;
+
+        // If either number is zero then the other number, rounded and
+        // scaled if necessary, is used as the result.
+        {
+            boolean lhsIsZero = lhs.signum() == 0;
+            boolean augendIsZero = augend.signum() == 0;
+
+            if (lhsIsZero || augendIsZero) {
+                int preferredScale = Math.max(lhs.scale(), augend.scale());
+                BigDecimal result;
+
+                if (lhsIsZero && augendIsZero)
+                    return zeroValueOf(preferredScale);
+                result = lhsIsZero ? doRound(augend, mc) : doRound(lhs, mc);
+
+                if (result.scale() == preferredScale)
+                    return result;
+                else if (result.scale() > preferredScale) {
+                    return stripZerosToMatchScale(result.intVal, result.intCompact, result.scale, preferredScale);
+                } else { // result.scale < preferredScale
+                    int precisionDiff = mc.precision - result.precision();
+                    int scaleDiff     = preferredScale - result.scale();
+
+                    if (precisionDiff >= scaleDiff)
+                        return result.setScale(preferredScale); // can achieve target scale
+                    else
+                        return result.setScale(result.scale() + precisionDiff);
+                }
+            }
+        }
+
+        long padding = (long) lhs.scale - augend.scale;
+        if (padding != 0) { // scales differ; alignment needed
+            BigDecimal arg[] = preAlign(lhs, augend, padding, mc);
+            matchScale(arg);
+            lhs = arg[0];
+            augend = arg[1];
+        }
+        return doRound(lhs.inflated().add(augend.inflated()), lhs.scale, mc);
+    }
+
+    /**
+     * Returns an array of length two, the sum of whose entries is
+     * equal to the rounded sum of the {@code BigDecimal} arguments.
+     *
+     * <p>If the digit positions of the arguments have a sufficient
+     * gap between them, the value smaller in magnitude can be
+     * condensed into a {@literal "sticky bit"} and the end result will
+     * round the same way <em>if</em> the precision of the final
+     * result does not include the high order digit of the small
+     * magnitude operand.
+     *
+     * <p>Note that while strictly speaking this is an optimization,
+     * it makes a much wider range of additions practical.
+     *
+     * <p>This corresponds to a pre-shift operation in a fixed
+     * precision floating-point adder; this method is complicated by
+     * variable precision of the result as determined by the
+     * MathContext.  A more nuanced operation could implement a
+     * {@literal "right shift"} on the smaller magnitude operand so
+     * that the number of digits of the smaller operand could be
+     * reduced even though the significands partially overlapped.
+     */
+    private BigDecimal[] preAlign(BigDecimal lhs, BigDecimal augend, long padding, MathContext mc) {
+        assert padding != 0;
+        BigDecimal big;
+        BigDecimal small;
+
+        if (padding < 0) { // lhs is big; augend is small
+            big = lhs;
+            small = augend;
+        } else { // lhs is small; augend is big
+            big = augend;
+            small = lhs;
+        }
+
+        /*
+         * This is the estimated scale of an ulp of the result; it assumes that
+         * the result doesn't have a carry-out on a true add (e.g. 999 + 1 =>
+         * 1000) or any subtractive cancellation on borrowing (e.g. 100 - 1.2 =>
+         * 98.8)
+         */
+        long estResultUlpScale = (long) big.scale - big.precision() + mc.precision;
+
+        /*
+         * The low-order digit position of big is big.scale().  This
+         * is true regardless of whether big has a positive or
+         * negative scale.  The high-order digit position of small is
+         * small.scale - (small.precision() - 1).  To do the full
+         * condensation, the digit positions of big and small must be
+         * disjoint *and* the digit positions of small should not be
+         * directly visible in the result.
+         */
+        long smallHighDigitPos = (long) small.scale - small.precision() + 1;
+        if (smallHighDigitPos > big.scale + 2 && // big and small disjoint
+            smallHighDigitPos > estResultUlpScale + 2) { // small digits not visible
+            small = BigDecimal.valueOf(small.signum(), this.checkScale(Math.max(big.scale, estResultUlpScale) + 3));
+        }
+
+        // Since addition is symmetric, preserving input order in
+        // returned operands doesn't matter
+        BigDecimal[] result = {big, small};
+        return result;
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this -
+     * subtrahend)}, and whose scale is {@code max(this.scale(),
+     * subtrahend.scale())}.
+     *
+     * @param  subtrahend value to be subtracted from this {@code BigDecimal}.
+     * @return {@code this - subtrahend}
+     */
+    public BigDecimal subtract(BigDecimal subtrahend) {
+        if (this.intCompact != INFLATED) {
+            if ((subtrahend.intCompact != INFLATED)) {
+                return add(this.intCompact, this.scale, -subtrahend.intCompact, subtrahend.scale);
+            } else {
+                return add(this.intCompact, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
+            }
+        } else {
+            if ((subtrahend.intCompact != INFLATED)) {
+                // Pair of subtrahend values given before pair of
+                // values from this BigDecimal to avoid need for
+                // method overloading on the specialized add method
+                return add(-subtrahend.intCompact, subtrahend.scale, this.intVal, this.scale);
+            } else {
+                return add(this.intVal, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this - subtrahend)},
+     * with rounding according to the context settings.
+     *
+     * If {@code subtrahend} is zero then this, rounded if necessary, is used as the
+     * result.  If this is zero then the result is {@code subtrahend.negate(mc)}.
+     *
+     * @param  subtrahend value to be subtracted from this {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @return {@code this - subtrahend}, rounded as necessary.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) {
+        if (mc.precision == 0)
+            return subtract(subtrahend);
+        // share the special rounding code in add()
+        return add(subtrahend.negate(), mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is <tt>(this &times;
+     * multiplicand)</tt>, and whose scale is {@code (this.scale() +
+     * multiplicand.scale())}.
+     *
+     * @param  multiplicand value to be multiplied by this {@code BigDecimal}.
+     * @return {@code this * multiplicand}
+     */
+    public BigDecimal multiply(BigDecimal multiplicand) {
+        int productScale = checkScale((long) scale + multiplicand.scale);
+        if (this.intCompact != INFLATED) {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiply(this.intCompact, multiplicand.intCompact, productScale);
+            } else {
+                return multiply(this.intCompact, multiplicand.intVal, productScale);
+            }
+        } else {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiply(multiplicand.intCompact, this.intVal, productScale);
+            } else {
+                return multiply(this.intVal, multiplicand.intVal, productScale);
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is <tt>(this &times;
+     * multiplicand)</tt>, with rounding according to the context settings.
+     *
+     * @param  multiplicand value to be multiplied by this {@code BigDecimal}.
+     * @param  mc the context to use.
+     * @return {@code this * multiplicand}, rounded as necessary.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal multiply(BigDecimal multiplicand, MathContext mc) {
+        if (mc.precision == 0)
+            return multiply(multiplicand);
+        int productScale = checkScale((long) scale + multiplicand.scale);
+        if (this.intCompact != INFLATED) {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiplyAndRound(this.intCompact, multiplicand.intCompact, productScale, mc);
+            } else {
+                return multiplyAndRound(this.intCompact, multiplicand.intVal, productScale, mc);
+            }
+        } else {
+            if ((multiplicand.intCompact != INFLATED)) {
+                return multiplyAndRound(multiplicand.intCompact, this.intVal, productScale, mc);
+            } else {
+                return multiplyAndRound(this.intVal, multiplicand.intVal, productScale, mc);
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this /
+     * divisor)}, and whose scale is as specified.  If rounding must
+     * be performed to generate a result with the specified scale, the
+     * specified rounding mode is applied.
+     *
+     * <p>The new {@link #divide(BigDecimal, int, RoundingMode)} method
+     * should be used in preference to this legacy method.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @param  scale scale of the {@code BigDecimal} quotient to be returned.
+     * @param  roundingMode rounding mode to apply.
+     * @return {@code this / divisor}
+     * @throws ArithmeticException if {@code divisor} is zero,
+     *         {@code roundingMode==ROUND_UNNECESSARY} and
+     *         the specified scale is insufficient to represent the result
+     *         of the division exactly.
+     * @throws IllegalArgumentException if {@code roundingMode} does not
+     *         represent a valid rounding mode.
+     * @see    #ROUND_UP
+     * @see    #ROUND_DOWN
+     * @see    #ROUND_CEILING
+     * @see    #ROUND_FLOOR
+     * @see    #ROUND_HALF_UP
+     * @see    #ROUND_HALF_DOWN
+     * @see    #ROUND_HALF_EVEN
+     * @see    #ROUND_UNNECESSARY
+     */
+    public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
+        if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
+            throw new IllegalArgumentException("Invalid rounding mode");
+        if (this.intCompact != INFLATED) {
+            if ((divisor.intCompact != INFLATED)) {
+                return divide(this.intCompact, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
+            } else {
+                return divide(this.intCompact, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
+            }
+        } else {
+            if ((divisor.intCompact != INFLATED)) {
+                return divide(this.intVal, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
+            } else {
+                return divide(this.intVal, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this /
+     * divisor)}, and whose scale is as specified.  If rounding must
+     * be performed to generate a result with the specified scale, the
+     * specified rounding mode is applied.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @param  scale scale of the {@code BigDecimal} quotient to be returned.
+     * @param  roundingMode rounding mode to apply.
+     * @return {@code this / divisor}
+     * @throws ArithmeticException if {@code divisor} is zero,
+     *         {@code roundingMode==RoundingMode.UNNECESSARY} and
+     *         the specified scale is insufficient to represent the result
+     *         of the division exactly.
+     * @since 1.5
+     */
+    public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
+        return divide(divisor, scale, roundingMode.oldMode);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this /
+     * divisor)}, and whose scale is {@code this.scale()}.  If
+     * rounding must be performed to generate a result with the given
+     * scale, the specified rounding mode is applied.
+     *
+     * <p>The new {@link #divide(BigDecimal, RoundingMode)} method
+     * should be used in preference to this legacy method.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @param  roundingMode rounding mode to apply.
+     * @return {@code this / divisor}
+     * @throws ArithmeticException if {@code divisor==0}, or
+     *         {@code roundingMode==ROUND_UNNECESSARY} and
+     *         {@code this.scale()} is insufficient to represent the result
+     *         of the division exactly.
+     * @throws IllegalArgumentException if {@code roundingMode} does not
+     *         represent a valid rounding mode.
+     * @see    #ROUND_UP
+     * @see    #ROUND_DOWN
+     * @see    #ROUND_CEILING
+     * @see    #ROUND_FLOOR
+     * @see    #ROUND_HALF_UP
+     * @see    #ROUND_HALF_DOWN
+     * @see    #ROUND_HALF_EVEN
+     * @see    #ROUND_UNNECESSARY
+     */
+    public BigDecimal divide(BigDecimal divisor, int roundingMode) {
+        return this.divide(divisor, scale, roundingMode);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this /
+     * divisor)}, and whose scale is {@code this.scale()}.  If
+     * rounding must be performed to generate a result with the given
+     * scale, the specified rounding mode is applied.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @param  roundingMode rounding mode to apply.
+     * @return {@code this / divisor}
+     * @throws ArithmeticException if {@code divisor==0}, or
+     *         {@code roundingMode==RoundingMode.UNNECESSARY} and
+     *         {@code this.scale()} is insufficient to represent the result
+     *         of the division exactly.
+     * @since 1.5
+     */
+    public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) {
+        return this.divide(divisor, scale, roundingMode.oldMode);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this /
+     * divisor)}, and whose preferred scale is {@code (this.scale() -
+     * divisor.scale())}; if the exact quotient cannot be
+     * represented (because it has a non-terminating decimal
+     * expansion) an {@code ArithmeticException} is thrown.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @throws ArithmeticException if the exact quotient does not have a
+     *         terminating decimal expansion
+     * @return {@code this / divisor}
+     * @since 1.5
+     * @author Joseph D. Darcy
+     */
+    public BigDecimal divide(BigDecimal divisor) {
+        /*
+         * Handle zero cases first.
+         */
+        if (divisor.signum() == 0) {   // x/0
+            if (this.signum() == 0)    // 0/0
+                throw new ArithmeticException("Division undefined");  // NaN
+            throw new ArithmeticException("Division by zero");
+        }
+
+        // Calculate preferred scale
+        int preferredScale = saturateLong((long) this.scale - divisor.scale);
+
+        if (this.signum() == 0) // 0/y
+            return zeroValueOf(preferredScale);
+        else {
+            /*
+             * If the quotient this/divisor has a terminating decimal
+             * expansion, the expansion can have no more than
+             * (a.precision() + ceil(10*b.precision)/3) digits.
+             * Therefore, create a MathContext object with this
+             * precision and do a divide with the UNNECESSARY rounding
+             * mode.
+             */
+            MathContext mc = new MathContext( (int)Math.min(this.precision() +
+                                                            (long)Math.ceil(10.0*divisor.precision()/3.0),
+                                                            Integer.MAX_VALUE),
+                                              RoundingMode.UNNECESSARY);
+            BigDecimal quotient;
+            try {
+                quotient = this.divide(divisor, mc);
+            } catch (ArithmeticException e) {
+                throw new ArithmeticException("Non-terminating decimal expansion; " +
+                                              "no exact representable decimal result.");
+            }
+
+            int quotientScale = quotient.scale();
+
+            // divide(BigDecimal, mc) tries to adjust the quotient to
+            // the desired one by removing trailing zeros; since the
+            // exact divide method does not have an explicit digit
+            // limit, we can add zeros too.
+            if (preferredScale > quotientScale)
+                return quotient.setScale(preferredScale, ROUND_UNNECESSARY);
+
+            return quotient;
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this /
+     * divisor)}, with rounding according to the context settings.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @param  mc the context to use.
+     * @return {@code this / divisor}, rounded as necessary.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY} or
+     *         {@code mc.precision == 0} and the quotient has a
+     *         non-terminating decimal expansion.
+     * @since  1.5
+     */
+    public BigDecimal divide(BigDecimal divisor, MathContext mc) {
+        int mcp = mc.precision;
+        if (mcp == 0)
+            return divide(divisor);
+
+        BigDecimal dividend = this;
+        long preferredScale = (long)dividend.scale - divisor.scale;
+        // Now calculate the answer.  We use the existing
+        // divide-and-round method, but as this rounds to scale we have
+        // to normalize the values here to achieve the desired result.
+        // For x/y we first handle y=0 and x=0, and then normalize x and
+        // y to give x' and y' with the following constraints:
+        //   (a) 0.1 <= x' < 1
+        //   (b)  x' <= y' < 10*x'
+        // Dividing x'/y' with the required scale set to mc.precision then
+        // will give a result in the range 0.1 to 1 rounded to exactly
+        // the right number of digits (except in the case of a result of
+        // 1.000... which can arise when x=y, or when rounding overflows
+        // The 1.000... case will reduce properly to 1.
+        if (divisor.signum() == 0) {      // x/0
+            if (dividend.signum() == 0)    // 0/0
+                throw new ArithmeticException("Division undefined");  // NaN
+            throw new ArithmeticException("Division by zero");
+        }
+        if (dividend.signum() == 0) // 0/y
+            return zeroValueOf(saturateLong(preferredScale));
+        int xscale = dividend.precision();
+        int yscale = divisor.precision();
+        if(dividend.intCompact!=INFLATED) {
+            if(divisor.intCompact!=INFLATED) {
+                return divide(dividend.intCompact, xscale, divisor.intCompact, yscale, preferredScale, mc);
+            } else {
+                return divide(dividend.intCompact, xscale, divisor.intVal, yscale, preferredScale, mc);
+            }
+        } else {
+            if(divisor.intCompact!=INFLATED) {
+                return divide(dividend.intVal, xscale, divisor.intCompact, yscale, preferredScale, mc);
+            } else {
+                return divide(dividend.intVal, xscale, divisor.intVal, yscale, preferredScale, mc);
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is the integer part
+     * of the quotient {@code (this / divisor)} rounded down.  The
+     * preferred scale of the result is {@code (this.scale() -
+     * divisor.scale())}.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @return The integer part of {@code this / divisor}.
+     * @throws ArithmeticException if {@code divisor==0}
+     * @since  1.5
+     */
+    public BigDecimal divideToIntegralValue(BigDecimal divisor) {
+        // Calculate preferred scale
+        int preferredScale = saturateLong((long) this.scale - divisor.scale);
+        if (this.compareMagnitude(divisor) < 0) {
+            // much faster when this << divisor
+            return zeroValueOf(preferredScale);
+        }
+
+        if (this.signum() == 0 && divisor.signum() != 0)
+            return this.setScale(preferredScale, ROUND_UNNECESSARY);
+
+        // Perform a divide with enough digits to round to a correct
+        // integer value; then remove any fractional digits
+
+        int maxDigits = (int)Math.min(this.precision() +
+                                      (long)Math.ceil(10.0*divisor.precision()/3.0) +
+                                      Math.abs((long)this.scale() - divisor.scale()) + 2,
+                                      Integer.MAX_VALUE);
+        BigDecimal quotient = this.divide(divisor, new MathContext(maxDigits,
+                                                                   RoundingMode.DOWN));
+        if (quotient.scale > 0) {
+            quotient = quotient.setScale(0, RoundingMode.DOWN);
+            quotient = stripZerosToMatchScale(quotient.intVal, quotient.intCompact, quotient.scale, preferredScale);
+        }
+
+        if (quotient.scale < preferredScale) {
+            // pad with zeros if necessary
+            quotient = quotient.setScale(preferredScale, ROUND_UNNECESSARY);
+        }
+
+        return quotient;
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is the integer part
+     * of {@code (this / divisor)}.  Since the integer part of the
+     * exact quotient does not depend on the rounding mode, the
+     * rounding mode does not affect the values returned by this
+     * method.  The preferred scale of the result is
+     * {@code (this.scale() - divisor.scale())}.  An
+     * {@code ArithmeticException} is thrown if the integer part of
+     * the exact quotient needs more than {@code mc.precision}
+     * digits.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @param  mc the context to use.
+     * @return The integer part of {@code this / divisor}.
+     * @throws ArithmeticException if {@code divisor==0}
+     * @throws ArithmeticException if {@code mc.precision} {@literal >} 0 and the result
+     *         requires a precision of more than {@code mc.precision} digits.
+     * @since  1.5
+     * @author Joseph D. Darcy
+     */
+    public BigDecimal divideToIntegralValue(BigDecimal divisor, MathContext mc) {
+        if (mc.precision == 0 || // exact result
+            (this.compareMagnitude(divisor) < 0)) // zero result
+            return divideToIntegralValue(divisor);
+
+        // Calculate preferred scale
+        int preferredScale = saturateLong((long)this.scale - divisor.scale);
+
+        /*
+         * Perform a normal divide to mc.precision digits.  If the
+         * remainder has absolute value less than the divisor, the
+         * integer portion of the quotient fits into mc.precision
+         * digits.  Next, remove any fractional digits from the
+         * quotient and adjust the scale to the preferred value.
+         */
+        BigDecimal result = this.divide(divisor, new MathContext(mc.precision, RoundingMode.DOWN));
+
+        if (result.scale() < 0) {
+            /*
+             * Result is an integer. See if quotient represents the
+             * full integer portion of the exact quotient; if it does,
+             * the computed remainder will be less than the divisor.
+             */
+            BigDecimal product = result.multiply(divisor);
+            // If the quotient is the full integer value,
+            // |dividend-product| < |divisor|.
+            if (this.subtract(product).compareMagnitude(divisor) >= 0) {
+                throw new ArithmeticException("Division impossible");
+            }
+        } else if (result.scale() > 0) {
+            /*
+             * Integer portion of quotient will fit into precision
+             * digits; recompute quotient to scale 0 to avoid double
+             * rounding and then try to adjust, if necessary.
+             */
+            result = result.setScale(0, RoundingMode.DOWN);
+        }
+        // else result.scale() == 0;
+
+        int precisionDiff;
+        if ((preferredScale > result.scale()) &&
+            (precisionDiff = mc.precision - result.precision()) > 0) {
+            return result.setScale(result.scale() +
+                                   Math.min(precisionDiff, preferredScale - result.scale) );
+        } else {
+            return stripZerosToMatchScale(result.intVal,result.intCompact,result.scale,preferredScale);
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this % divisor)}.
+     *
+     * <p>The remainder is given by
+     * {@code this.subtract(this.divideToIntegralValue(divisor).multiply(divisor))}.
+     * Note that this is not the modulo operation (the result can be
+     * negative).
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @return {@code this % divisor}.
+     * @throws ArithmeticException if {@code divisor==0}
+     * @since  1.5
+     */
+    public BigDecimal remainder(BigDecimal divisor) {
+        BigDecimal divrem[] = this.divideAndRemainder(divisor);
+        return divrem[1];
+    }
+
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (this %
+     * divisor)}, with rounding according to the context settings.
+     * The {@code MathContext} settings affect the implicit divide
+     * used to compute the remainder.  The remainder computation
+     * itself is by definition exact.  Therefore, the remainder may
+     * contain more than {@code mc.getPrecision()} digits.
+     *
+     * <p>The remainder is given by
+     * {@code this.subtract(this.divideToIntegralValue(divisor,
+     * mc).multiply(divisor))}.  Note that this is not the modulo
+     * operation (the result can be negative).
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided.
+     * @param  mc the context to use.
+     * @return {@code this % divisor}, rounded as necessary.
+     * @throws ArithmeticException if {@code divisor==0}
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}, or {@code mc.precision}
+     *         {@literal >} 0 and the result of {@code this.divideToIntgralValue(divisor)} would
+     *         require a precision of more than {@code mc.precision} digits.
+     * @see    #divideToIntegralValue(java.math.BigDecimal, java.math.MathContext)
+     * @since  1.5
+     */
+    public BigDecimal remainder(BigDecimal divisor, MathContext mc) {
+        BigDecimal divrem[] = this.divideAndRemainder(divisor, mc);
+        return divrem[1];
+    }
+
+    /**
+     * Returns a two-element {@code BigDecimal} array containing the
+     * result of {@code divideToIntegralValue} followed by the result of
+     * {@code remainder} on the two operands.
+     *
+     * <p>Note that if both the integer quotient and remainder are
+     * needed, this method is faster than using the
+     * {@code divideToIntegralValue} and {@code remainder} methods
+     * separately because the division need only be carried out once.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided,
+     *         and the remainder computed.
+     * @return a two element {@code BigDecimal} array: the quotient
+     *         (the result of {@code divideToIntegralValue}) is the initial element
+     *         and the remainder is the final element.
+     * @throws ArithmeticException if {@code divisor==0}
+     * @see    #divideToIntegralValue(java.math.BigDecimal, java.math.MathContext)
+     * @see    #remainder(java.math.BigDecimal, java.math.MathContext)
+     * @since  1.5
+     */
+    public BigDecimal[] divideAndRemainder(BigDecimal divisor) {
+        // we use the identity  x = i * y + r to determine r
+        BigDecimal[] result = new BigDecimal[2];
+
+        result[0] = this.divideToIntegralValue(divisor);
+        result[1] = this.subtract(result[0].multiply(divisor));
+        return result;
+    }
+
+    /**
+     * Returns a two-element {@code BigDecimal} array containing the
+     * result of {@code divideToIntegralValue} followed by the result of
+     * {@code remainder} on the two operands calculated with rounding
+     * according to the context settings.
+     *
+     * <p>Note that if both the integer quotient and remainder are
+     * needed, this method is faster than using the
+     * {@code divideToIntegralValue} and {@code remainder} methods
+     * separately because the division need only be carried out once.
+     *
+     * @param  divisor value by which this {@code BigDecimal} is to be divided,
+     *         and the remainder computed.
+     * @param  mc the context to use.
+     * @return a two element {@code BigDecimal} array: the quotient
+     *         (the result of {@code divideToIntegralValue}) is the
+     *         initial element and the remainder is the final element.
+     * @throws ArithmeticException if {@code divisor==0}
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}, or {@code mc.precision}
+     *         {@literal >} 0 and the result of {@code this.divideToIntgralValue(divisor)} would
+     *         require a precision of more than {@code mc.precision} digits.
+     * @see    #divideToIntegralValue(java.math.BigDecimal, java.math.MathContext)
+     * @see    #remainder(java.math.BigDecimal, java.math.MathContext)
+     * @since  1.5
+     */
+    public BigDecimal[] divideAndRemainder(BigDecimal divisor, MathContext mc) {
+        if (mc.precision == 0)
+            return divideAndRemainder(divisor);
+
+        BigDecimal[] result = new BigDecimal[2];
+        BigDecimal lhs = this;
+
+        result[0] = lhs.divideToIntegralValue(divisor, mc);
+        result[1] = lhs.subtract(result[0].multiply(divisor));
+        return result;
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is
+     * <tt>(this<sup>n</sup>)</tt>, The power is computed exactly, to
+     * unlimited precision.
+     *
+     * <p>The parameter {@code n} must be in the range 0 through
+     * 999999999, inclusive.  {@code ZERO.pow(0)} returns {@link
+     * #ONE}.
+     *
+     * Note that future releases may expand the allowable exponent
+     * range of this method.
+     *
+     * @param  n power to raise this {@code BigDecimal} to.
+     * @return <tt>this<sup>n</sup></tt>
+     * @throws ArithmeticException if {@code n} is out of range.
+     * @since  1.5
+     */
+    public BigDecimal pow(int n) {
+        if (n < 0 || n > 999999999)
+            throw new ArithmeticException("Invalid operation");
+        // No need to calculate pow(n) if result will over/underflow.
+        // Don't attempt to support "supernormal" numbers.
+        int newScale = checkScale((long)scale * n);
+        return new BigDecimal(this.inflated().pow(n), newScale);
+    }
+
+
+    /**
+     * Returns a {@code BigDecimal} whose value is
+     * <tt>(this<sup>n</sup>)</tt>.  The current implementation uses
+     * the core algorithm defined in ANSI standard X3.274-1996 with
+     * rounding according to the context settings.  In general, the
+     * returned numerical value is within two ulps of the exact
+     * numerical value for the chosen precision.  Note that future
+     * releases may use a different algorithm with a decreased
+     * allowable error bound and increased allowable exponent range.
+     *
+     * <p>The X3.274-1996 algorithm is:
+     *
+     * <ul>
+     * <li> An {@code ArithmeticException} exception is thrown if
+     *  <ul>
+     *    <li>{@code abs(n) > 999999999}
+     *    <li>{@code mc.precision == 0} and {@code n < 0}
+     *    <li>{@code mc.precision > 0} and {@code n} has more than
+     *    {@code mc.precision} decimal digits
+     *  </ul>
+     *
+     * <li> if {@code n} is zero, {@link #ONE} is returned even if
+     * {@code this} is zero, otherwise
+     * <ul>
+     *   <li> if {@code n} is positive, the result is calculated via
+     *   the repeated squaring technique into a single accumulator.
+     *   The individual multiplications with the accumulator use the
+     *   same math context settings as in {@code mc} except for a
+     *   precision increased to {@code mc.precision + elength + 1}
+     *   where {@code elength} is the number of decimal digits in
+     *   {@code n}.
+     *
+     *   <li> if {@code n} is negative, the result is calculated as if
+     *   {@code n} were positive; this value is then divided into one
+     *   using the working precision specified above.
+     *
+     *   <li> The final value from either the positive or negative case
+     *   is then rounded to the destination precision.
+     *   </ul>
+     * </ul>
+     *
+     * @param  n power to raise this {@code BigDecimal} to.
+     * @param  mc the context to use.
+     * @return <tt>this<sup>n</sup></tt> using the ANSI standard X3.274-1996
+     *         algorithm
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}, or {@code n} is out
+     *         of range.
+     * @since  1.5
+     */
+    public BigDecimal pow(int n, MathContext mc) {
+        if (mc.precision == 0)
+            return pow(n);
+        if (n < -999999999 || n > 999999999)
+            throw new ArithmeticException("Invalid operation");
+        if (n == 0)
+            return ONE;                      // x**0 == 1 in X3.274
+        BigDecimal lhs = this;
+        MathContext workmc = mc;           // working settings
+        int mag = Math.abs(n);               // magnitude of n
+        if (mc.precision > 0) {
+            int elength = longDigitLength(mag); // length of n in digits
+            if (elength > mc.precision)        // X3.274 rule
+                throw new ArithmeticException("Invalid operation");
+            workmc = new MathContext(mc.precision + elength + 1,
+                                      mc.roundingMode);
+        }
+        // ready to carry out power calculation...
+        BigDecimal acc = ONE;           // accumulator
+        boolean seenbit = false;        // set once we've seen a 1-bit
+        for (int i=1;;i++) {            // for each bit [top bit ignored]
+            mag += mag;                 // shift left 1 bit
+            if (mag < 0) {              // top bit is set
+                seenbit = true;         // OK, we're off
+                acc = acc.multiply(lhs, workmc); // acc=acc*x
+            }
+            if (i == 31)
+                break;                  // that was the last bit
+            if (seenbit)
+                acc=acc.multiply(acc, workmc);   // acc=acc*acc [square]
+                // else (!seenbit) no point in squaring ONE
+        }
+        // if negative n, calculate the reciprocal using working precision
+        if (n < 0) // [hence mc.precision>0]
+            acc=ONE.divide(acc, workmc);
+        // round to final precision and strip zeros
+        return doRound(acc, mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is the absolute value
+     * of this {@code BigDecimal}, and whose scale is
+     * {@code this.scale()}.
+     *
+     * @return {@code abs(this)}
+     */
+    public BigDecimal abs() {
+        return (signum() < 0 ? negate() : this);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is the absolute value
+     * of this {@code BigDecimal}, with rounding according to the
+     * context settings.
+     *
+     * @param mc the context to use.
+     * @return {@code abs(this)}, rounded as necessary.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since 1.5
+     */
+    public BigDecimal abs(MathContext mc) {
+        return (signum() < 0 ? negate(mc) : plus(mc));
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (-this)},
+     * and whose scale is {@code this.scale()}.
+     *
+     * @return {@code -this}.
+     */
+    public BigDecimal negate() {
+        if (intCompact == INFLATED) {
+            return new BigDecimal(intVal.negate(), INFLATED, scale, precision);
+        } else {
+            return valueOf(-intCompact, scale, precision);
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (-this)},
+     * with rounding according to the context settings.
+     *
+     * @param mc the context to use.
+     * @return {@code -this}, rounded as necessary.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @since  1.5
+     */
+    public BigDecimal negate(MathContext mc) {
+        return negate().plus(mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (+this)}, and whose
+     * scale is {@code this.scale()}.
+     *
+     * <p>This method, which simply returns this {@code BigDecimal}
+     * is included for symmetry with the unary minus method {@link
+     * #negate()}.
+     *
+     * @return {@code this}.
+     * @see #negate()
+     * @since  1.5
+     */
+    public BigDecimal plus() {
+        return this;
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (+this)},
+     * with rounding according to the context settings.
+     *
+     * <p>The effect of this method is identical to that of the {@link
+     * #round(MathContext)} method.
+     *
+     * @param mc the context to use.
+     * @return {@code this}, rounded as necessary.  A zero result will
+     *         have a scale of 0.
+     * @throws ArithmeticException if the result is inexact but the
+     *         rounding mode is {@code UNNECESSARY}.
+     * @see    #round(MathContext)
+     * @since  1.5
+     */
+    public BigDecimal plus(MathContext mc) {
+        if (mc.precision == 0)                 // no rounding please
+            return this;
+        return doRound(this, mc);
+    }
+
+    /**
+     * Returns the signum function of this {@code BigDecimal}.
+     *
+     * @return -1, 0, or 1 as the value of this {@code BigDecimal}
+     *         is negative, zero, or positive.
+     */
+    public int signum() {
+        return (intCompact != INFLATED)?
+            Long.signum(intCompact):
+            intVal.signum();
+    }
+
+    /**
+     * Returns the <i>scale</i> of this {@code BigDecimal}.  If zero
+     * or positive, the scale is the number of digits to the right of
+     * the decimal point.  If negative, the unscaled value of the
+     * number is multiplied by ten to the power of the negation of the
+     * scale.  For example, a scale of {@code -3} means the unscaled
+     * value is multiplied by 1000.
+     *
+     * @return the scale of this {@code BigDecimal}.
+     */
+    public int scale() {
+        return scale;
+    }
+
+    /**
+     * Returns the <i>precision</i> of this {@code BigDecimal}.  (The
+     * precision is the number of digits in the unscaled value.)
+     *
+     * <p>The precision of a zero value is 1.
+     *
+     * @return the precision of this {@code BigDecimal}.
+     * @since  1.5
+     */
+    public int precision() {
+        int result = precision;
+        if (result == 0) {
+            long s = intCompact;
+            if (s != INFLATED)
+                result = longDigitLength(s);
+            else
+                result = bigDigitLength(intVal);
+            precision = result;
+        }
+        return result;
+    }
+
+
+    /**
+     * Returns a {@code BigInteger} whose value is the <i>unscaled
+     * value</i> of this {@code BigDecimal}.  (Computes <tt>(this *
+     * 10<sup>this.scale()</sup>)</tt>.)
+     *
+     * @return the unscaled value of this {@code BigDecimal}.
+     * @since  1.2
+     */
+    public BigInteger unscaledValue() {
+        return this.inflated();
+    }
+
+    // Rounding Modes
+
+    /**
+     * Rounding mode to round away from zero.  Always increments the
+     * digit prior to a nonzero discarded fraction.  Note that this rounding
+     * mode never decreases the magnitude of the calculated value.
+     */
+    public final static int ROUND_UP =           0;
+
+    /**
+     * Rounding mode to round towards zero.  Never increments the digit
+     * prior to a discarded fraction (i.e., truncates).  Note that this
+     * rounding mode never increases the magnitude of the calculated value.
+     */
+    public final static int ROUND_DOWN =         1;
+
+    /**
+     * Rounding mode to round towards positive infinity.  If the
+     * {@code BigDecimal} is positive, behaves as for
+     * {@code ROUND_UP}; if negative, behaves as for
+     * {@code ROUND_DOWN}.  Note that this rounding mode never
+     * decreases the calculated value.
+     */
+    public final static int ROUND_CEILING =      2;
+
+    /**
+     * Rounding mode to round towards negative infinity.  If the
+     * {@code BigDecimal} is positive, behave as for
+     * {@code ROUND_DOWN}; if negative, behave as for
+     * {@code ROUND_UP}.  Note that this rounding mode never
+     * increases the calculated value.
+     */
+    public final static int ROUND_FLOOR =        3;
+
+    /**
+     * Rounding mode to round towards {@literal "nearest neighbor"}
+     * unless both neighbors are equidistant, in which case round up.
+     * Behaves as for {@code ROUND_UP} if the discarded fraction is
+     * &ge; 0.5; otherwise, behaves as for {@code ROUND_DOWN}.  Note
+     * that this is the rounding mode that most of us were taught in
+     * grade school.
+     */
+    public final static int ROUND_HALF_UP =      4;
+
+    /**
+     * Rounding mode to round towards {@literal "nearest neighbor"}
+     * unless both neighbors are equidistant, in which case round
+     * down.  Behaves as for {@code ROUND_UP} if the discarded
+     * fraction is {@literal >} 0.5; otherwise, behaves as for
+     * {@code ROUND_DOWN}.
+     */
+    public final static int ROUND_HALF_DOWN =    5;
+
+    /**
+     * Rounding mode to round towards the {@literal "nearest neighbor"}
+     * unless both neighbors are equidistant, in which case, round
+     * towards the even neighbor.  Behaves as for
+     * {@code ROUND_HALF_UP} if the digit to the left of the
+     * discarded fraction is odd; behaves as for
+     * {@code ROUND_HALF_DOWN} if it's even.  Note that this is the
+     * rounding mode that minimizes cumulative error when applied
+     * repeatedly over a sequence of calculations.
+     */
+    public final static int ROUND_HALF_EVEN =    6;
+
+    /**
+     * Rounding mode to assert that the requested operation has an exact
+     * result, hence no rounding is necessary.  If this rounding mode is
+     * specified on an operation that yields an inexact result, an
+     * {@code ArithmeticException} is thrown.
+     */
+    public final static int ROUND_UNNECESSARY =  7;
+
+
+    // Scaling/Rounding Operations
+
+    /**
+     * Returns a {@code BigDecimal} rounded according to the
+     * {@code MathContext} settings.  If the precision setting is 0 then
+     * no rounding takes place.
+     *
+     * <p>The effect of this method is identical to that of the
+     * {@link #plus(MathContext)} method.
+     *
+     * @param mc the context to use.
+     * @return a {@code BigDecimal} rounded according to the
+     *         {@code MathContext} settings.
+     * @throws ArithmeticException if the rounding mode is
+     *         {@code UNNECESSARY} and the
+     *         {@code BigDecimal}  operation would require rounding.
+     * @see    #plus(MathContext)
+     * @since  1.5
+     */
+    public BigDecimal round(MathContext mc) {
+        return plus(mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose scale is the specified
+     * value, and whose unscaled value is determined by multiplying or
+     * dividing this {@code BigDecimal}'s unscaled value by the
+     * appropriate power of ten to maintain its overall value.  If the
+     * scale is reduced by the operation, the unscaled value must be
+     * divided (rather than multiplied), and the value may be changed;
+     * in this case, the specified rounding mode is applied to the
+     * division.
+     *
+     * <p>Note that since BigDecimal objects are immutable, calls of
+     * this method do <i>not</i> result in the original object being
+     * modified, contrary to the usual convention of having methods
+     * named <tt>set<i>X</i></tt> mutate field <i>{@code X}</i>.
+     * Instead, {@code setScale} returns an object with the proper
+     * scale; the returned object may or may not be newly allocated.
+     *
+     * @param  newScale scale of the {@code BigDecimal} value to be returned.
+     * @param  roundingMode The rounding mode to apply.
+     * @return a {@code BigDecimal} whose scale is the specified value,
+     *         and whose unscaled value is determined by multiplying or
+     *         dividing this {@code BigDecimal}'s unscaled value by the
+     *         appropriate power of ten to maintain its overall value.
+     * @throws ArithmeticException if {@code roundingMode==UNNECESSARY}
+     *         and the specified scaling operation would require
+     *         rounding.
+     * @see    RoundingMode
+     * @since  1.5
+     */
+    public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
+        return setScale(newScale, roundingMode.oldMode);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose scale is the specified
+     * value, and whose unscaled value is determined by multiplying or
+     * dividing this {@code BigDecimal}'s unscaled value by the
+     * appropriate power of ten to maintain its overall value.  If the
+     * scale is reduced by the operation, the unscaled value must be
+     * divided (rather than multiplied), and the value may be changed;
+     * in this case, the specified rounding mode is applied to the
+     * division.
+     *
+     * <p>Note that since BigDecimal objects are immutable, calls of
+     * this method do <i>not</i> result in the original object being
+     * modified, contrary to the usual convention of having methods
+     * named <tt>set<i>X</i></tt> mutate field <i>{@code X}</i>.
+     * Instead, {@code setScale} returns an object with the proper
+     * scale; the returned object may or may not be newly allocated.
+     *
+     * <p>The new {@link #setScale(int, RoundingMode)} method should
+     * be used in preference to this legacy method.
+     *
+     * @param  newScale scale of the {@code BigDecimal} value to be returned.
+     * @param  roundingMode The rounding mode to apply.
+     * @return a {@code BigDecimal} whose scale is the specified value,
+     *         and whose unscaled value is determined by multiplying or
+     *         dividing this {@code BigDecimal}'s unscaled value by the
+     *         appropriate power of ten to maintain its overall value.
+     * @throws ArithmeticException if {@code roundingMode==ROUND_UNNECESSARY}
+     *         and the specified scaling operation would require
+     *         rounding.
+     * @throws IllegalArgumentException if {@code roundingMode} does not
+     *         represent a valid rounding mode.
+     * @see    #ROUND_UP
+     * @see    #ROUND_DOWN
+     * @see    #ROUND_CEILING
+     * @see    #ROUND_FLOOR
+     * @see    #ROUND_HALF_UP
+     * @see    #ROUND_HALF_DOWN
+     * @see    #ROUND_HALF_EVEN
+     * @see    #ROUND_UNNECESSARY
+     */
+    public BigDecimal setScale(int newScale, int roundingMode) {
+        if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
+            throw new IllegalArgumentException("Invalid rounding mode");
+
+        int oldScale = this.scale;
+        if (newScale == oldScale)        // easy case
+            return this;
+        if (this.signum() == 0)            // zero can have any scale
+            return zeroValueOf(newScale);
+        if(this.intCompact!=INFLATED) {
+            long rs = this.intCompact;
+            if (newScale > oldScale) {
+                int raise = checkScale((long) newScale - oldScale);
+                if ((rs = longMultiplyPowerTen(rs, raise)) != INFLATED) {
+                    return valueOf(rs,newScale);
+                }
+                BigInteger rb = bigMultiplyPowerTen(raise);
+                return new BigDecimal(rb, INFLATED, newScale, (precision > 0) ? precision + raise : 0);
+            } else {
+                // newScale < oldScale -- drop some digits
+                // Can't predict the precision due to the effect of rounding.
+                int drop = checkScale((long) oldScale - newScale);
+                if (drop < LONG_TEN_POWERS_TABLE.length) {
+                    return divideAndRound(rs, LONG_TEN_POWERS_TABLE[drop], newScale, roundingMode, newScale);
+                } else {
+                    return divideAndRound(this.inflated(), bigTenToThe(drop), newScale, roundingMode, newScale);
+                }
+            }
+        } else {
+            if (newScale > oldScale) {
+                int raise = checkScale((long) newScale - oldScale);
+                BigInteger rb = bigMultiplyPowerTen(this.intVal,raise);
+                return new BigDecimal(rb, INFLATED, newScale, (precision > 0) ? precision + raise : 0);
+            } else {
+                // newScale < oldScale -- drop some digits
+                // Can't predict the precision due to the effect of rounding.
+                int drop = checkScale((long) oldScale - newScale);
+                if (drop < LONG_TEN_POWERS_TABLE.length)
+                    return divideAndRound(this.intVal, LONG_TEN_POWERS_TABLE[drop], newScale, roundingMode,
+                                          newScale);
+                else
+                    return divideAndRound(this.intVal,  bigTenToThe(drop), newScale, roundingMode, newScale);
+            }
+        }
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose scale is the specified
+     * value, and whose value is numerically equal to this
+     * {@code BigDecimal}'s.  Throws an {@code ArithmeticException}
+     * if this is not possible.
+     *
+     * <p>This call is typically used to increase the scale, in which
+     * case it is guaranteed that there exists a {@code BigDecimal}
+     * of the specified scale and the correct value.  The call can
+     * also be used to reduce the scale if the caller knows that the
+     * {@code BigDecimal} has sufficiently many zeros at the end of
+     * its fractional part (i.e., factors of ten in its integer value)
+     * to allow for the rescaling without changing its value.
+     *
+     * <p>This method returns the same result as the two-argument
+     * versions of {@code setScale}, but saves the caller the trouble
+     * of specifying a rounding mode in cases where it is irrelevant.
+     *
+     * <p>Note that since {@code BigDecimal} objects are immutable,
+     * calls of this method do <i>not</i> result in the original
+     * object being modified, contrary to the usual convention of
+     * having methods named <tt>set<i>X</i></tt> mutate field
+     * <i>{@code X}</i>.  Instead, {@code setScale} returns an
+     * object with the proper scale; the returned object may or may
+     * not be newly allocated.
+     *
+     * @param  newScale scale of the {@code BigDecimal} value to be returned.
+     * @return a {@code BigDecimal} whose scale is the specified value, and
+     *         whose unscaled value is determined by multiplying or dividing
+     *         this {@code BigDecimal}'s unscaled value by the appropriate
+     *         power of ten to maintain its overall value.
+     * @throws ArithmeticException if the specified scaling operation would
+     *         require rounding.
+     * @see    #setScale(int, int)
+     * @see    #setScale(int, RoundingMode)
+     */
+    public BigDecimal setScale(int newScale) {
+        return setScale(newScale, ROUND_UNNECESSARY);
+    }
+
+    // Decimal Point Motion Operations
+
+    /**
+     * Returns a {@code BigDecimal} which is equivalent to this one
+     * with the decimal point moved {@code n} places to the left.  If
+     * {@code n} is non-negative, the call merely adds {@code n} to
+     * the scale.  If {@code n} is negative, the call is equivalent
+     * to {@code movePointRight(-n)}.  The {@code BigDecimal}
+     * returned by this call has value <tt>(this &times;
+     * 10<sup>-n</sup>)</tt> and scale {@code max(this.scale()+n,
+     * 0)}.
+     *
+     * @param  n number of places to move the decimal point to the left.
+     * @return a {@code BigDecimal} which is equivalent to this one with the
+     *         decimal point moved {@code n} places to the left.
+     * @throws ArithmeticException if scale overflows.
+     */
+    public BigDecimal movePointLeft(int n) {
+        // Cannot use movePointRight(-n) in case of n==Integer.MIN_VALUE
+        int newScale = checkScale((long)scale + n);
+        BigDecimal num = new BigDecimal(intVal, intCompact, newScale, 0);
+        return num.scale < 0 ? num.setScale(0, ROUND_UNNECESSARY) : num;
+    }
+
+    /**
+     * Returns a {@code BigDecimal} which is equivalent to this one
+     * with the decimal point moved {@code n} places to the right.
+     * If {@code n} is non-negative, the call merely subtracts
+     * {@code n} from the scale.  If {@code n} is negative, the call
+     * is equivalent to {@code movePointLeft(-n)}.  The
+     * {@code BigDecimal} returned by this call has value <tt>(this
+     * &times; 10<sup>n</sup>)</tt> and scale {@code max(this.scale()-n,
+     * 0)}.
+     *
+     * @param  n number of places to move the decimal point to the right.
+     * @return a {@code BigDecimal} which is equivalent to this one
+     *         with the decimal point moved {@code n} places to the right.
+     * @throws ArithmeticException if scale overflows.
+     */
+    public BigDecimal movePointRight(int n) {
+        // Cannot use movePointLeft(-n) in case of n==Integer.MIN_VALUE
+        int newScale = checkScale((long)scale - n);
+        BigDecimal num = new BigDecimal(intVal, intCompact, newScale, 0);
+        return num.scale < 0 ? num.setScale(0, ROUND_UNNECESSARY) : num;
+    }
+
+    /**
+     * Returns a BigDecimal whose numerical value is equal to
+     * ({@code this} * 10<sup>n</sup>).  The scale of
+     * the result is {@code (this.scale() - n)}.
+     *
+     * @param n the exponent power of ten to scale by
+     * @return a BigDecimal whose numerical value is equal to
+     * ({@code this} * 10<sup>n</sup>)
+     * @throws ArithmeticException if the scale would be
+     *         outside the range of a 32-bit integer.
+     *
+     * @since 1.5
+     */
+    public BigDecimal scaleByPowerOfTen(int n) {
+        return new BigDecimal(intVal, intCompact,
+                              checkScale((long)scale - n), precision);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} which is numerically equal to
+     * this one but with any trailing zeros removed from the
+     * representation.  For example, stripping the trailing zeros from
+     * the {@code BigDecimal} value {@code 600.0}, which has
+     * [{@code BigInteger}, {@code scale}] components equals to
+     * [6000, 1], yields {@code 6E2} with [{@code BigInteger},
+     * {@code scale}] components equals to [6, -2].  If
+     * this BigDecimal is numerically equal to zero, then
+     * {@code BigDecimal.ZERO} is returned.
+     *
+     * @return a numerically equal {@code BigDecimal} with any
+     * trailing zeros removed.
+     * @since 1.5
+     */
+    public BigDecimal stripTrailingZeros() {
+        if (intCompact == 0 || (intVal != null && intVal.signum() == 0)) {
+            return BigDecimal.ZERO;
+        } else if (intCompact != INFLATED) {
+            return createAndStripZerosToMatchScale(intCompact, scale, Long.MIN_VALUE);
+        } else {
+            return createAndStripZerosToMatchScale(intVal, scale, Long.MIN_VALUE);
+        }
+    }
+
+    // Comparison Operations
+
+    /**
+     * Compares this {@code BigDecimal} with the specified
+     * {@code BigDecimal}.  Two {@code BigDecimal} objects that are
+     * equal in value but have a different scale (like 2.0 and 2.00)
+     * are considered equal by this method.  This method is provided
+     * in preference to individual methods for each of the six boolean
+     * comparison operators ({@literal <}, ==,
+     * {@literal >}, {@literal >=}, !=, {@literal <=}).  The
+     * suggested idiom for performing these comparisons is:
+     * {@code (x.compareTo(y)} &lt;<i>op</i>&gt; {@code 0)}, where
+     * &lt;<i>op</i>&gt; is one of the six comparison operators.
+     *
+     * @param  val {@code BigDecimal} to which this {@code BigDecimal} is
+     *         to be compared.
+     * @return -1, 0, or 1 as this {@code BigDecimal} is numerically
+     *          less than, equal to, or greater than {@code val}.
+     */
+    public int compareTo(BigDecimal val) {
+        // Quick path for equal scale and non-inflated case.
+        if (scale == val.scale) {
+            long xs = intCompact;
+            long ys = val.intCompact;
+            if (xs != INFLATED && ys != INFLATED)
+                return xs != ys ? ((xs > ys) ? 1 : -1) : 0;
+        }
+        int xsign = this.signum();
+        int ysign = val.signum();
+        if (xsign != ysign)
+            return (xsign > ysign) ? 1 : -1;
+        if (xsign == 0)
+            return 0;
+        int cmp = compareMagnitude(val);
+        return (xsign > 0) ? cmp : -cmp;
+    }
+
+    /**
+     * Version of compareTo that ignores sign.
+     */
+    private int compareMagnitude(BigDecimal val) {
+        // Match scales, avoid unnecessary inflation
+        long ys = val.intCompact;
+        long xs = this.intCompact;
+        if (xs == 0)
+            return (ys == 0) ? 0 : -1;
+        if (ys == 0)
+            return 1;
+
+        long sdiff = (long)this.scale - val.scale;
+        if (sdiff != 0) {
+            // Avoid matching scales if the (adjusted) exponents differ
+            long xae = (long)this.precision() - this.scale;   // [-1]
+            long yae = (long)val.precision() - val.scale;     // [-1]
+            if (xae < yae)
+                return -1;
+            if (xae > yae)
+                return 1;
+            BigInteger rb = null;
+            if (sdiff < 0) {
+                // The cases sdiff <= Integer.MIN_VALUE intentionally fall through.
+                if ( sdiff > Integer.MIN_VALUE &&
+                      (xs == INFLATED ||
+                      (xs = longMultiplyPowerTen(xs, (int)-sdiff)) == INFLATED) &&
+                     ys == INFLATED) {
+                    rb = bigMultiplyPowerTen((int)-sdiff);
+                    return rb.compareMagnitude(val.intVal);
+                }
+            } else { // sdiff > 0
+                // The cases sdiff > Integer.MAX_VALUE intentionally fall through.
+                if ( sdiff <= Integer.MAX_VALUE &&
+                      (ys == INFLATED ||
+                      (ys = longMultiplyPowerTen(ys, (int)sdiff)) == INFLATED) &&
+                     xs == INFLATED) {
+                    rb = val.bigMultiplyPowerTen((int)sdiff);
+                    return this.intVal.compareMagnitude(rb);
+                }
+            }
+        }
+        if (xs != INFLATED)
+            return (ys != INFLATED) ? longCompareMagnitude(xs, ys) : -1;
+        else if (ys != INFLATED)
+            return 1;
+        else
+            return this.intVal.compareMagnitude(val.intVal);
+    }
+
+    /**
+     * Compares this {@code BigDecimal} with the specified
+     * {@code Object} for equality.  Unlike {@link
+     * #compareTo(BigDecimal) compareTo}, this method considers two
+     * {@code BigDecimal} objects equal only if they are equal in
+     * value and scale (thus 2.0 is not equal to 2.00 when compared by
+     * this method).
+     *
+     * @param  x {@code Object} to which this {@code BigDecimal} is
+     *         to be compared.
+     * @return {@code true} if and only if the specified {@code Object} is a
+     *         {@code BigDecimal} whose value and scale are equal to this
+     *         {@code BigDecimal}'s.
+     * @see    #compareTo(java.math.BigDecimal)
+     * @see    #hashCode
+     */
+    @Override
+    public boolean equals(Object x) {
+        if (!(x instanceof BigDecimal))
+            return false;
+        BigDecimal xDec = (BigDecimal) x;
+        if (x == this)
+            return true;
+        if (scale != xDec.scale)
+            return false;
+        long s = this.intCompact;
+        long xs = xDec.intCompact;
+        if (s != INFLATED) {
+            if (xs == INFLATED)
+                xs = compactValFor(xDec.intVal);
+            return xs == s;
+        } else if (xs != INFLATED)
+            return xs == compactValFor(this.intVal);
+
+        return this.inflated().equals(xDec.inflated());
+    }
+
+    /**
+     * Returns the minimum of this {@code BigDecimal} and
+     * {@code val}.
+     *
+     * @param  val value with which the minimum is to be computed.
+     * @return the {@code BigDecimal} whose value is the lesser of this
+     *         {@code BigDecimal} and {@code val}.  If they are equal,
+     *         as defined by the {@link #compareTo(BigDecimal) compareTo}
+     *         method, {@code this} is returned.
+     * @see    #compareTo(java.math.BigDecimal)
+     */
+    public BigDecimal min(BigDecimal val) {
+        return (compareTo(val) <= 0 ? this : val);
+    }
+
+    /**
+     * Returns the maximum of this {@code BigDecimal} and {@code val}.
+     *
+     * @param  val value with which the maximum is to be computed.
+     * @return the {@code BigDecimal} whose value is the greater of this
+     *         {@code BigDecimal} and {@code val}.  If they are equal,
+     *         as defined by the {@link #compareTo(BigDecimal) compareTo}
+     *         method, {@code this} is returned.
+     * @see    #compareTo(java.math.BigDecimal)
+     */
+    public BigDecimal max(BigDecimal val) {
+        return (compareTo(val) >= 0 ? this : val);
+    }
+
+    // Hash Function
+
+    /**
+     * Returns the hash code for this {@code BigDecimal}.  Note that
+     * two {@code BigDecimal} objects that are numerically equal but
+     * differ in scale (like 2.0 and 2.00) will generally <i>not</i>
+     * have the same hash code.
+     *
+     * @return hash code for this {@code BigDecimal}.
+     * @see #equals(Object)
+     */
+    @Override
+    public int hashCode() {
+        if (intCompact != INFLATED) {
+            long val2 = (intCompact < 0)? -intCompact : intCompact;
+            int temp = (int)( ((int)(val2 >>> 32)) * 31  +
+                              (val2 & LONG_MASK));
+            return 31*((intCompact < 0) ?-temp:temp) + scale;
+        } else
+            return 31*intVal.hashCode() + scale;
+    }
+
+    // Format Converters
+
+    /**
+     * Returns the string representation of this {@code BigDecimal},
+     * using scientific notation if an exponent is needed.
+     *
+     * <p>A standard canonical string form of the {@code BigDecimal}
+     * is created as though by the following steps: first, the
+     * absolute value of the unscaled value of the {@code BigDecimal}
+     * is converted to a string in base ten using the characters
+     * {@code '0'} through {@code '9'} with no leading zeros (except
+     * if its value is zero, in which case a single {@code '0'}
+     * character is used).
+     *
+     * <p>Next, an <i>adjusted exponent</i> is calculated; this is the
+     * negated scale, plus the number of characters in the converted
+     * unscaled value, less one.  That is,
+     * {@code -scale+(ulength-1)}, where {@code ulength} is the
+     * length of the absolute value of the unscaled value in decimal
+     * digits (its <i>precision</i>).
+     *
+     * <p>If the scale is greater than or equal to zero and the
+     * adjusted exponent is greater than or equal to {@code -6}, the
+     * number will be converted to a character form without using
+     * exponential notation.  In this case, if the scale is zero then
+     * no decimal point is added and if the scale is positive a
+     * decimal point will be inserted with the scale specifying the
+     * number of characters to the right of the decimal point.
+     * {@code '0'} characters are added to the left of the converted
+     * unscaled value as necessary.  If no character precedes the
+     * decimal point after this insertion then a conventional
+     * {@code '0'} character is prefixed.
+     *
+     * <p>Otherwise (that is, if the scale is negative, or the
+     * adjusted exponent is less than {@code -6}), the number will be
+     * converted to a character form using exponential notation.  In
+     * this case, if the converted {@code BigInteger} has more than
+     * one digit a decimal point is inserted after the first digit.
+     * An exponent in character form is then suffixed to the converted
+     * unscaled value (perhaps with inserted decimal point); this
+     * comprises the letter {@code 'E'} followed immediately by the
+     * adjusted exponent converted to a character form.  The latter is
+     * in base ten, using the characters {@code '0'} through
+     * {@code '9'} with no leading zeros, and is always prefixed by a
+     * sign character {@code '-'} (<tt>'&#92;u002D'</tt>) if the
+     * adjusted exponent is negative, {@code '+'}
+     * (<tt>'&#92;u002B'</tt>) otherwise).
+     *
+     * <p>Finally, the entire string is prefixed by a minus sign
+     * character {@code '-'} (<tt>'&#92;u002D'</tt>) if the unscaled
+     * value is less than zero.  No sign character is prefixed if the
+     * unscaled value is zero or positive.
+     *
+     * <p><b>Examples:</b>
+     * <p>For each representation [<i>unscaled value</i>, <i>scale</i>]
+     * on the left, the resulting string is shown on the right.
+     * <pre>
+     * [123,0]      "123"
+     * [-123,0]     "-123"
+     * [123,-1]     "1.23E+3"
+     * [123,-3]     "1.23E+5"
+     * [123,1]      "12.3"
+     * [123,5]      "0.00123"
+     * [123,10]     "1.23E-8"
+     * [-123,12]    "-1.23E-10"
+     * </pre>
+     *
+     * <b>Notes:</b>
+     * <ol>
+     *
+     * <li>There is a one-to-one mapping between the distinguishable
+     * {@code BigDecimal} values and the result of this conversion.
+     * That is, every distinguishable {@code BigDecimal} value
+     * (unscaled value and scale) has a unique string representation
+     * as a result of using {@code toString}.  If that string
+     * representation is converted back to a {@code BigDecimal} using
+     * the {@link #BigDecimal(String)} constructor, then the original
+     * value will be recovered.
+     *
+     * <li>The string produced for a given number is always the same;
+     * it is not affected by locale.  This means that it can be used
+     * as a canonical string representation for exchanging decimal
+     * data, or as a key for a Hashtable, etc.  Locale-sensitive
+     * number formatting and parsing is handled by the {@link
+     * java.text.NumberFormat} class and its subclasses.
+     *
+     * <li>The {@link #toEngineeringString} method may be used for
+     * presenting numbers with exponents in engineering notation, and the
+     * {@link #setScale(int,RoundingMode) setScale} method may be used for
+     * rounding a {@code BigDecimal} so it has a known number of digits after
+     * the decimal point.
+     *
+     * <li>The digit-to-character mapping provided by
+     * {@code Character.forDigit} is used.
+     *
+     * </ol>
+     *
+     * @return string representation of this {@code BigDecimal}.
+     * @see    Character#forDigit
+     * @see    #BigDecimal(java.lang.String)
+     */
+    @Override
+    public String toString() {
+        String sc = stringCache;
+        if (sc == null)
+            stringCache = sc = layoutChars(true);
+        return sc;
+    }
+
+    /**
+     * Returns a string representation of this {@code BigDecimal},
+     * using engineering notation if an exponent is needed.
+     *
+     * <p>Returns a string that represents the {@code BigDecimal} as
+     * described in the {@link #toString()} method, except that if
+     * exponential notation is used, the power of ten is adjusted to
+     * be a multiple of three (engineering notation) such that the
+     * integer part of nonzero values will be in the range 1 through
+     * 999.  If exponential notation is used for zero values, a
+     * decimal point and one or two fractional zero digits are used so
+     * that the scale of the zero value is preserved.  Note that
+     * unlike the output of {@link #toString()}, the output of this
+     * method is <em>not</em> guaranteed to recover the same [integer,
+     * scale] pair of this {@code BigDecimal} if the output string is
+     * converting back to a {@code BigDecimal} using the {@linkplain
+     * #BigDecimal(String) string constructor}.  The result of this method meets
+     * the weaker constraint of always producing a numerically equal
+     * result from applying the string constructor to the method's output.
+     *
+     * @return string representation of this {@code BigDecimal}, using
+     *         engineering notation if an exponent is needed.
+     * @since  1.5
+     */
+    public String toEngineeringString() {
+        return layoutChars(false);
+    }
+
+    /**
+     * Returns a string representation of this {@code BigDecimal}
+     * without an exponent field.  For values with a positive scale,
+     * the number of digits to the right of the decimal point is used
+     * to indicate scale.  For values with a zero or negative scale,
+     * the resulting string is generated as if the value were
+     * converted to a numerically equal value with zero scale and as
+     * if all the trailing zeros of the zero scale value were present
+     * in the result.
+     *
+     * The entire string is prefixed by a minus sign character '-'
+     * (<tt>'&#92;u002D'</tt>) if the unscaled value is less than
+     * zero. No sign character is prefixed if the unscaled value is
+     * zero or positive.
+     *
+     * Note that if the result of this method is passed to the
+     * {@linkplain #BigDecimal(String) string constructor}, only the
+     * numerical value of this {@code BigDecimal} will necessarily be
+     * recovered; the representation of the new {@code BigDecimal}
+     * may have a different scale.  In particular, if this
+     * {@code BigDecimal} has a negative scale, the string resulting
+     * from this method will have a scale of zero when processed by
+     * the string constructor.
+     *
+     * (This method behaves analogously to the {@code toString}
+     * method in 1.4 and earlier releases.)
+     *
+     * @return a string representation of this {@code BigDecimal}
+     * without an exponent field.
+     * @since 1.5
+     * @see #toString()
+     * @see #toEngineeringString()
+     */
+    public String toPlainString() {
+        if(scale==0) {
+            if(intCompact!=INFLATED) {
+                return Long.toString(intCompact);
+            } else {
+                return intVal.toString();
+            }
+        }
+        if(this.scale<0) { // No decimal point
+            if(signum()==0) {
+                return "0";
+            }
+            int tailingZeros = checkScaleNonZero((-(long)scale));
+            StringBuilder buf;
+            if(intCompact!=INFLATED) {
+                buf = new StringBuilder(20+tailingZeros);
+                buf.append(intCompact);
+            } else {
+                String str = intVal.toString();
+                buf = new StringBuilder(str.length()+tailingZeros);
+                buf.append(str);
+            }
+            for (int i = 0; i < tailingZeros; i++)
+                buf.append('0');
+            return buf.toString();
+        }
+        String str ;
+        if(intCompact!=INFLATED) {
+            str = Long.toString(Math.abs(intCompact));
+        } else {
+            str = intVal.abs().toString();
+        }
+        return getValueString(signum(), str, scale);
+    }
+
+    /* Returns a digit.digit string */
+    private String getValueString(int signum, String intString, int scale) {
+        /* Insert decimal point */
+        StringBuilder buf;
+        int insertionPoint = intString.length() - scale;
+        if (insertionPoint == 0) {  /* Point goes right before intVal */
+            return (signum<0 ? "-0." : "0.") + intString;
+        } else if (insertionPoint > 0) { /* Point goes inside intVal */
+            buf = new StringBuilder(intString);
+            buf.insert(insertionPoint, '.');
+            if (signum < 0)
+                buf.insert(0, '-');
+        } else { /* We must insert zeros between point and intVal */
+            buf = new StringBuilder(3-insertionPoint + intString.length());
+            buf.append(signum<0 ? "-0." : "0.");
+            for (int i=0; i<-insertionPoint; i++)
+                buf.append('0');
+            buf.append(intString);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code BigInteger}.
+     * This conversion is analogous to the
+     * <i>narrowing primitive conversion</i> from {@code double} to
+     * {@code long} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * any fractional part of this
+     * {@code BigDecimal} will be discarded.  Note that this
+     * conversion can lose information about the precision of the
+     * {@code BigDecimal} value.
+     * <p>
+     * To have an exception thrown if the conversion is inexact (in
+     * other words if a nonzero fractional part is discarded), use the
+     * {@link #toBigIntegerExact()} method.
+     *
+     * @return this {@code BigDecimal} converted to a {@code BigInteger}.
+     */
+    public BigInteger toBigInteger() {
+        // force to an integer, quietly
+        return this.setScale(0, ROUND_DOWN).inflated();
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code BigInteger},
+     * checking for lost information.  An exception is thrown if this
+     * {@code BigDecimal} has a nonzero fractional part.
+     *
+     * @return this {@code BigDecimal} converted to a {@code BigInteger}.
+     * @throws ArithmeticException if {@code this} has a nonzero
+     *         fractional part.
+     * @since  1.5
+     */
+    public BigInteger toBigIntegerExact() {
+        // round to an integer, with Exception if decimal part non-0
+        return this.setScale(0, ROUND_UNNECESSARY).inflated();
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code long}.
+     * This conversion is analogous to the
+     * <i>narrowing primitive conversion</i> from {@code double} to
+     * {@code short} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * any fractional part of this
+     * {@code BigDecimal} will be discarded, and if the resulting
+     * "{@code BigInteger}" is too big to fit in a
+     * {@code long}, only the low-order 64 bits are returned.
+     * Note that this conversion can lose information about the
+     * overall magnitude and precision of this {@code BigDecimal} value as well
+     * as return a result with the opposite sign.
+     *
+     * @return this {@code BigDecimal} converted to a {@code long}.
+     */
+    public long longValue(){
+        if (intCompact != INFLATED && scale == 0) {
+            return intCompact;
+        } else {
+            // Fastpath zero and small values
+            if (this.signum() == 0 || fractionOnly() ||
+                // Fastpath very large-scale values that will result
+                // in a truncated value of zero. If the scale is -64
+                // or less, there are at least 64 powers of 10 in the
+                // value of the numerical result. Since 10 = 2*5, in
+                // that case there would also be 64 powers of 2 in the
+                // result, meaning all 64 bits of a long will be zero.
+                scale <= -64) {
+                return 0;
+            } else {
+                return toBigInteger().longValue();
+            }
+        }
+    }
+
+    /**
+     * Return true if a nonzero BigDecimal has an absolute value less
+     * than one; i.e. only has fraction digits.
+     */
+    private boolean fractionOnly() {
+        assert this.signum() != 0;
+        return (this.precision() - this.scale) <= 0;
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code long}, checking
+     * for lost information.  If this {@code BigDecimal} has a
+     * nonzero fractional part or is out of the possible range for a
+     * {@code long} result then an {@code ArithmeticException} is
+     * thrown.
+     *
+     * @return this {@code BigDecimal} converted to a {@code long}.
+     * @throws ArithmeticException if {@code this} has a nonzero
+     *         fractional part, or will not fit in a {@code long}.
+     * @since  1.5
+     */
+    public long longValueExact() {
+        if (intCompact != INFLATED && scale == 0)
+            return intCompact;
+
+        // Fastpath zero
+        if (this.signum() == 0)
+            return 0;
+
+        // Fastpath numbers less than 1.0 (the latter can be very slow
+        // to round if very small)
+        if (fractionOnly())
+            throw new ArithmeticException("Rounding necessary");
+
+        // If more than 19 digits in integer part it cannot possibly fit
+        if ((precision() - scale) > 19) // [OK for negative scale too]
+            throw new java.lang.ArithmeticException("Overflow");
+
+        // round to an integer, with Exception if decimal part non-0
+        BigDecimal num = this.setScale(0, ROUND_UNNECESSARY);
+        if (num.precision() >= 19) // need to check carefully
+            LongOverflow.check(num);
+        return num.inflated().longValue();
+    }
+
+    private static class LongOverflow {
+        /** BigInteger equal to Long.MIN_VALUE. */
+        private static final BigInteger LONGMIN = BigInteger.valueOf(Long.MIN_VALUE);
+
+        /** BigInteger equal to Long.MAX_VALUE. */
+        private static final BigInteger LONGMAX = BigInteger.valueOf(Long.MAX_VALUE);
+
+        public static void check(BigDecimal num) {
+            BigInteger intVal = num.inflated();
+            if (intVal.compareTo(LONGMIN) < 0 ||
+                intVal.compareTo(LONGMAX) > 0)
+                throw new java.lang.ArithmeticException("Overflow");
+        }
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to an {@code int}.
+     * This conversion is analogous to the
+     * <i>narrowing primitive conversion</i> from {@code double} to
+     * {@code short} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * any fractional part of this
+     * {@code BigDecimal} will be discarded, and if the resulting
+     * "{@code BigInteger}" is too big to fit in an
+     * {@code int}, only the low-order 32 bits are returned.
+     * Note that this conversion can lose information about the
+     * overall magnitude and precision of this {@code BigDecimal}
+     * value as well as return a result with the opposite sign.
+     *
+     * @return this {@code BigDecimal} converted to an {@code int}.
+     */
+    public int intValue() {
+        return  (intCompact != INFLATED && scale == 0) ?
+            (int)intCompact :
+            (int)longValue();
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to an {@code int}, checking
+     * for lost information.  If this {@code BigDecimal} has a
+     * nonzero fractional part or is out of the possible range for an
+     * {@code int} result then an {@code ArithmeticException} is
+     * thrown.
+     *
+     * @return this {@code BigDecimal} converted to an {@code int}.
+     * @throws ArithmeticException if {@code this} has a nonzero
+     *         fractional part, or will not fit in an {@code int}.
+     * @since  1.5
+     */
+    public int intValueExact() {
+       long num;
+       num = this.longValueExact();     // will check decimal part
+       if ((int)num != num)
+           throw new java.lang.ArithmeticException("Overflow");
+       return (int)num;
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code short}, checking
+     * for lost information.  If this {@code BigDecimal} has a
+     * nonzero fractional part or is out of the possible range for a
+     * {@code short} result then an {@code ArithmeticException} is
+     * thrown.
+     *
+     * @return this {@code BigDecimal} converted to a {@code short}.
+     * @throws ArithmeticException if {@code this} has a nonzero
+     *         fractional part, or will not fit in a {@code short}.
+     * @since  1.5
+     */
+    public short shortValueExact() {
+       long num;
+       num = this.longValueExact();     // will check decimal part
+       if ((short)num != num)
+           throw new java.lang.ArithmeticException("Overflow");
+       return (short)num;
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code byte}, checking
+     * for lost information.  If this {@code BigDecimal} has a
+     * nonzero fractional part or is out of the possible range for a
+     * {@code byte} result then an {@code ArithmeticException} is
+     * thrown.
+     *
+     * @return this {@code BigDecimal} converted to a {@code byte}.
+     * @throws ArithmeticException if {@code this} has a nonzero
+     *         fractional part, or will not fit in a {@code byte}.
+     * @since  1.5
+     */
+    public byte byteValueExact() {
+       long num;
+       num = this.longValueExact();     // will check decimal part
+       if ((byte)num != num)
+           throw new java.lang.ArithmeticException("Overflow");
+       return (byte)num;
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code float}.
+     * This conversion is similar to the
+     * <i>narrowing primitive conversion</i> from {@code double} to
+     * {@code float} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * if this {@code BigDecimal} has too great a
+     * magnitude to represent as a {@code float}, it will be
+     * converted to {@link Float#NEGATIVE_INFINITY} or {@link
+     * Float#POSITIVE_INFINITY} as appropriate.  Note that even when
+     * the return value is finite, this conversion can lose
+     * information about the precision of the {@code BigDecimal}
+     * value.
+     *
+     * @return this {@code BigDecimal} converted to a {@code float}.
+     */
+    public float floatValue(){
+        if(intCompact != INFLATED) {
+            if (scale == 0) {
+                return (float)intCompact;
+            } else {
+                /*
+                 * If both intCompact and the scale can be exactly
+                 * represented as float values, perform a single float
+                 * multiply or divide to compute the (properly
+                 * rounded) result.
+                 */
+                if (Math.abs(intCompact) < 1L<<22 ) {
+                    // Don't have too guard against
+                    // Math.abs(MIN_VALUE) because of outer check
+                    // against INFLATED.
+                    if (scale > 0 && scale < float10pow.length) {
+                        return (float)intCompact / float10pow[scale];
+                    } else if (scale < 0 && scale > -float10pow.length) {
+                        return (float)intCompact * float10pow[-scale];
+                    }
+                }
+            }
+        }
+        // Somewhat inefficient, but guaranteed to work.
+        return Float.parseFloat(this.toString());
+    }
+
+    /**
+     * Converts this {@code BigDecimal} to a {@code double}.
+     * This conversion is similar to the
+     * <i>narrowing primitive conversion</i> from {@code double} to
+     * {@code float} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * if this {@code BigDecimal} has too great a
+     * magnitude represent as a {@code double}, it will be
+     * converted to {@link Double#NEGATIVE_INFINITY} or {@link
+     * Double#POSITIVE_INFINITY} as appropriate.  Note that even when
+     * the return value is finite, this conversion can lose
+     * information about the precision of the {@code BigDecimal}
+     * value.
+     *
+     * @return this {@code BigDecimal} converted to a {@code double}.
+     */
+    public double doubleValue(){
+        if(intCompact != INFLATED) {
+            if (scale == 0) {
+                return (double)intCompact;
+            } else {
+                /*
+                 * If both intCompact and the scale can be exactly
+                 * represented as double values, perform a single
+                 * double multiply or divide to compute the (properly
+                 * rounded) result.
+                 */
+                if (Math.abs(intCompact) < 1L<<52 ) {
+                    // Don't have too guard against
+                    // Math.abs(MIN_VALUE) because of outer check
+                    // against INFLATED.
+                    if (scale > 0 && scale < double10pow.length) {
+                        return (double)intCompact / double10pow[scale];
+                    } else if (scale < 0 && scale > -double10pow.length) {
+                        return (double)intCompact * double10pow[-scale];
+                    }
+                }
+            }
+        }
+        // Somewhat inefficient, but guaranteed to work.
+        return Double.parseDouble(this.toString());
+    }
+
+    /**
+     * Powers of 10 which can be represented exactly in {@code
+     * double}.
+     */
+    private static final double double10pow[] = {
+        1.0e0,  1.0e1,  1.0e2,  1.0e3,  1.0e4,  1.0e5,
+        1.0e6,  1.0e7,  1.0e8,  1.0e9,  1.0e10, 1.0e11,
+        1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17,
+        1.0e18, 1.0e19, 1.0e20, 1.0e21, 1.0e22
+    };
+
+    /**
+     * Powers of 10 which can be represented exactly in {@code
+     * float}.
+     */
+    private static final float float10pow[] = {
+        1.0e0f, 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
+        1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
+    };
+
+    /**
+     * Returns the size of an ulp, a unit in the last place, of this
+     * {@code BigDecimal}.  An ulp of a nonzero {@code BigDecimal}
+     * value is the positive distance between this value and the
+     * {@code BigDecimal} value next larger in magnitude with the
+     * same number of digits.  An ulp of a zero value is numerically
+     * equal to 1 with the scale of {@code this}.  The result is
+     * stored with the same scale as {@code this} so the result
+     * for zero and nonzero values is equal to {@code [1,
+     * this.scale()]}.
+     *
+     * @return the size of an ulp of {@code this}
+     * @since 1.5
+     */
+    public BigDecimal ulp() {
+        return BigDecimal.valueOf(1, this.scale(), 1);
+    }
+
+    // Private class to build a string representation for BigDecimal object.
+    // "StringBuilderHelper" is constructed as a thread local variable so it is
+    // thread safe. The StringBuilder field acts as a buffer to hold the temporary
+    // representation of BigDecimal. The cmpCharArray holds all the characters for
+    // the compact representation of BigDecimal (except for '-' sign' if it is
+    // negative) if its intCompact field is not INFLATED. It is shared by all
+    // calls to toString() and its variants in that particular thread.
+    static class StringBuilderHelper {
+        final StringBuilder sb;    // Placeholder for BigDecimal string
+        final char[] cmpCharArray; // character array to place the intCompact
+
+        StringBuilderHelper() {
+            sb = new StringBuilder();
+            // All non negative longs can be made to fit into 19 character array.
+            cmpCharArray = new char[19];
+        }
+
+        // Accessors.
+        StringBuilder getStringBuilder() {
+            sb.setLength(0);
+            return sb;
+        }
+
+        char[] getCompactCharArray() {
+            return cmpCharArray;
+        }
+
+        /**
+         * Places characters representing the intCompact in {@code long} into
+         * cmpCharArray and returns the offset to the array where the
+         * representation starts.
+         *
+         * @param intCompact the number to put into the cmpCharArray.
+         * @return offset to the array where the representation starts.
+         * Note: intCompact must be greater or equal to zero.
+         */
+        int putIntCompact(long intCompact) {
+            assert intCompact >= 0;
+
+            long q;
+            int r;
+            // since we start from the least significant digit, charPos points to
+            // the last character in cmpCharArray.
+            int charPos = cmpCharArray.length;
+
+            // Get 2 digits/iteration using longs until quotient fits into an int
+            while (intCompact > Integer.MAX_VALUE) {
+                q = intCompact / 100;
+                r = (int)(intCompact - q * 100);
+                intCompact = q;
+                cmpCharArray[--charPos] = DIGIT_ONES[r];
+                cmpCharArray[--charPos] = DIGIT_TENS[r];
+            }
+
+            // Get 2 digits/iteration using ints when i2 >= 100
+            int q2;
+            int i2 = (int)intCompact;
+            while (i2 >= 100) {
+                q2 = i2 / 100;
+                r  = i2 - q2 * 100;
+                i2 = q2;
+                cmpCharArray[--charPos] = DIGIT_ONES[r];
+                cmpCharArray[--charPos] = DIGIT_TENS[r];
+            }
+
+            cmpCharArray[--charPos] = DIGIT_ONES[i2];
+            if (i2 >= 10)
+                cmpCharArray[--charPos] = DIGIT_TENS[i2];
+
+            return charPos;
+        }
+
+        final static char[] DIGIT_TENS = {
+            '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+            '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
+            '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
+            '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
+            '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
+            '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
+            '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
+            '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
+            '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
+            '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
+        };
+
+        final static char[] DIGIT_ONES = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        };
+    }
+
+    /**
+     * Lay out this {@code BigDecimal} into a {@code char[]} array.
+     * The Java 1.2 equivalent to this was called {@code getValueString}.
+     *
+     * @param  sci {@code true} for Scientific exponential notation;
+     *          {@code false} for Engineering
+     * @return string with canonical string representation of this
+     *         {@code BigDecimal}
+     */
+    private String layoutChars(boolean sci) {
+        if (scale == 0)                      // zero scale is trivial
+            return (intCompact != INFLATED) ?
+                Long.toString(intCompact):
+                intVal.toString();
+        if (scale == 2  &&
+            intCompact >= 0 && intCompact < Integer.MAX_VALUE) {
+            // currency fast path
+            int lowInt = (int)intCompact % 100;
+            int highInt = (int)intCompact / 100;
+            return (Integer.toString(highInt) + '.' +
+                    StringBuilderHelper.DIGIT_TENS[lowInt] +
+                    StringBuilderHelper.DIGIT_ONES[lowInt]) ;
+        }
+
+        StringBuilderHelper sbHelper = threadLocalStringBuilderHelper.get();
+        char[] coeff;
+        int offset;  // offset is the starting index for coeff array
+        // Get the significand as an absolute value
+        if (intCompact != INFLATED) {
+            offset = sbHelper.putIntCompact(Math.abs(intCompact));
+            coeff  = sbHelper.getCompactCharArray();
+        } else {
+            offset = 0;
+            coeff  = intVal.abs().toString().toCharArray();
+        }
+
+        // Construct a buffer, with sufficient capacity for all cases.
+        // If E-notation is needed, length will be: +1 if negative, +1
+        // if '.' needed, +2 for "E+", + up to 10 for adjusted exponent.
+        // Otherwise it could have +1 if negative, plus leading "0.00000"
+        StringBuilder buf = sbHelper.getStringBuilder();
+        if (signum() < 0)             // prefix '-' if negative
+            buf.append('-');
+        int coeffLen = coeff.length - offset;
+        long adjusted = -(long)scale + (coeffLen -1);
+        if ((scale >= 0) && (adjusted >= -6)) { // plain number
+            int pad = scale - coeffLen;         // count of padding zeros
+            if (pad >= 0) {                     // 0.xxx form
+                buf.append('0');
+                buf.append('.');
+                for (; pad>0; pad--) {
+                    buf.append('0');
+                }
+                buf.append(coeff, offset, coeffLen);
+            } else {                         // xx.xx form
+                buf.append(coeff, offset, -pad);
+                buf.append('.');
+                buf.append(coeff, -pad + offset, scale);
+            }
+        } else { // E-notation is needed
+            if (sci) {                       // Scientific notation
+                buf.append(coeff[offset]);   // first character
+                if (coeffLen > 1) {          // more to come
+                    buf.append('.');
+                    buf.append(coeff, offset + 1, coeffLen - 1);
+                }
+            } else {                         // Engineering notation
+                int sig = (int)(adjusted % 3);
+                if (sig < 0)
+                    sig += 3;                // [adjusted was negative]
+                adjusted -= sig;             // now a multiple of 3
+                sig++;
+                if (signum() == 0) {
+                    switch (sig) {
+                    case 1:
+                        buf.append('0'); // exponent is a multiple of three
+                        break;
+                    case 2:
+                        buf.append("0.00");
+                        adjusted += 3;
+                        break;
+                    case 3:
+                        buf.append("0.0");
+                        adjusted += 3;
+                        break;
+                    default:
+                        throw new AssertionError("Unexpected sig value " + sig);
+                    }
+                } else if (sig >= coeffLen) {   // significand all in integer
+                    buf.append(coeff, offset, coeffLen);
+                    // may need some zeros, too
+                    for (int i = sig - coeffLen; i > 0; i--)
+                        buf.append('0');
+                } else {                     // xx.xxE form
+                    buf.append(coeff, offset, sig);
+                    buf.append('.');
+                    buf.append(coeff, offset + sig, coeffLen - sig);
+                }
+            }
+            if (adjusted != 0) {             // [!sci could have made 0]
+                buf.append('E');
+                if (adjusted > 0)            // force sign for positive
+                    buf.append('+');
+                buf.append(adjusted);
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Return 10 to the power n, as a {@code BigInteger}.
+     *
+     * @param  n the power of ten to be returned (>=0)
+     * @return a {@code BigInteger} with the value (10<sup>n</sup>)
+     */
+    private static BigInteger bigTenToThe(int n) {
+        if (n < 0)
+            return BigInteger.ZERO;
+
+        if (n < BIG_TEN_POWERS_TABLE_MAX) {
+            BigInteger[] pows = BIG_TEN_POWERS_TABLE;
+            if (n < pows.length)
+                return pows[n];
+            else
+                return expandBigIntegerTenPowers(n);
+        }
+
+        return BigInteger.TEN.pow(n);
+    }
+
+    /**
+     * Expand the BIG_TEN_POWERS_TABLE array to contain at least 10**n.
+     *
+     * @param n the power of ten to be returned (>=0)
+     * @return a {@code BigDecimal} with the value (10<sup>n</sup>) and
+     *         in the meantime, the BIG_TEN_POWERS_TABLE array gets
+     *         expanded to the size greater than n.
+     */
+    private static BigInteger expandBigIntegerTenPowers(int n) {
+        synchronized(BigDecimal.class) {
+            BigInteger[] pows = BIG_TEN_POWERS_TABLE;
+            int curLen = pows.length;
+            // The following comparison and the above synchronized statement is
+            // to prevent multiple threads from expanding the same array.
+            if (curLen <= n) {
+                int newLen = curLen << 1;
+                while (newLen <= n)
+                    newLen <<= 1;
+                pows = Arrays.copyOf(pows, newLen);
+                for (int i = curLen; i < newLen; i++)
+                    pows[i] = pows[i - 1].multiply(BigInteger.TEN);
+                // Based on the following facts:
+                // 1. pows is a private local variable;
+                // 2. the following store is a volatile store.
+                // the newly created array elements can be safely published.
+                BIG_TEN_POWERS_TABLE = pows;
+            }
+            return pows[n];
+        }
+    }
+
+    private static final long[] LONG_TEN_POWERS_TABLE = {
+        1,                     // 0 / 10^0
+        10,                    // 1 / 10^1
+        100,                   // 2 / 10^2
+        1000,                  // 3 / 10^3
+        10000,                 // 4 / 10^4
+        100000,                // 5 / 10^5
+        1000000,               // 6 / 10^6
+        10000000,              // 7 / 10^7
+        100000000,             // 8 / 10^8
+        1000000000,            // 9 / 10^9
+        10000000000L,          // 10 / 10^10
+        100000000000L,         // 11 / 10^11
+        1000000000000L,        // 12 / 10^12
+        10000000000000L,       // 13 / 10^13
+        100000000000000L,      // 14 / 10^14
+        1000000000000000L,     // 15 / 10^15
+        10000000000000000L,    // 16 / 10^16
+        100000000000000000L,   // 17 / 10^17
+        1000000000000000000L   // 18 / 10^18
+    };
+
+    private static volatile BigInteger BIG_TEN_POWERS_TABLE[] = {
+        BigInteger.ONE,
+        BigInteger.valueOf(10),
+        BigInteger.valueOf(100),
+        BigInteger.valueOf(1000),
+        BigInteger.valueOf(10000),
+        BigInteger.valueOf(100000),
+        BigInteger.valueOf(1000000),
+        BigInteger.valueOf(10000000),
+        BigInteger.valueOf(100000000),
+        BigInteger.valueOf(1000000000),
+        BigInteger.valueOf(10000000000L),
+        BigInteger.valueOf(100000000000L),
+        BigInteger.valueOf(1000000000000L),
+        BigInteger.valueOf(10000000000000L),
+        BigInteger.valueOf(100000000000000L),
+        BigInteger.valueOf(1000000000000000L),
+        BigInteger.valueOf(10000000000000000L),
+        BigInteger.valueOf(100000000000000000L),
+        BigInteger.valueOf(1000000000000000000L)
+    };
+
+    private static final int BIG_TEN_POWERS_TABLE_INITLEN =
+        BIG_TEN_POWERS_TABLE.length;
+    private static final int BIG_TEN_POWERS_TABLE_MAX =
+        16 * BIG_TEN_POWERS_TABLE_INITLEN;
+
+    private static final long THRESHOLDS_TABLE[] = {
+        Long.MAX_VALUE,                     // 0
+        Long.MAX_VALUE/10L,                 // 1
+        Long.MAX_VALUE/100L,                // 2
+        Long.MAX_VALUE/1000L,               // 3
+        Long.MAX_VALUE/10000L,              // 4
+        Long.MAX_VALUE/100000L,             // 5
+        Long.MAX_VALUE/1000000L,            // 6
+        Long.MAX_VALUE/10000000L,           // 7
+        Long.MAX_VALUE/100000000L,          // 8
+        Long.MAX_VALUE/1000000000L,         // 9
+        Long.MAX_VALUE/10000000000L,        // 10
+        Long.MAX_VALUE/100000000000L,       // 11
+        Long.MAX_VALUE/1000000000000L,      // 12
+        Long.MAX_VALUE/10000000000000L,     // 13
+        Long.MAX_VALUE/100000000000000L,    // 14
+        Long.MAX_VALUE/1000000000000000L,   // 15
+        Long.MAX_VALUE/10000000000000000L,  // 16
+        Long.MAX_VALUE/100000000000000000L, // 17
+        Long.MAX_VALUE/1000000000000000000L // 18
+    };
+
+    /**
+     * Compute val * 10 ^ n; return this product if it is
+     * representable as a long, INFLATED otherwise.
+     */
+    private static long longMultiplyPowerTen(long val, int n) {
+        if (val == 0 || n <= 0)
+            return val;
+        long[] tab = LONG_TEN_POWERS_TABLE;
+        long[] bounds = THRESHOLDS_TABLE;
+        if (n < tab.length && n < bounds.length) {
+            long tenpower = tab[n];
+            if (val == 1)
+                return tenpower;
+            if (Math.abs(val) <= bounds[n])
+                return val * tenpower;
+        }
+        return INFLATED;
+    }
+
+    /**
+     * Compute this * 10 ^ n.
+     * Needed mainly to allow special casing to trap zero value
+     */
+    private BigInteger bigMultiplyPowerTen(int n) {
+        if (n <= 0)
+            return this.inflated();
+
+        if (intCompact != INFLATED)
+            return bigTenToThe(n).multiply(intCompact);
+        else
+            return intVal.multiply(bigTenToThe(n));
+    }
+
+    /**
+     * Returns appropriate BigInteger from intVal field if intVal is
+     * null, i.e. the compact representation is in use.
+     */
+    private BigInteger inflated() {
+        if (intVal == null) {
+            return BigInteger.valueOf(intCompact);
+        }
+        return intVal;
+    }
+
+    /**
+     * Match the scales of two {@code BigDecimal}s to align their
+     * least significant digits.
+     *
+     * <p>If the scales of val[0] and val[1] differ, rescale
+     * (non-destructively) the lower-scaled {@code BigDecimal} so
+     * they match.  That is, the lower-scaled reference will be
+     * replaced by a reference to a new object with the same scale as
+     * the other {@code BigDecimal}.
+     *
+     * @param  val array of two elements referring to the two
+     *         {@code BigDecimal}s to be aligned.
+     */
+    private static void matchScale(BigDecimal[] val) {
+        if (val[0].scale == val[1].scale) {
+            return;
+        } else if (val[0].scale < val[1].scale) {
+            val[0] = val[0].setScale(val[1].scale, ROUND_UNNECESSARY);
+        } else if (val[1].scale < val[0].scale) {
+            val[1] = val[1].setScale(val[0].scale, ROUND_UNNECESSARY);
+        }
+    }
+
+    private static class UnsafeHolder {
+        private static final sun.misc.Unsafe unsafe;
+        private static final long intCompactOffset;
+        private static final long intValOffset;
+        static {
+            try {
+                unsafe = sun.misc.Unsafe.getUnsafe();
+                intCompactOffset = unsafe.objectFieldOffset
+                    (BigDecimal.class.getDeclaredField("intCompact"));
+                intValOffset = unsafe.objectFieldOffset
+                    (BigDecimal.class.getDeclaredField("intVal"));
+            } catch (Exception ex) {
+                throw new ExceptionInInitializerError(ex);
+            }
+        }
+        static void setIntCompactVolatile(BigDecimal bd, long val) {
+            unsafe.putLongVolatile(bd, intCompactOffset, val);
+        }
+
+        static void setIntValVolatile(BigDecimal bd, BigInteger val) {
+            unsafe.putObjectVolatile(bd, intValOffset, val);
+        }
+    }
+
+    /**
+     * Reconstitute the {@code BigDecimal} instance from a stream (that is,
+     * deserialize it).
+     *
+     * @param s the stream being read.
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+        // Read in all fields
+        s.defaultReadObject();
+        // validate possibly bad fields
+        if (intVal == null) {
+            String message = "BigDecimal: null intVal in stream";
+            throw new java.io.StreamCorruptedException(message);
+        // [all values of scale are now allowed]
+        }
+        UnsafeHolder.setIntCompactVolatile(this, compactValFor(intVal));
+    }
+
+   /**
+    * Serialize this {@code BigDecimal} to the stream in question
+    *
+    * @param s the stream to serialize to.
+    */
+   private void writeObject(java.io.ObjectOutputStream s)
+       throws java.io.IOException {
+       // Must inflate to maintain compatible serial form.
+       if (this.intVal == null)
+           UnsafeHolder.setIntValVolatile(this, BigInteger.valueOf(this.intCompact));
+       // Could reset intVal back to null if it has to be set.
+       s.defaultWriteObject();
+   }
+
+    /**
+     * Returns the length of the absolute value of a {@code long}, in decimal
+     * digits.
+     *
+     * @param x the {@code long}
+     * @return the length of the unscaled value, in deciaml digits.
+     */
+    static int longDigitLength(long x) {
+        /*
+         * As described in "Bit Twiddling Hacks" by Sean Anderson,
+         * (http://graphics.stanford.edu/~seander/bithacks.html)
+         * integer log 10 of x is within 1 of (1233/4096)* (1 +
+         * integer log 2 of x). The fraction 1233/4096 approximates
+         * log10(2). So we first do a version of log2 (a variant of
+         * Long class with pre-checks and opposite directionality) and
+         * then scale and check against powers table. This is a little
+         * simpler in present context than the version in Hacker's
+         * Delight sec 11-4. Adding one to bit length allows comparing
+         * downward from the LONG_TEN_POWERS_TABLE that we need
+         * anyway.
+         */
+        assert x != BigDecimal.INFLATED;
+        if (x < 0)
+            x = -x;
+        if (x < 10) // must screen for 0, might as well 10
+            return 1;
+        int r = ((64 - Long.numberOfLeadingZeros(x) + 1) * 1233) >>> 12;
+        long[] tab = LONG_TEN_POWERS_TABLE;
+        // if r >= length, must have max possible digits for long
+        return (r >= tab.length || x < tab[r]) ? r : r + 1;
+    }
+
+    /**
+     * Returns the length of the absolute value of a BigInteger, in
+     * decimal digits.
+     *
+     * @param b the BigInteger
+     * @return the length of the unscaled value, in decimal digits
+     */
+    private static int bigDigitLength(BigInteger b) {
+        /*
+         * Same idea as the long version, but we need a better
+         * approximation of log10(2). Using 646456993/2^31
+         * is accurate up to max possible reported bitLength.
+         */
+        if (b.signum == 0)
+            return 1;
+        int r = (int)((((long)b.bitLength() + 1) * 646456993) >>> 31);
+        return b.compareMagnitude(bigTenToThe(r)) < 0? r : r+1;
+    }
+
+    /**
+     * Check a scale for Underflow or Overflow.  If this BigDecimal is
+     * nonzero, throw an exception if the scale is outof range. If this
+     * is zero, saturate the scale to the extreme value of the right
+     * sign if the scale is out of range.
+     *
+     * @param val The new scale.
+     * @throws ArithmeticException (overflow or underflow) if the new
+     *         scale is out of range.
+     * @return validated scale as an int.
+     */
+    private int checkScale(long val) {
+        int asInt = (int)val;
+        if (asInt != val) {
+            asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+            BigInteger b;
+            if (intCompact != 0 &&
+                ((b = intVal) == null || b.signum() != 0))
+                throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
+        }
+        return asInt;
+    }
+
+   /**
+     * Returns the compact value for given {@code BigInteger}, or
+     * INFLATED if too big. Relies on internal representation of
+     * {@code BigInteger}.
+     */
+    private static long compactValFor(BigInteger b) {
+        int[] m = b.mag;
+        int len = m.length;
+        if (len == 0)
+            return 0;
+        int d = m[0];
+        if (len > 2 || (len == 2 && d < 0))
+            return INFLATED;
+
+        long u = (len == 2)?
+            (((long) m[1] & LONG_MASK) + (((long)d) << 32)) :
+            (((long)d)   & LONG_MASK);
+        return (b.signum < 0)? -u : u;
+    }
+
+    private static int longCompareMagnitude(long x, long y) {
+        if (x < 0)
+            x = -x;
+        if (y < 0)
+            y = -y;
+        return (x < y) ? -1 : ((x == y) ? 0 : 1);
+    }
+
+    private static int saturateLong(long s) {
+        int i = (int)s;
+        return (s == i) ? i : (s < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE);
+    }
+
+    /*
+     * Internal printing routine
+     */
+    private static void print(String name, BigDecimal bd) {
+        System.err.format("%s:\tintCompact %d\tintVal %d\tscale %d\tprecision %d%n",
+                          name,
+                          bd.intCompact,
+                          bd.intVal,
+                          bd.scale,
+                          bd.precision);
+    }
+
+    /**
+     * Check internal invariants of this BigDecimal.  These invariants
+     * include:
+     *
+     * <ul>
+     *
+     * <li>The object must be initialized; either intCompact must not be
+     * INFLATED or intVal is non-null.  Both of these conditions may
+     * be true.
+     *
+     * <li>If both intCompact and intVal and set, their values must be
+     * consistent.
+     *
+     * <li>If precision is nonzero, it must have the right value.
+     * </ul>
+     *
+     * Note: Since this is an audit method, we are not supposed to change the
+     * state of this BigDecimal object.
+     */
+    private BigDecimal audit() {
+        if (intCompact == INFLATED) {
+            if (intVal == null) {
+                print("audit", this);
+                throw new AssertionError("null intVal");
+            }
+            // Check precision
+            if (precision > 0 && precision != bigDigitLength(intVal)) {
+                print("audit", this);
+                throw new AssertionError("precision mismatch");
+            }
+        } else {
+            if (intVal != null) {
+                long val = intVal.longValue();
+                if (val != intCompact) {
+                    print("audit", this);
+                    throw new AssertionError("Inconsistent state, intCompact=" +
+                                             intCompact + "\t intVal=" + val);
+                }
+            }
+            // Check precision
+            if (precision > 0 && precision != longDigitLength(intCompact)) {
+                print("audit", this);
+                throw new AssertionError("precision mismatch");
+            }
+        }
+        return this;
+    }
+
+    /* the same as checkScale where value!=0 */
+    private static int checkScaleNonZero(long val) {
+        int asInt = (int)val;
+        if (asInt != val) {
+            throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
+        }
+        return asInt;
+    }
+
+    private static int checkScale(long intCompact, long val) {
+        int asInt = (int)val;
+        if (asInt != val) {
+            asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+            if (intCompact != 0)
+                throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
+        }
+        return asInt;
+    }
+
+    private static int checkScale(BigInteger intVal, long val) {
+        int asInt = (int)val;
+        if (asInt != val) {
+            asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+            if (intVal.signum() != 0)
+                throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
+        }
+        return asInt;
+    }
+
+    /**
+     * Returns a {@code BigDecimal} rounded according to the MathContext
+     * settings;
+     * If rounding is needed a new {@code BigDecimal} is created and returned.
+     *
+     * @param val the value to be rounded
+     * @param mc the context to use.
+     * @return a {@code BigDecimal} rounded according to the MathContext
+     *         settings.  May return {@code value}, if no rounding needed.
+     * @throws ArithmeticException if the rounding mode is
+     *         {@code RoundingMode.UNNECESSARY} and the
+     *         result is inexact.
+     */
+    private static BigDecimal doRound(BigDecimal val, MathContext mc) {
+        int mcp = mc.precision;
+        boolean wasDivided = false;
+        if (mcp > 0) {
+            BigInteger intVal = val.intVal;
+            long compactVal = val.intCompact;
+            int scale = val.scale;
+            int prec = val.precision();
+            int mode = mc.roundingMode.oldMode;
+            int drop;
+            if (compactVal == INFLATED) {
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    wasDivided = true;
+                    compactVal = compactValFor(intVal);
+                    if (compactVal != INFLATED) {
+                        prec = longDigitLength(compactVal);
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                drop = prec - mcp;  // drop can't be more than 18
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    wasDivided = true;
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                    intVal = null;
+                }
+            }
+            return wasDivided ? new BigDecimal(intVal,compactVal,scale,prec) : val;
+        }
+        return val;
+    }
+
+    /*
+     * Returns a {@code BigDecimal} created from {@code long} value with
+     * given scale rounded according to the MathContext settings
+     */
+    private static BigDecimal doRound(long compactVal, int scale, MathContext mc) {
+        int mcp = mc.precision;
+        if (mcp > 0 && mcp < 19) {
+            int prec = longDigitLength(compactVal);
+            int drop = prec - mcp;  // drop can't be more than 18
+            while (drop > 0) {
+                scale = checkScaleNonZero((long) scale - drop);
+                compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;
+            }
+            return valueOf(compactVal, scale, prec);
+        }
+        return valueOf(compactVal, scale);
+    }
+
+    /*
+     * Returns a {@code BigDecimal} created from {@code BigInteger} value with
+     * given scale rounded according to the MathContext settings
+     */
+    private static BigDecimal doRound(BigInteger intVal, int scale, MathContext mc) {
+        int mcp = mc.precision;
+        int prec = 0;
+        if (mcp > 0) {
+            long compactVal = compactValFor(intVal);
+            int mode = mc.roundingMode.oldMode;
+            int drop;
+            if (compactVal == INFLATED) {
+                prec = bigDigitLength(intVal);
+                drop = prec - mcp;
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    intVal = divideAndRoundByTenPow(intVal, drop, mode);
+                    compactVal = compactValFor(intVal);
+                    if (compactVal != INFLATED) {
+                        break;
+                    }
+                    prec = bigDigitLength(intVal);
+                    drop = prec - mcp;
+                }
+            }
+            if (compactVal != INFLATED) {
+                prec = longDigitLength(compactVal);
+                drop = prec - mcp;     // drop can't be more than 18
+                while (drop > 0) {
+                    scale = checkScaleNonZero((long) scale - drop);
+                    compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode);
+                    prec = longDigitLength(compactVal);
+                    drop = prec - mcp;
+                }
+                return valueOf(compactVal,scale,prec);
+            }
+        }
+        return new BigDecimal(intVal,INFLATED,scale,prec);
+    }
+
+    /*
+     * Divides {@code BigInteger} value by ten power.
+     */
+    private static BigInteger divideAndRoundByTenPow(BigInteger intVal, int tenPow, int roundingMode) {
+        if (tenPow < LONG_TEN_POWERS_TABLE.length)
+            intVal = divideAndRound(intVal, LONG_TEN_POWERS_TABLE[tenPow], roundingMode);
+        else
+            intVal = divideAndRound(intVal, bigTenToThe(tenPow), roundingMode);
+        return intVal;
+    }
+
+    /**
+     * Internally used for division operation for division {@code long} by
+     * {@code long}.
+     * The returned {@code BigDecimal} object is the quotient whose scale is set
+     * to the passed in scale. If the remainder is not zero, it will be rounded
+     * based on the passed in roundingMode. Also, if the remainder is zero and
+     * the last parameter, i.e. preferredScale is NOT equal to scale, the
+     * trailing zeros of the result is stripped to match the preferredScale.
+     */
+    private static BigDecimal divideAndRound(long ldividend, long ldivisor, int scale, int roundingMode,
+                                             int preferredScale) {
+
+        int qsign; // quotient sign
+        long q = ldividend / ldivisor; // store quotient in long
+        if (roundingMode == ROUND_DOWN && scale == preferredScale)
+            return valueOf(q, scale);
+        long r = ldividend % ldivisor; // store remainder in long
+        qsign = ((ldividend < 0) == (ldivisor < 0)) ? 1 : -1;
+        if (r != 0) {
+            boolean increment = needIncrement(ldivisor, roundingMode, qsign, q, r);
+            return valueOf((increment ? q + qsign : q), scale);
+        } else {
+            if (preferredScale != scale)
+                return createAndStripZerosToMatchScale(q, scale, preferredScale);
+            else
+                return valueOf(q, scale);
+        }
+    }
+
+    /**
+     * Divides {@code long} by {@code long} and do rounding based on the
+     * passed in roundingMode.
+     */
+    private static long divideAndRound(long ldividend, long ldivisor, int roundingMode) {
+        int qsign; // quotient sign
+        long q = ldividend / ldivisor; // store quotient in long
+        if (roundingMode == ROUND_DOWN)
+            return q;
+        long r = ldividend % ldivisor; // store remainder in long
+        qsign = ((ldividend < 0) == (ldivisor < 0)) ? 1 : -1;
+        if (r != 0) {
+            boolean increment = needIncrement(ldivisor, roundingMode, qsign, q,     r);
+            return increment ? q + qsign : q;
+        } else {
+            return q;
+        }
+    }
+
+    /**
+     * Shared logic of need increment computation.
+     */
+    private static boolean commonNeedIncrement(int roundingMode, int qsign,
+                                        int cmpFracHalf, boolean oddQuot) {
+        switch(roundingMode) {
+        case ROUND_UNNECESSARY:
+            throw new ArithmeticException("Rounding necessary");
+
+        case ROUND_UP: // Away from zero
+            return true;
+
+        case ROUND_DOWN: // Towards zero
+            return false;
+
+        case ROUND_CEILING: // Towards +infinity
+            return qsign > 0;
+
+        case ROUND_FLOOR: // Towards -infinity
+            return qsign < 0;
+
+        default: // Some kind of half-way rounding
+            assert roundingMode >= ROUND_HALF_UP &&
+                roundingMode <= ROUND_HALF_EVEN: "Unexpected rounding mode" + RoundingMode.valueOf(roundingMode);
+
+            if (cmpFracHalf < 0 ) // We're closer to higher digit
+                return false;
+            else if (cmpFracHalf > 0 ) // We're closer to lower digit
+                return true;
+            else { // half-way
+                assert cmpFracHalf == 0;
+
+                switch(roundingMode) {
+                case ROUND_HALF_DOWN:
+                    return false;
+
+                case ROUND_HALF_UP:
+                    return true;
+
+                case ROUND_HALF_EVEN:
+                    return oddQuot;
+
+                default:
+                    throw new AssertionError("Unexpected rounding mode" + roundingMode);
+                }
+            }
+        }
+    }
+
+    /**
+     * Tests if quotient has to be incremented according the roundingMode
+     */
+    private static boolean needIncrement(long ldivisor, int roundingMode,
+                                         int qsign, long q, long r) {
+        assert r != 0L;
+
+        int cmpFracHalf;
+        if (r <= HALF_LONG_MIN_VALUE || r > HALF_LONG_MAX_VALUE) {
+            cmpFracHalf = 1; // 2 * r can't fit into long
+        } else {
+            cmpFracHalf = longCompareMagnitude(2 * r, ldivisor);
+        }
+
+        return commonNeedIncrement(roundingMode, qsign, cmpFracHalf, (q & 1L) != 0L);
+    }
+
+    /**
+     * Divides {@code BigInteger} value by {@code long} value and
+     * do rounding based on the passed in roundingMode.
+     */
+    private static BigInteger divideAndRound(BigInteger bdividend, long ldivisor, int roundingMode) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        long r = 0; // store quotient & remainder in long
+        MutableBigInteger mq = null; // store quotient
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        mq = new MutableBigInteger();
+        r = mdividend.divide(ldivisor, mq);
+        isRemainderZero = (r == 0);
+        qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;
+        if (!isRemainderZero) {
+            if(needIncrement(ldivisor, roundingMode, qsign, mq, r)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+        }
+        return mq.toBigInteger(qsign);
+    }
+
+    /**
+     * Internally used for division operation for division {@code BigInteger}
+     * by {@code long}.
+     * The returned {@code BigDecimal} object is the quotient whose scale is set
+     * to the passed in scale. If the remainder is not zero, it will be rounded
+     * based on the passed in roundingMode. Also, if the remainder is zero and
+     * the last parameter, i.e. preferredScale is NOT equal to scale, the
+     * trailing zeros of the result is stripped to match the preferredScale.
+     */
+    private static BigDecimal divideAndRound(BigInteger bdividend,
+                                             long ldivisor, int scale, int roundingMode, int preferredScale) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        long r = 0; // store quotient & remainder in long
+        MutableBigInteger mq = null; // store quotient
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        mq = new MutableBigInteger();
+        r = mdividend.divide(ldivisor, mq);
+        isRemainderZero = (r == 0);
+        qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;
+        if (!isRemainderZero) {
+            if(needIncrement(ldivisor, roundingMode, qsign, mq, r)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+            return mq.toBigDecimal(qsign, scale);
+        } else {
+            if (preferredScale != scale) {
+                long compactVal = mq.toCompactValue(qsign);
+                if(compactVal!=INFLATED) {
+                    return createAndStripZerosToMatchScale(compactVal, scale, preferredScale);
+                }
+                BigInteger intVal =  mq.toBigInteger(qsign);
+                return createAndStripZerosToMatchScale(intVal,scale, preferredScale);
+            } else {
+                return mq.toBigDecimal(qsign, scale);
+            }
+        }
+    }
+
+    /**
+     * Tests if quotient has to be incremented according the roundingMode
+     */
+    private static boolean needIncrement(long ldivisor, int roundingMode,
+                                         int qsign, MutableBigInteger mq, long r) {
+        assert r != 0L;
+
+        int cmpFracHalf;
+        if (r <= HALF_LONG_MIN_VALUE || r > HALF_LONG_MAX_VALUE) {
+            cmpFracHalf = 1; // 2 * r can't fit into long
+        } else {
+            cmpFracHalf = longCompareMagnitude(2 * r, ldivisor);
+        }
+
+        return commonNeedIncrement(roundingMode, qsign, cmpFracHalf, mq.isOdd());
+    }
+
+    /**
+     * Divides {@code BigInteger} value by {@code BigInteger} value and
+     * do rounding based on the passed in roundingMode.
+     */
+    private static BigInteger divideAndRound(BigInteger bdividend, BigInteger bdivisor, int roundingMode) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        MutableBigInteger mq = new MutableBigInteger();
+        MutableBigInteger mdivisor = new MutableBigInteger(bdivisor.mag);
+        MutableBigInteger mr = mdividend.divide(mdivisor, mq);
+        isRemainderZero = mr.isZero();
+        qsign = (bdividend.signum != bdivisor.signum) ? -1 : 1;
+        if (!isRemainderZero) {
+            if (needIncrement(mdivisor, roundingMode, qsign, mq, mr)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+        }
+        return mq.toBigInteger(qsign);
+    }
+
+    /**
+     * Internally used for division operation for division {@code BigInteger}
+     * by {@code BigInteger}.
+     * The returned {@code BigDecimal} object is the quotient whose scale is set
+     * to the passed in scale. If the remainder is not zero, it will be rounded
+     * based on the passed in roundingMode. Also, if the remainder is zero and
+     * the last parameter, i.e. preferredScale is NOT equal to scale, the
+     * trailing zeros of the result is stripped to match the preferredScale.
+     */
+    private static BigDecimal divideAndRound(BigInteger bdividend, BigInteger bdivisor, int scale, int roundingMode,
+                                             int preferredScale) {
+        boolean isRemainderZero; // record remainder is zero or not
+        int qsign; // quotient sign
+        // Descend into mutables for faster remainder checks
+        MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+        MutableBigInteger mq = new MutableBigInteger();
+        MutableBigInteger mdivisor = new MutableBigInteger(bdivisor.mag);
+        MutableBigInteger mr = mdividend.divide(mdivisor, mq);
+        isRemainderZero = mr.isZero();
+        qsign = (bdividend.signum != bdivisor.signum) ? -1 : 1;
+        if (!isRemainderZero) {
+            if (needIncrement(mdivisor, roundingMode, qsign, mq, mr)) {
+                mq.add(MutableBigInteger.ONE);
+            }
+            return mq.toBigDecimal(qsign, scale);
+        } else {
+            if (preferredScale != scale) {
+                long compactVal = mq.toCompactValue(qsign);
+                if (compactVal != INFLATED) {
+                    return createAndStripZerosToMatchScale(compactVal, scale, preferredScale);
+                }
+                BigInteger intVal = mq.toBigInteger(qsign);
+                return createAndStripZerosToMatchScale(intVal, scale, preferredScale);
+            } else {
+                return mq.toBigDecimal(qsign, scale);
+            }
+        }
+    }
+
+    /**
+     * Tests if quotient has to be incremented according the roundingMode
+     */
+    private static boolean needIncrement(MutableBigInteger mdivisor, int roundingMode,
+                                         int qsign, MutableBigInteger mq, MutableBigInteger mr) {
+        assert !mr.isZero();
+        int cmpFracHalf = mr.compareHalf(mdivisor);
+        return commonNeedIncrement(roundingMode, qsign, cmpFracHalf, mq.isOdd());
+    }
+
+    /**
+     * Remove insignificant trailing zeros from this
+     * {@code BigInteger} value until the preferred scale is reached or no
+     * more zeros can be removed.  If the preferred scale is less than
+     * Integer.MIN_VALUE, all the trailing zeros will be removed.
+     *
+     * @return new {@code BigDecimal} with a scale possibly reduced
+     * to be closed to the preferred scale.
+     */
+    private static BigDecimal createAndStripZerosToMatchScale(BigInteger intVal, int scale, long preferredScale) {
+        BigInteger qr[]; // quotient-remainder pair
+        while (intVal.compareMagnitude(BigInteger.TEN) >= 0
+               && scale > preferredScale) {
+            if (intVal.testBit(0))
+                break; // odd number cannot end in 0
+            qr = intVal.divideAndRemainder(BigInteger.TEN);
+            if (qr[1].signum() != 0)
+                break; // non-0 remainder
+            intVal = qr[0];
+            scale = checkScale(intVal,(long) scale - 1); // could Overflow
+        }
+        return valueOf(intVal, scale, 0);
+    }
+
+    /**
+     * Remove insignificant trailing zeros from this
+     * {@code long} value until the preferred scale is reached or no
+     * more zeros can be removed.  If the preferred scale is less than
+     * Integer.MIN_VALUE, all the trailing zeros will be removed.
+     *
+     * @return new {@code BigDecimal} with a scale possibly reduced
+     * to be closed to the preferred scale.
+     */
+    private static BigDecimal createAndStripZerosToMatchScale(long compactVal, int scale, long preferredScale) {
+        while (Math.abs(compactVal) >= 10L && scale > preferredScale) {
+            if ((compactVal & 1L) != 0L)
+                break; // odd number cannot end in 0
+            long r = compactVal % 10L;
+            if (r != 0L)
+                break; // non-0 remainder
+            compactVal /= 10;
+            scale = checkScale(compactVal, (long) scale - 1); // could Overflow
+        }
+        return valueOf(compactVal, scale);
+    }
+
+    private static BigDecimal stripZerosToMatchScale(BigInteger intVal, long intCompact, int scale, int preferredScale) {
+        if(intCompact!=INFLATED) {
+            return createAndStripZerosToMatchScale(intCompact, scale, preferredScale);
+        } else {
+            return createAndStripZerosToMatchScale(intVal==null ? INFLATED_BIGINT : intVal,
+                                                   scale, preferredScale);
+        }
+    }
+
+    /*
+     * returns INFLATED if oveflow
+     */
+    private static long add(long xs, long ys){
+        long sum = xs + ys;
+        // See "Hacker's Delight" section 2-12 for explanation of
+        // the overflow test.
+        if ( (((sum ^ xs) & (sum ^ ys))) >= 0L) { // not overflowed
+            return sum;
+        }
+        return INFLATED;
+    }
+
+    private static BigDecimal add(long xs, long ys, int scale){
+        long sum = add(xs, ys);
+        if (sum!=INFLATED)
+            return BigDecimal.valueOf(sum, scale);
+        return new BigDecimal(BigInteger.valueOf(xs).add(ys), scale);
+    }
+
+    private static BigDecimal add(final long xs, int scale1, final long ys, int scale2) {
+        long sdiff = (long) scale1 - scale2;
+        if (sdiff == 0) {
+            return add(xs, ys, scale1);
+        } else if (sdiff < 0) {
+            int raise = checkScale(xs,-sdiff);
+            long scaledX = longMultiplyPowerTen(xs, raise);
+            if (scaledX != INFLATED) {
+                return add(scaledX, ys, scale2);
+            } else {
+                BigInteger bigsum = bigMultiplyPowerTen(xs,raise).add(ys);
+                return ((xs^ys)>=0) ? // same sign test
+                    new BigDecimal(bigsum, INFLATED, scale2, 0)
+                    : valueOf(bigsum, scale2, 0);
+            }
+        } else {
+            int raise = checkScale(ys,sdiff);
+            long scaledY = longMultiplyPowerTen(ys, raise);
+            if (scaledY != INFLATED) {
+                return add(xs, scaledY, scale1);
+            } else {
+                BigInteger bigsum = bigMultiplyPowerTen(ys,raise).add(xs);
+                return ((xs^ys)>=0) ?
+                    new BigDecimal(bigsum, INFLATED, scale1, 0)
+                    : valueOf(bigsum, scale1, 0);
+            }
+        }
+    }
+
+    private static BigDecimal add(final long xs, int scale1, BigInteger snd, int scale2) {
+        int rscale = scale1;
+        long sdiff = (long)rscale - scale2;
+        boolean sameSigns =  (Long.signum(xs) == snd.signum);
+        BigInteger sum;
+        if (sdiff < 0) {
+            int raise = checkScale(xs,-sdiff);
+            rscale = scale2;
+            long scaledX = longMultiplyPowerTen(xs, raise);
+            if (scaledX == INFLATED) {
+                sum = snd.add(bigMultiplyPowerTen(xs,raise));
+            } else {
+                sum = snd.add(scaledX);
+            }
+        } else { //if (sdiff > 0) {
+            int raise = checkScale(snd,sdiff);
+            snd = bigMultiplyPowerTen(snd,raise);
+            sum = snd.add(xs);
+        }
+        return (sameSigns) ?
+            new BigDecimal(sum, INFLATED, rscale, 0) :
+            valueOf(sum, rscale, 0);
+    }
+
+    private static BigDecimal add(BigInteger fst, int scale1, BigInteger snd, int scale2) {
+        int rscale = scale1;
+        long sdiff = (long)rscale - scale2;
+        if (sdiff != 0) {
+            if (sdiff < 0) {
+                int raise = checkScale(fst,-sdiff);
+                rscale = scale2;
+                fst = bigMultiplyPowerTen(fst,raise);
+            } else {
+                int raise = checkScale(snd,sdiff);
+                snd = bigMultiplyPowerTen(snd,raise);
+            }
+        }
+        BigInteger sum = fst.add(snd);
+        return (fst.signum == snd.signum) ?
+                new BigDecimal(sum, INFLATED, rscale, 0) :
+                valueOf(sum, rscale, 0);
+    }
+
+    private static BigInteger bigMultiplyPowerTen(long value, int n) {
+        if (n <= 0)
+            return BigInteger.valueOf(value);
+        return bigTenToThe(n).multiply(value);
+    }
+
+    private static BigInteger bigMultiplyPowerTen(BigInteger value, int n) {
+        if (n <= 0)
+            return value;
+        if(n<LONG_TEN_POWERS_TABLE.length) {
+                return value.multiply(LONG_TEN_POWERS_TABLE[n]);
+        }
+        return value.multiply(bigTenToThe(n));
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     *
+     * Fast path - used only when (xscale <= yscale && yscale < 18
+     *  && mc.presision<18) {
+     */
+    private static BigDecimal divideSmallFastPath(final long xs, int xscale,
+                                                  final long ys, int yscale,
+                                                  long preferredScale, MathContext mc) {
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        assert (xscale <= yscale) && (yscale < 18) && (mcp < 18);
+        int xraise = yscale - xscale; // xraise >=0
+        long scaledX = (xraise==0) ? xs :
+            longMultiplyPowerTen(xs, xraise); // can't overflow here!
+        BigDecimal quotient;
+
+        int cmp = longCompareMagnitude(scaledX, ys);
+        if(cmp > 0) { // satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+            int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+            if (checkScaleNonZero((long) mcp + yscale - xscale) > 0) {
+                // assert newScale >= xscale
+                int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+                long scaledXs;
+                if ((scaledXs = longMultiplyPowerTen(xs, raise)) == INFLATED) {
+                    quotient = null;
+                    if((mcp-1) >=0 && (mcp-1)<LONG_TEN_POWERS_TABLE.length) {
+                        quotient = multiplyDivideAndRound(LONG_TEN_POWERS_TABLE[mcp-1], scaledX, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                    if(quotient==null) {
+                        BigInteger rb = bigMultiplyPowerTen(scaledX,mcp-1);
+                        quotient = divideAndRound(rb, ys,
+                                                  scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                } else {
+                    quotient = divideAndRound(scaledXs, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                }
+            } else {
+                int newScale = checkScaleNonZero((long) xscale - mcp);
+                // assert newScale >= yscale
+                if (newScale == yscale) { // easy case
+                    quotient = divideAndRound(xs, ys, scl, roundingMode,checkScaleNonZero(preferredScale));
+                } else {
+                    int raise = checkScaleNonZero((long) newScale - yscale);
+                    long scaledYs;
+                    if ((scaledYs = longMultiplyPowerTen(ys, raise)) == INFLATED) {
+                        BigInteger rb = bigMultiplyPowerTen(ys,raise);
+                        quotient = divideAndRound(BigInteger.valueOf(xs),
+                                                  rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+                    } else {
+                        quotient = divideAndRound(xs, scaledYs, scl, roundingMode,checkScaleNonZero(preferredScale));
+                    }
+                }
+            }
+        } else {
+            // abs(scaledX) <= abs(ys)
+            // result is "scaledX * 10^msp / ys"
+            int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+            if(cmp==0) {
+                // abs(scaleX)== abs(ys) => result will be scaled 10^mcp + correct sign
+                quotient = roundedTenPower(((scaledX < 0) == (ys < 0)) ? 1 : -1, mcp, scl, checkScaleNonZero(preferredScale));
+            } else {
+                // abs(scaledX) < abs(ys)
+                long scaledXs;
+                if ((scaledXs = longMultiplyPowerTen(scaledX, mcp)) == INFLATED) {
+                    quotient = null;
+                    if(mcp<LONG_TEN_POWERS_TABLE.length) {
+                        quotient = multiplyDivideAndRound(LONG_TEN_POWERS_TABLE[mcp], scaledX, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                    if(quotient==null) {
+                        BigInteger rb = bigMultiplyPowerTen(scaledX,mcp);
+                        quotient = divideAndRound(rb, ys,
+                                                  scl, roundingMode, checkScaleNonZero(preferredScale));
+                    }
+                } else {
+                    quotient = divideAndRound(scaledXs, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+                }
+            }
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient,mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(final long xs, int xscale, final long ys, int yscale, long preferredScale, MathContext mc) {
+        int mcp = mc.precision;
+        if(xscale <= yscale && yscale < 18 && mcp<18) {
+            return divideSmallFastPath(xs, xscale, ys, yscale, preferredScale, mc);
+        }
+        if (compareMagnitudeNormalized(xs, xscale, ys, yscale) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int roundingMode = mc.roundingMode.oldMode;
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        BigDecimal quotient;
+        if (checkScaleNonZero((long) mcp + yscale - xscale) > 0) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            long scaledXs;
+            if ((scaledXs = longMultiplyPowerTen(xs, raise)) == INFLATED) {
+                BigInteger rb = bigMultiplyPowerTen(xs,raise);
+                quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+            } else {
+                quotient = divideAndRound(scaledXs, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+            }
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            // assert newScale >= yscale
+            if (newScale == yscale) { // easy case
+                quotient = divideAndRound(xs, ys, scl, roundingMode,checkScaleNonZero(preferredScale));
+            } else {
+                int raise = checkScaleNonZero((long) newScale - yscale);
+                long scaledYs;
+                if ((scaledYs = longMultiplyPowerTen(ys, raise)) == INFLATED) {
+                    BigInteger rb = bigMultiplyPowerTen(ys,raise);
+                    quotient = divideAndRound(BigInteger.valueOf(xs),
+                                              rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+                } else {
+                    quotient = divideAndRound(xs, scaledYs, scl, roundingMode,checkScaleNonZero(preferredScale));
+                }
+            }
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient,mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(BigInteger xs, int xscale, long ys, int yscale, long preferredScale, MathContext mc) {
+        // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+        if ((-compareMagnitudeNormalized(ys, yscale, xs, xscale)) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        BigDecimal quotient;
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        if (checkScaleNonZero((long) mcp + yscale - xscale) > 0) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            BigInteger rb = bigMultiplyPowerTen(xs,raise);
+            quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            // assert newScale >= yscale
+            if (newScale == yscale) { // easy case
+                quotient = divideAndRound(xs, ys, scl, roundingMode,checkScaleNonZero(preferredScale));
+            } else {
+                int raise = checkScaleNonZero((long) newScale - yscale);
+                long scaledYs;
+                if ((scaledYs = longMultiplyPowerTen(ys, raise)) == INFLATED) {
+                    BigInteger rb = bigMultiplyPowerTen(ys,raise);
+                    quotient = divideAndRound(xs, rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+                } else {
+                    quotient = divideAndRound(xs, scaledYs, scl, roundingMode,checkScaleNonZero(preferredScale));
+                }
+            }
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient, mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(long xs, int xscale, BigInteger ys, int yscale, long preferredScale, MathContext mc) {
+        // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+        if (compareMagnitudeNormalized(xs, xscale, ys, yscale) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        BigDecimal quotient;
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        if (checkScaleNonZero((long) mcp + yscale - xscale) > 0) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            BigInteger rb = bigMultiplyPowerTen(xs,raise);
+            quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            int raise = checkScaleNonZero((long) newScale - yscale);
+            BigInteger rb = bigMultiplyPowerTen(ys,raise);
+            quotient = divideAndRound(BigInteger.valueOf(xs), rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient, mc);
+    }
+
+    /**
+     * Returns a {@code BigDecimal} whose value is {@code (xs /
+     * ys)}, with rounding according to the context settings.
+     */
+    private static BigDecimal divide(BigInteger xs, int xscale, BigInteger ys, int yscale, long preferredScale, MathContext mc) {
+        // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+        if (compareMagnitudeNormalized(xs, xscale, ys, yscale) > 0) {// satisfy constraint (b)
+            yscale -= 1; // [that is, divisor *= 10]
+        }
+        int mcp = mc.precision;
+        int roundingMode = mc.roundingMode.oldMode;
+
+        // In order to find out whether the divide generates the exact result,
+        // we avoid calling the above divide method. 'quotient' holds the
+        // return BigDecimal object whose scale will be set to 'scl'.
+        BigDecimal quotient;
+        int scl = checkScaleNonZero(preferredScale + yscale - xscale + mcp);
+        if (checkScaleNonZero((long) mcp + yscale - xscale) > 0) {
+            int raise = checkScaleNonZero((long) mcp + yscale - xscale);
+            BigInteger rb = bigMultiplyPowerTen(xs,raise);
+            quotient = divideAndRound(rb, ys, scl, roundingMode, checkScaleNonZero(preferredScale));
+        } else {
+            int newScale = checkScaleNonZero((long) xscale - mcp);
+            int raise = checkScaleNonZero((long) newScale - yscale);
+            BigInteger rb = bigMultiplyPowerTen(ys,raise);
+            quotient = divideAndRound(xs, rb, scl, roundingMode,checkScaleNonZero(preferredScale));
+        }
+        // doRound, here, only affects 1000000000 case.
+        return doRound(quotient, mc);
+    }
+
+    /*
+     * performs divideAndRound for (dividend0*dividend1, divisor)
+     * returns null if quotient can't fit into long value;
+     */
+    private static BigDecimal multiplyDivideAndRound(long dividend0, long dividend1, long divisor, int scale, int roundingMode,
+                                                     int preferredScale) {
+        int qsign = Long.signum(dividend0)*Long.signum(dividend1)*Long.signum(divisor);
+        dividend0 = Math.abs(dividend0);
+        dividend1 = Math.abs(dividend1);
+        divisor = Math.abs(divisor);
+        // multiply dividend0 * dividend1
+        long d0_hi = dividend0 >>> 32;
+        long d0_lo = dividend0 & LONG_MASK;
+        long d1_hi = dividend1 >>> 32;
+        long d1_lo = dividend1 & LONG_MASK;
+        long product = d0_lo * d1_lo;
+        long d0 = product & LONG_MASK;
+        long d1 = product >>> 32;
+        product = d0_hi * d1_lo + d1;
+        d1 = product & LONG_MASK;
+        long d2 = product >>> 32;
+        product = d0_lo * d1_hi + d1;
+        d1 = product & LONG_MASK;
+        d2 += product >>> 32;
+        long d3 = d2>>>32;
+        d2 &= LONG_MASK;
+        product = d0_hi*d1_hi + d2;
+        d2 = product & LONG_MASK;
+        d3 = ((product>>>32) + d3) & LONG_MASK;
+        final long dividendHi = make64(d3,d2);
+        final long dividendLo = make64(d1,d0);
+        // divide
+        return divideAndRound128(dividendHi, dividendLo, divisor, qsign, scale, roundingMode, preferredScale);
+    }
+
+    private static final long DIV_NUM_BASE = (1L<<32); // Number base (32 bits).
+
+    /*
+     * divideAndRound 128-bit value by long divisor.
+     * returns null if quotient can't fit into long value;
+     * Specialized version of Knuth's division
+     */
+    private static BigDecimal divideAndRound128(final long dividendHi, final long dividendLo, long divisor, int sign,
+                                                int scale, int roundingMode, int preferredScale) {
+        if (dividendHi >= divisor) {
+            return null;
+        }
+
+        final int shift = Long.numberOfLeadingZeros(divisor);
+        divisor <<= shift;
+
+        final long v1 = divisor >>> 32;
+        final long v0 = divisor & LONG_MASK;
+
+        long tmp = dividendLo << shift;
+        long u1 = tmp >>> 32;
+        long u0 = tmp & LONG_MASK;
+
+        tmp = (dividendHi << shift) | (dividendLo >>> 64 - shift);
+        long u2 = tmp & LONG_MASK;
+        long q1, r_tmp;
+        if (v1 == 1) {
+            q1 = tmp;
+            r_tmp = 0;
+        } else if (tmp >= 0) {
+            q1 = tmp / v1;
+            r_tmp = tmp - q1 * v1;
+        } else {
+            long[] rq = divRemNegativeLong(tmp, v1);
+            q1 = rq[1];
+            r_tmp = rq[0];
+        }
+
+        while(q1 >= DIV_NUM_BASE || unsignedLongCompare(q1*v0, make64(r_tmp, u1))) {
+            q1--;
+            r_tmp += v1;
+            if (r_tmp >= DIV_NUM_BASE)
+                break;
+        }
+
+        tmp = mulsub(u2,u1,v1,v0,q1);
+        u1 = tmp & LONG_MASK;
+        long q0;
+        if (v1 == 1) {
+            q0 = tmp;
+            r_tmp = 0;
+        } else if (tmp >= 0) {
+            q0 = tmp / v1;
+            r_tmp = tmp - q0 * v1;
+        } else {
+            long[] rq = divRemNegativeLong(tmp, v1);
+            q0 = rq[1];
+            r_tmp = rq[0];
+        }
+
+        while(q0 >= DIV_NUM_BASE || unsignedLongCompare(q0*v0,make64(r_tmp,u0))) {
+            q0--;
+            r_tmp += v1;
+            if (r_tmp >= DIV_NUM_BASE)
+                break;
+        }
+
+        if((int)q1 < 0) {
+            // result (which is positive and unsigned here)
+            // can't fit into long due to sign bit is used for value
+            MutableBigInteger mq = new MutableBigInteger(new int[]{(int)q1, (int)q0});
+            if (roundingMode == ROUND_DOWN && scale == preferredScale) {
+                return mq.toBigDecimal(sign, scale);
+            }
+            long r = mulsub(u1, u0, v1, v0, q0) >>> shift;
+            if (r != 0) {
+                if(needIncrement(divisor >>> shift, roundingMode, sign, mq, r)){
+                    mq.add(MutableBigInteger.ONE);
+                }
+                return mq.toBigDecimal(sign, scale);
+            } else {
+                if (preferredScale != scale) {
+                    BigInteger intVal =  mq.toBigInteger(sign);
+                    return createAndStripZerosToMatchScale(intVal,scale, preferredScale);
+                } else {
+                    return mq.toBigDecimal(sign, scale);
+                }
+            }
+        }
+
+        long q = make64(q1,q0);
+        q*=sign;
+
+        if (roundingMode == ROUND_DOWN && scale == preferredScale)
+            return valueOf(q, scale);
+
+        long r = mulsub(u1, u0, v1, v0, q0) >>> shift;
+        if (r != 0) {
+            boolean increment = needIncrement(divisor >>> shift, roundingMode, sign, q, r);
+            return valueOf((increment ? q + sign : q), scale);
+        } else {
+            if (preferredScale != scale) {
+                return createAndStripZerosToMatchScale(q, scale, preferredScale);
+            } else {
+                return valueOf(q, scale);
+            }
+        }
+    }
+
+    /*
+     * calculate divideAndRound for ldividend*10^raise / divisor
+     * when abs(dividend)==abs(divisor);
+     */
+    private static BigDecimal roundedTenPower(int qsign, int raise, int scale, int preferredScale) {
+        if (scale > preferredScale) {
+            int diff = scale - preferredScale;
+            if(diff < raise) {
+                return scaledTenPow(raise - diff, qsign, preferredScale);
+            } else {
+                return valueOf(qsign,scale-raise);
+            }
+        } else {
+            return scaledTenPow(raise, qsign, scale);
+        }
+    }
+
+    static BigDecimal scaledTenPow(int n, int sign, int scale) {
+        if (n < LONG_TEN_POWERS_TABLE.length)
+            return valueOf(sign*LONG_TEN_POWERS_TABLE[n],scale);
+        else {
+            BigInteger unscaledVal = bigTenToThe(n);
+            if(sign==-1) {
+                unscaledVal = unscaledVal.negate();
+            }
+            return new BigDecimal(unscaledVal, INFLATED, scale, n+1);
+        }
+    }
+
+    /**
+     * Calculate the quotient and remainder of dividing a negative long by
+     * another long.
+     *
+     * @param n the numerator; must be negative
+     * @param d the denominator; must not be unity
+     * @return a two-element {@long} array with the remainder and quotient in
+     *         the initial and final elements, respectively
+     */
+    private static long[] divRemNegativeLong(long n, long d) {
+        assert n < 0 : "Non-negative numerator " + n;
+        assert d != 1 : "Unity denominator";
+
+        // Approximate the quotient and remainder
+        long q = (n >>> 1) / (d >>> 1);
+        long r = n - q * d;
+
+        // Correct the approximation
+        while (r < 0) {
+            r += d;
+            q--;
+        }
+        while (r >= d) {
+            r -= d;
+            q++;
+        }
+
+        // n - q*d == r && 0 <= r < d, hence we're done.
+        return new long[] {r, q};
+    }
+
+    private static long make64(long hi, long lo) {
+        return hi<<32 | lo;
+    }
+
+    private static long mulsub(long u1, long u0, final long v1, final long v0, long q0) {
+        long tmp = u0 - q0*v0;
+        return make64(u1 + (tmp>>>32) - q0*v1,tmp & LONG_MASK);
+    }
+
+    private static boolean unsignedLongCompare(long one, long two) {
+        return (one+Long.MIN_VALUE) > (two+Long.MIN_VALUE);
+    }
+
+    private static boolean unsignedLongCompareEq(long one, long two) {
+        return (one+Long.MIN_VALUE) >= (two+Long.MIN_VALUE);
+    }
+
+
+    // Compare Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+    private static int compareMagnitudeNormalized(long xs, int xscale, long ys, int yscale) {
+        // assert xs!=0 && ys!=0
+        int sdiff = xscale - yscale;
+        if (sdiff != 0) {
+            if (sdiff < 0) {
+                xs = longMultiplyPowerTen(xs, -sdiff);
+            } else { // sdiff > 0
+                ys = longMultiplyPowerTen(ys, sdiff);
+            }
+        }
+        if (xs != INFLATED)
+            return (ys != INFLATED) ? longCompareMagnitude(xs, ys) : -1;
+        else
+            return 1;
+    }
+
+    // Compare Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+    private static int compareMagnitudeNormalized(long xs, int xscale, BigInteger ys, int yscale) {
+        // assert "ys can't be represented as long"
+        if (xs == 0)
+            return -1;
+        int sdiff = xscale - yscale;
+        if (sdiff < 0) {
+            if (longMultiplyPowerTen(xs, -sdiff) == INFLATED ) {
+                return bigMultiplyPowerTen(xs, -sdiff).compareMagnitude(ys);
+            }
+        }
+        return -1;
+    }
+
+    // Compare Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+    private static int compareMagnitudeNormalized(BigInteger xs, int xscale, BigInteger ys, int yscale) {
+        int sdiff = xscale - yscale;
+        if (sdiff < 0) {
+            return bigMultiplyPowerTen(xs, -sdiff).compareMagnitude(ys);
+        } else { // sdiff >= 0
+            return xs.compareMagnitude(bigMultiplyPowerTen(ys, sdiff));
+        }
+    }
+
+    private static long multiply(long x, long y){
+                long product = x * y;
+        long ax = Math.abs(x);
+        long ay = Math.abs(y);
+        if (((ax | ay) >>> 31 == 0) || (y == 0) || (product / y == x)){
+                        return product;
+                }
+        return INFLATED;
+    }
+
+    private static BigDecimal multiply(long x, long y, int scale) {
+        long product = multiply(x, y);
+        if(product!=INFLATED) {
+            return valueOf(product,scale);
+        }
+        return new BigDecimal(BigInteger.valueOf(x).multiply(y),INFLATED,scale,0);
+    }
+
+    private static BigDecimal multiply(long x, BigInteger y, int scale) {
+        if(x==0) {
+            return zeroValueOf(scale);
+        }
+        return new BigDecimal(y.multiply(x),INFLATED,scale,0);
+    }
+
+    private static BigDecimal multiply(BigInteger x, BigInteger y, int scale) {
+        return new BigDecimal(x.multiply(y),INFLATED,scale,0);
+    }
+
+    /**
+     * Multiplies two long values and rounds according {@code MathContext}
+     */
+    private static BigDecimal multiplyAndRound(long x, long y, int scale, MathContext mc) {
+        long product = multiply(x, y);
+        if(product!=INFLATED) {
+            return doRound(product, scale, mc);
+        }
+        // attempt to do it in 128 bits
+        int rsign = 1;
+        if(x < 0) {
+            x = -x;
+            rsign = -1;
+        }
+        if(y < 0) {
+            y = -y;
+            rsign *= -1;
+        }
+        // multiply dividend0 * dividend1
+        long m0_hi = x >>> 32;
+        long m0_lo = x & LONG_MASK;
+        long m1_hi = y >>> 32;
+        long m1_lo = y & LONG_MASK;
+        product = m0_lo * m1_lo;
+        long m0 = product & LONG_MASK;
+        long m1 = product >>> 32;
+        product = m0_hi * m1_lo + m1;
+        m1 = product & LONG_MASK;
+        long m2 = product >>> 32;
+        product = m0_lo * m1_hi + m1;
+        m1 = product & LONG_MASK;
+        m2 += product >>> 32;
+        long m3 = m2>>>32;
+        m2 &= LONG_MASK;
+        product = m0_hi*m1_hi + m2;
+        m2 = product & LONG_MASK;
+        m3 = ((product>>>32) + m3) & LONG_MASK;
+        final long mHi = make64(m3,m2);
+        final long mLo = make64(m1,m0);
+        BigDecimal res = doRound128(mHi, mLo, rsign, scale, mc);
+        if(res!=null) {
+            return res;
+        }
+        res = new BigDecimal(BigInteger.valueOf(x).multiply(y*rsign), INFLATED, scale, 0);
+        return doRound(res,mc);
+    }
+
+    private static BigDecimal multiplyAndRound(long x, BigInteger y, int scale, MathContext mc) {
+        if(x==0) {
+            return zeroValueOf(scale);
+        }
+        return doRound(y.multiply(x), scale, mc);
+    }
+
+    private static BigDecimal multiplyAndRound(BigInteger x, BigInteger y, int scale, MathContext mc) {
+        return doRound(x.multiply(y), scale, mc);
+    }
+
+    /**
+     * rounds 128-bit value according {@code MathContext}
+     * returns null if result can't be repsented as compact BigDecimal.
+     */
+    private static BigDecimal doRound128(long hi, long lo, int sign, int scale, MathContext mc) {
+        int mcp = mc.precision;
+        int drop;
+        BigDecimal res = null;
+        if(((drop = precision(hi, lo) - mcp) > 0)&&(drop<LONG_TEN_POWERS_TABLE.length)) {
+            scale = checkScaleNonZero((long)scale - drop);
+            res = divideAndRound128(hi, lo, LONG_TEN_POWERS_TABLE[drop], sign, scale, mc.roundingMode.oldMode, scale);
+        }
+        if(res!=null) {
+            return doRound(res,mc);
+        }
+        return null;
+    }
+
+    private static final long[][] LONGLONG_TEN_POWERS_TABLE = {
+        {   0L, 0x8AC7230489E80000L },  //10^19
+        {       0x5L, 0x6bc75e2d63100000L },  //10^20
+        {       0x36L, 0x35c9adc5dea00000L },  //10^21
+        {       0x21eL, 0x19e0c9bab2400000L  },  //10^22
+        {       0x152dL, 0x02c7e14af6800000L  },  //10^23
+        {       0xd3c2L, 0x1bcecceda1000000L  },  //10^24
+        {       0x84595L, 0x161401484a000000L  },  //10^25
+        {       0x52b7d2L, 0xdcc80cd2e4000000L  },  //10^26
+        {       0x33b2e3cL, 0x9fd0803ce8000000L  },  //10^27
+        {       0x204fce5eL, 0x3e25026110000000L  },  //10^28
+        {       0x1431e0faeL, 0x6d7217caa0000000L  },  //10^29
+        {       0xc9f2c9cd0L, 0x4674edea40000000L  },  //10^30
+        {       0x7e37be2022L, 0xc0914b2680000000L  },  //10^31
+        {       0x4ee2d6d415bL, 0x85acef8100000000L  },  //10^32
+        {       0x314dc6448d93L, 0x38c15b0a00000000L  },  //10^33
+        {       0x1ed09bead87c0L, 0x378d8e6400000000L  },  //10^34
+        {       0x13426172c74d82L, 0x2b878fe800000000L  },  //10^35
+        {       0xc097ce7bc90715L, 0xb34b9f1000000000L  },  //10^36
+        {       0x785ee10d5da46d9L, 0x00f436a000000000L  },  //10^37
+        {       0x4b3b4ca85a86c47aL, 0x098a224000000000L  },  //10^38
+    };
+
+    /*
+     * returns precision of 128-bit value
+     */
+    private static int precision(long hi, long lo){
+        if(hi==0) {
+            if(lo>=0) {
+                return longDigitLength(lo);
+            }
+            return (unsignedLongCompareEq(lo, LONGLONG_TEN_POWERS_TABLE[0][1])) ? 20 : 19;
+            // 0x8AC7230489E80000L  = unsigned 2^19
+        }
+        int r = ((128 - Long.numberOfLeadingZeros(hi) + 1) * 1233) >>> 12;
+        int idx = r-19;
+        return (idx >= LONGLONG_TEN_POWERS_TABLE.length || longLongCompareMagnitude(hi, lo,
+                                                                                    LONGLONG_TEN_POWERS_TABLE[idx][0], LONGLONG_TEN_POWERS_TABLE[idx][1])) ? r : r + 1;
+    }
+
+    /*
+     * returns true if 128 bit number <hi0,lo0> is less than <hi1,lo1>
+     * hi0 & hi1 should be non-negative
+     */
+    private static boolean longLongCompareMagnitude(long hi0, long lo0, long hi1, long lo1) {
+        if(hi0!=hi1) {
+            return hi0<hi1;
+        }
+        return (lo0+Long.MIN_VALUE) <(lo1+Long.MIN_VALUE);
+    }
+
+    private static BigDecimal divide(long dividend, int dividendScale, long divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            if(raise<LONG_TEN_POWERS_TABLE.length) {
+                long xs = dividend;
+                if ((xs = longMultiplyPowerTen(xs, raise)) != INFLATED) {
+                    return divideAndRound(xs, divisor, scale, roundingMode, scale);
+                }
+                BigDecimal q = multiplyDivideAndRound(LONG_TEN_POWERS_TABLE[raise], dividend, divisor, scale, roundingMode, scale);
+                if(q!=null) {
+                    return q;
+                }
+            }
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            if(raise<LONG_TEN_POWERS_TABLE.length) {
+                long ys = divisor;
+                if ((ys = longMultiplyPowerTen(ys, raise)) != INFLATED) {
+                    return divideAndRound(dividend, ys, scale, roundingMode, scale);
+                }
+            }
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(BigInteger.valueOf(dividend), scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
+    private static BigDecimal divide(BigInteger dividend, int dividendScale, long divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            if(raise<LONG_TEN_POWERS_TABLE.length) {
+                long ys = divisor;
+                if ((ys = longMultiplyPowerTen(ys, raise)) != INFLATED) {
+                    return divideAndRound(dividend, ys, scale, roundingMode, scale);
+                }
+            }
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(dividend, scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
+    private static BigDecimal divide(long dividend, int dividendScale, BigInteger divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(BigInteger.valueOf(dividend), scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
+    private static BigDecimal divide(BigInteger dividend, int dividendScale, BigInteger divisor, int divisorScale, int scale, int roundingMode) {
+        if (checkScale(dividend,(long)scale + divisorScale) > dividendScale) {
+            int newScale = scale + divisorScale;
+            int raise = newScale - dividendScale;
+            BigInteger scaledDividend = bigMultiplyPowerTen(dividend, raise);
+            return divideAndRound(scaledDividend, divisor, scale, roundingMode, scale);
+        } else {
+            int newScale = checkScale(divisor,(long)dividendScale - scale);
+            int raise = newScale - divisorScale;
+            BigInteger scaledDivisor = bigMultiplyPowerTen(divisor, raise);
+            return divideAndRound(dividend, scaledDivisor, scale, roundingMode, scale);
+        }
+    }
+
+}
diff --git a/ojluni/src/main/java/java/math/BigInteger.java b/ojluni/src/main/java/java/math/BigInteger.java
new file mode 100644
index 0000000..b20c5ec
--- /dev/null
+++ b/ojluni/src/main/java/java/math/BigInteger.java
@@ -0,0 +1,4812 @@
+/*
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Portions Copyright (c) 1995  Colin Plumb.  All rights reserved.
+ */
+
+package java.math;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import libcore.math.NativeBN;
+import sun.misc.DoubleConsts;
+import sun.misc.FloatConsts;
+import libcore.util.NonNull;
+
+/**
+ * Immutable arbitrary-precision integers.  All operations behave as if
+ * BigIntegers were represented in two's-complement notation (like Java's
+ * primitive integer types).  BigInteger provides analogues to all of Java's
+ * primitive integer operators, and all relevant methods from java.lang.Math.
+ * Additionally, BigInteger provides operations for modular arithmetic, GCD
+ * calculation, primality testing, prime generation, bit manipulation,
+ * and a few other miscellaneous operations.
+ *
+ * <p>Semantics of arithmetic operations exactly mimic those of Java's integer
+ * arithmetic operators, as defined in <i>The Java Language Specification</i>.
+ * For example, division by zero throws an {@code ArithmeticException}, and
+ * division of a negative by a positive yields a negative (or zero) remainder.
+ * All of the details in the Spec concerning overflow are ignored, as
+ * BigIntegers are made as large as necessary to accommodate the results of an
+ * operation.
+ *
+ * <p>Semantics of shift operations extend those of Java's shift operators
+ * to allow for negative shift distances.  A right-shift with a negative
+ * shift distance results in a left shift, and vice-versa.  The unsigned
+ * right shift operator ({@code >>>}) is omitted, as this operation makes
+ * little sense in combination with the "infinite word size" abstraction
+ * provided by this class.
+ *
+ * <p>Semantics of bitwise logical operations exactly mimic those of Java's
+ * bitwise integer operators.  The binary operators ({@code and},
+ * {@code or}, {@code xor}) implicitly perform sign extension on the shorter
+ * of the two operands prior to performing the operation.
+ *
+ * <p>Comparison operations perform signed integer comparisons, analogous to
+ * those performed by Java's relational and equality operators.
+ *
+ * <p>Modular arithmetic operations are provided to compute residues, perform
+ * exponentiation, and compute multiplicative inverses.  These methods always
+ * return a non-negative result, between {@code 0} and {@code (modulus - 1)},
+ * inclusive.
+ *
+ * <p>Bit operations operate on a single bit of the two's-complement
+ * representation of their operand.  If necessary, the operand is sign-
+ * extended so that it contains the designated bit.  None of the single-bit
+ * operations can produce a BigInteger with a different sign from the
+ * BigInteger being operated on, as they affect only a single bit, and the
+ * "infinite word size" abstraction provided by this class ensures that there
+ * are infinitely many "virtual sign bits" preceding each BigInteger.
+ *
+ * <p>For the sake of brevity and clarity, pseudo-code is used throughout the
+ * descriptions of BigInteger methods.  The pseudo-code expression
+ * {@code (i + j)} is shorthand for "a BigInteger whose value is
+ * that of the BigInteger {@code i} plus that of the BigInteger {@code j}."
+ * The pseudo-code expression {@code (i == j)} is shorthand for
+ * "{@code true} if and only if the BigInteger {@code i} represents the same
+ * value as the BigInteger {@code j}."  Other pseudo-code expressions are
+ * interpreted similarly.
+ *
+ * <p>All methods and constructors in this class throw
+ * {@code NullPointerException} when passed
+ * a null object reference for any input parameter.
+ *
+ * BigInteger must support values in the range
+ * -2<sup>{@code Integer.MAX_VALUE}</sup> (exclusive) to
+ * +2<sup>{@code Integer.MAX_VALUE}</sup> (exclusive)
+ * and may support values outside of that range.
+ *
+ * The range of probable prime values is limited and may be less than
+ * the full supported positive range of {@code BigInteger}.
+ * The range must be at least 1 to 2<sup>500000000</sup>.
+ *
+ * @implNote
+ * BigInteger constructors and operations throw {@code ArithmeticException} when
+ * the result is out of the supported range of
+ * -2<sup>{@code Integer.MAX_VALUE}</sup> (exclusive) to
+ * +2<sup>{@code Integer.MAX_VALUE}</sup> (exclusive).
+ *
+ * @see     BigDecimal
+ * @author  Josh Bloch
+ * @author  Michael McCloskey
+ * @author  Alan Eliasen
+ * @author  Timothy Buktu
+ * @since JDK1.1
+ */
+
+public class BigInteger extends Number implements Comparable<BigInteger> {
+    // Android-changed: Added @NonNull annotations.
+
+    /**
+     * The signum of this BigInteger: -1 for negative, 0 for zero, or
+     * 1 for positive.  Note that the BigInteger zero <i>must</i> have
+     * a signum of 0.  This is necessary to ensures that there is exactly one
+     * representation for each BigInteger value.
+     *
+     * @serial
+     */
+    final int signum;
+
+    /**
+     * The magnitude of this BigInteger, in <i>big-endian</i> order: the
+     * zeroth element of this array is the most-significant int of the
+     * magnitude.  The magnitude must be "minimal" in that the most-significant
+     * int ({@code mag[0]}) must be non-zero.  This is necessary to
+     * ensure that there is exactly one representation for each BigInteger
+     * value.  Note that this implies that the BigInteger zero has a
+     * zero-length mag array.
+     */
+    final int[] mag;
+
+    // These "redundant fields" are initialized with recognizable nonsense
+    // values, and cached the first time they are needed (or never, if they
+    // aren't needed).
+
+     /**
+     * One plus the bitCount of this BigInteger. Zeros means uninitialized.
+     *
+     * @serial
+     * @see #bitCount
+     * @deprecated Deprecated since logical value is offset from stored
+     * value and correction factor is applied in accessor method.
+     */
+    @Deprecated
+    private int bitCount;
+
+    /**
+     * One plus the bitLength of this BigInteger. Zeros means uninitialized.
+     * (either value is acceptable).
+     *
+     * @serial
+     * @see #bitLength()
+     * @deprecated Deprecated since logical value is offset from stored
+     * value and correction factor is applied in accessor method.
+     */
+    @Deprecated
+    private int bitLength;
+
+    /**
+     * Two plus the lowest set bit of this BigInteger, as returned by
+     * getLowestSetBit().
+     *
+     * @serial
+     * @see #getLowestSetBit
+     * @deprecated Deprecated since logical value is offset from stored
+     * value and correction factor is applied in accessor method.
+     */
+    @Deprecated
+    private int lowestSetBit;
+
+    /**
+     * Two plus the index of the lowest-order int in the magnitude of this
+     * BigInteger that contains a nonzero int, or -2 (either value is acceptable).
+     * The least significant int has int-number 0, the next int in order of
+     * increasing significance has int-number 1, and so forth.
+     * @deprecated Deprecated since logical value is offset from stored
+     * value and correction factor is applied in accessor method.
+     */
+    @Deprecated
+    private int firstNonzeroIntNum;
+
+    /**
+     * This mask is used to obtain the value of an int as if it were unsigned.
+     */
+    final static long LONG_MASK = 0xffffffffL;
+
+    /**
+     * This constant limits {@code mag.length} of BigIntegers to the supported
+     * range.
+     */
+    private static final int MAX_MAG_LENGTH = Integer.MAX_VALUE / Integer.SIZE + 1; // (1 << 26)
+
+    /**
+     * Bit lengths larger than this constant can cause overflow in searchLen
+     * calculation and in BitSieve.singleSearch method.
+     */
+    private static final  int PRIME_SEARCH_BIT_LENGTH_LIMIT = 500000000;
+
+    /**
+     * The threshold value for using Karatsuba multiplication.  If the number
+     * of ints in both mag arrays are greater than this number, then
+     * Karatsuba multiplication will be used.   This value is found
+     * experimentally to work well.
+     */
+    private static final int KARATSUBA_THRESHOLD = 80;
+
+    /**
+     * The threshold value for using 3-way Toom-Cook multiplication.
+     * If the number of ints in each mag array is greater than the
+     * Karatsuba threshold, and the number of ints in at least one of
+     * the mag arrays is greater than this threshold, then Toom-Cook
+     * multiplication will be used.
+     */
+    private static final int TOOM_COOK_THRESHOLD = 240;
+
+    /**
+     * The threshold value for using Karatsuba squaring.  If the number
+     * of ints in the number are larger than this value,
+     * Karatsuba squaring will be used.   This value is found
+     * experimentally to work well.
+     */
+    private static final int KARATSUBA_SQUARE_THRESHOLD = 128;
+
+    /**
+     * The threshold value for using Toom-Cook squaring.  If the number
+     * of ints in the number are larger than this value,
+     * Toom-Cook squaring will be used.   This value is found
+     * experimentally to work well.
+     */
+    private static final int TOOM_COOK_SQUARE_THRESHOLD = 216;
+
+    /**
+     * The threshold value for using Burnikel-Ziegler division.  If the number
+     * of ints in the divisor are larger than this value, Burnikel-Ziegler
+     * division may be used.  This value is found experimentally to work well.
+     */
+    static final int BURNIKEL_ZIEGLER_THRESHOLD = 80;
+
+    /**
+     * The offset value for using Burnikel-Ziegler division.  If the number
+     * of ints in the divisor exceeds the Burnikel-Ziegler threshold, and the
+     * number of ints in the dividend is greater than the number of ints in the
+     * divisor plus this value, Burnikel-Ziegler division will be used.  This
+     * value is found experimentally to work well.
+     */
+    static final int BURNIKEL_ZIEGLER_OFFSET = 40;
+
+    /**
+     * The threshold value for using Schoenhage recursive base conversion. If
+     * the number of ints in the number are larger than this value,
+     * the Schoenhage algorithm will be used.  In practice, it appears that the
+     * Schoenhage routine is faster for any threshold down to 2, and is
+     * relatively flat for thresholds between 2-25, so this choice may be
+     * varied within this range for very small effect.
+     */
+    private static final int SCHOENHAGE_BASE_CONVERSION_THRESHOLD = 20;
+
+    /**
+     * The threshold value for using squaring code to perform multiplication
+     * of a {@code BigInteger} instance by itself.  If the number of ints in
+     * the number are larger than this value, {@code multiply(this)} will
+     * return {@code square()}.
+     */
+    private static final int MULTIPLY_SQUARE_THRESHOLD = 20;
+
+    /**
+     * The threshold for using an intrinsic version of
+     * implMontgomeryXXX to perform Montgomery multiplication.  If the
+     * number of ints in the number is more than this value we do not
+     * use the intrinsic.
+     */
+    private static final int MONTGOMERY_INTRINSIC_THRESHOLD = 512;
+
+
+    // Constructors
+
+    /**
+     * Translates a byte array containing the two's-complement binary
+     * representation of a BigInteger into a BigInteger.  The input array is
+     * assumed to be in <i>big-endian</i> byte-order: the most significant
+     * byte is in the zeroth element.
+     *
+     * @param  val big-endian two's-complement binary representation of
+     *         BigInteger.
+     * @throws NumberFormatException {@code val} is zero bytes long.
+     */
+    public BigInteger(byte[] val) {
+        if (val.length == 0)
+            throw new NumberFormatException("Zero length BigInteger");
+
+        if (val[0] < 0) {
+            mag = makePositive(val);
+            signum = -1;
+        } else {
+            mag = stripLeadingZeroBytes(val);
+            signum = (mag.length == 0 ? 0 : 1);
+        }
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    /**
+     * This private constructor translates an int array containing the
+     * two's-complement binary representation of a BigInteger into a
+     * BigInteger. The input array is assumed to be in <i>big-endian</i>
+     * int-order: the most significant int is in the zeroth element.
+     */
+    private BigInteger(int[] val) {
+        if (val.length == 0)
+            throw new NumberFormatException("Zero length BigInteger");
+
+        if (val[0] < 0) {
+            mag = makePositive(val);
+            signum = -1;
+        } else {
+            mag = trustedStripLeadingZeroInts(val);
+            signum = (mag.length == 0 ? 0 : 1);
+        }
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    /**
+     * Translates the sign-magnitude representation of a BigInteger into a
+     * BigInteger.  The sign is represented as an integer signum value: -1 for
+     * negative, 0 for zero, or 1 for positive.  The magnitude is a byte array
+     * in <i>big-endian</i> byte-order: the most significant byte is in the
+     * zeroth element.  A zero-length magnitude array is permissible, and will
+     * result in a BigInteger value of 0, whether signum is -1, 0 or 1.
+     *
+     * @param  signum signum of the number (-1 for negative, 0 for zero, 1
+     *         for positive).
+     * @param  magnitude big-endian binary representation of the magnitude of
+     *         the number.
+     * @throws NumberFormatException {@code signum} is not one of the three
+     *         legal values (-1, 0, and 1), or {@code signum} is 0 and
+     *         {@code magnitude} contains one or more non-zero bytes.
+     */
+    public BigInteger(int signum, byte[] magnitude) {
+        this.mag = stripLeadingZeroBytes(magnitude);
+
+        if (signum < -1 || signum > 1)
+            throw(new NumberFormatException("Invalid signum value"));
+
+        if (this.mag.length == 0) {
+            this.signum = 0;
+        } else {
+            if (signum == 0)
+                throw(new NumberFormatException("signum-magnitude mismatch"));
+            this.signum = signum;
+        }
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    /**
+     * A constructor for internal use that translates the sign-magnitude
+     * representation of a BigInteger into a BigInteger. It checks the
+     * arguments and copies the magnitude so this constructor would be
+     * safe for external use.
+     */
+    private BigInteger(int signum, int[] magnitude) {
+        this.mag = stripLeadingZeroInts(magnitude);
+
+        if (signum < -1 || signum > 1)
+            throw(new NumberFormatException("Invalid signum value"));
+
+        if (this.mag.length == 0) {
+            this.signum = 0;
+        } else {
+            if (signum == 0)
+                throw(new NumberFormatException("signum-magnitude mismatch"));
+            this.signum = signum;
+        }
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    /**
+     * Translates the String representation of a BigInteger in the
+     * specified radix into a BigInteger.  The String representation
+     * consists of an optional minus or plus sign followed by a
+     * sequence of one or more digits in the specified radix.  The
+     * character-to-digit mapping is provided by {@code
+     * Character.digit}.  The String may not contain any extraneous
+     * characters (whitespace, for example).
+     *
+     * @param val String representation of BigInteger.
+     * @param radix radix to be used in interpreting {@code val}.
+     * @throws NumberFormatException {@code val} is not a valid representation
+     *         of a BigInteger in the specified radix, or {@code radix} is
+     *         outside the range from {@link Character#MIN_RADIX} to
+     *         {@link Character#MAX_RADIX}, inclusive.
+     * @see    Character#digit
+     */
+    public BigInteger(@NonNull String val, int radix) {
+        int cursor = 0, numDigits;
+        final int len = val.length();
+
+        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
+            throw new NumberFormatException("Radix out of range");
+        if (len == 0)
+            throw new NumberFormatException("Zero length BigInteger");
+
+        // Check for at most one leading sign
+        int sign = 1;
+        int index1 = val.lastIndexOf('-');
+        int index2 = val.lastIndexOf('+');
+        if (index1 >= 0) {
+            if (index1 != 0 || index2 >= 0) {
+                throw new NumberFormatException("Illegal embedded sign character");
+            }
+            sign = -1;
+            cursor = 1;
+        } else if (index2 >= 0) {
+            if (index2 != 0) {
+                throw new NumberFormatException("Illegal embedded sign character");
+            }
+            cursor = 1;
+        }
+        if (cursor == len)
+            throw new NumberFormatException("Zero length BigInteger");
+
+        // Skip leading zeros and compute number of digits in magnitude
+        while (cursor < len &&
+               Character.digit(val.charAt(cursor), radix) == 0) {
+            cursor++;
+        }
+
+        if (cursor == len) {
+            signum = 0;
+            mag = ZERO.mag;
+            return;
+        }
+
+        numDigits = len - cursor;
+        signum = sign;
+
+        // Pre-allocate array of expected size. May be too large but can
+        // never be too small. Typically exact.
+        long numBits = ((numDigits * bitsPerDigit[radix]) >>> 10) + 1;
+        if (numBits + 31 >= (1L << 32)) {
+            reportOverflow();
+        }
+        int numWords = (int) (numBits + 31) >>> 5;
+        int[] magnitude = new int[numWords];
+
+        // Process first (potentially short) digit group
+        int firstGroupLen = numDigits % digitsPerInt[radix];
+        if (firstGroupLen == 0)
+            firstGroupLen = digitsPerInt[radix];
+        String group = val.substring(cursor, cursor += firstGroupLen);
+        magnitude[numWords - 1] = Integer.parseInt(group, radix);
+        if (magnitude[numWords - 1] < 0)
+            throw new NumberFormatException("Illegal digit");
+
+        // Process remaining digit groups
+        int superRadix = intRadix[radix];
+        int groupVal = 0;
+        while (cursor < len) {
+            group = val.substring(cursor, cursor += digitsPerInt[radix]);
+            groupVal = Integer.parseInt(group, radix);
+            if (groupVal < 0)
+                throw new NumberFormatException("Illegal digit");
+            destructiveMulAdd(magnitude, superRadix, groupVal);
+        }
+        // Required for cases where the array was overallocated.
+        mag = trustedStripLeadingZeroInts(magnitude);
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    /*
+     * Constructs a new BigInteger using a char array with radix=10.
+     * Sign is precalculated outside and not allowed in the val.
+     */
+    BigInteger(char[] val, int sign, int len) {
+        int cursor = 0, numDigits;
+
+        // Skip leading zeros and compute number of digits in magnitude
+        while (cursor < len && Character.digit(val[cursor], 10) == 0) {
+            cursor++;
+        }
+        if (cursor == len) {
+            signum = 0;
+            mag = ZERO.mag;
+            return;
+        }
+
+        numDigits = len - cursor;
+        signum = sign;
+        // Pre-allocate array of expected size
+        int numWords;
+        if (len < 10) {
+            numWords = 1;
+        } else {
+            long numBits = ((numDigits * bitsPerDigit[10]) >>> 10) + 1;
+            if (numBits + 31 >= (1L << 32)) {
+                reportOverflow();
+            }
+            numWords = (int) (numBits + 31) >>> 5;
+        }
+        int[] magnitude = new int[numWords];
+
+        // Process first (potentially short) digit group
+        int firstGroupLen = numDigits % digitsPerInt[10];
+        if (firstGroupLen == 0)
+            firstGroupLen = digitsPerInt[10];
+        magnitude[numWords - 1] = parseInt(val, cursor,  cursor += firstGroupLen);
+
+        // Process remaining digit groups
+        while (cursor < len) {
+            int groupVal = parseInt(val, cursor, cursor += digitsPerInt[10]);
+            destructiveMulAdd(magnitude, intRadix[10], groupVal);
+        }
+        mag = trustedStripLeadingZeroInts(magnitude);
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    // Create an integer with the digits between the two indexes
+    // Assumes start < end. The result may be negative, but it
+    // is to be treated as an unsigned value.
+    private int parseInt(char[] source, int start, int end) {
+        int result = Character.digit(source[start++], 10);
+        if (result == -1)
+            throw new NumberFormatException(new String(source));
+
+        for (int index = start; index < end; index++) {
+            int nextVal = Character.digit(source[index], 10);
+            if (nextVal == -1)
+                throw new NumberFormatException(new String(source));
+            result = 10*result + nextVal;
+        }
+
+        return result;
+    }
+
+    // bitsPerDigit in the given radix times 1024
+    // Rounded up to avoid underallocation.
+    private static long bitsPerDigit[] = { 0, 0,
+        1024, 1624, 2048, 2378, 2648, 2875, 3072, 3247, 3402, 3543, 3672,
+        3790, 3899, 4001, 4096, 4186, 4271, 4350, 4426, 4498, 4567, 4633,
+        4696, 4756, 4814, 4870, 4923, 4975, 5025, 5074, 5120, 5166, 5210,
+                                           5253, 5295};
+
+    // Multiply x array times word y in place, and add word z
+    private static void destructiveMulAdd(int[] x, int y, int z) {
+        // Perform the multiplication word by word
+        long ylong = y & LONG_MASK;
+        long zlong = z & LONG_MASK;
+        int len = x.length;
+
+        long product = 0;
+        long carry = 0;
+        for (int i = len-1; i >= 0; i--) {
+            product = ylong * (x[i] & LONG_MASK) + carry;
+            x[i] = (int)product;
+            carry = product >>> 32;
+        }
+
+        // Perform the addition
+        long sum = (x[len-1] & LONG_MASK) + zlong;
+        x[len-1] = (int)sum;
+        carry = sum >>> 32;
+        for (int i = len-2; i >= 0; i--) {
+            sum = (x[i] & LONG_MASK) + carry;
+            x[i] = (int)sum;
+            carry = sum >>> 32;
+        }
+    }
+
+    /**
+     * Translates the decimal String representation of a BigInteger into a
+     * BigInteger.  The String representation consists of an optional minus
+     * sign followed by a sequence of one or more decimal digits.  The
+     * character-to-digit mapping is provided by {@code Character.digit}.
+     * The String may not contain any extraneous characters (whitespace, for
+     * example).
+     *
+     * @param val decimal String representation of BigInteger.
+     * @throws NumberFormatException {@code val} is not a valid representation
+     *         of a BigInteger.
+     * @see    Character#digit
+     */
+    public BigInteger(@NonNull String val) {
+        this(val, 10);
+    }
+
+    /**
+     * Constructs a randomly generated BigInteger, uniformly distributed over
+     * the range 0 to (2<sup>{@code numBits}</sup> - 1), inclusive.
+     * The uniformity of the distribution assumes that a fair source of random
+     * bits is provided in {@code rnd}.  Note that this constructor always
+     * constructs a non-negative BigInteger.
+     *
+     * @param  numBits maximum bitLength of the new BigInteger.
+     * @param  rnd source of randomness to be used in computing the new
+     *         BigInteger.
+     * @throws IllegalArgumentException {@code numBits} is negative.
+     * @see #bitLength()
+     */
+    public BigInteger(int numBits, @NonNull Random rnd) {
+        this(1, randomBits(numBits, rnd));
+    }
+
+    private static byte[] randomBits(int numBits, Random rnd) {
+        if (numBits < 0)
+            throw new IllegalArgumentException("numBits must be non-negative");
+        int numBytes = (int)(((long)numBits+7)/8); // avoid overflow
+        byte[] randomBits = new byte[numBytes];
+
+        // Generate random bytes and mask out any excess bits
+        if (numBytes > 0) {
+            rnd.nextBytes(randomBits);
+            int excessBits = 8*numBytes - numBits;
+            randomBits[0] &= (1 << (8-excessBits)) - 1;
+        }
+        return randomBits;
+    }
+
+    /**
+     * Constructs a randomly generated positive BigInteger that is probably
+     * prime, with the specified bitLength.
+     *
+     * <p>It is recommended that the {@link #probablePrime probablePrime}
+     * method be used in preference to this constructor unless there
+     * is a compelling need to specify a certainty.
+     *
+     * @param  bitLength bitLength of the returned BigInteger.
+     * @param  certainty a measure of the uncertainty that the caller is
+     *         willing to tolerate.  The probability that the new BigInteger
+     *         represents a prime number will exceed
+     *         (1 - 1/2<sup>{@code certainty}</sup>).  The execution time of
+     *         this constructor is proportional to the value of this parameter.
+     * @param  rnd source of random bits used to select candidates to be
+     *         tested for primality.
+     * @throws ArithmeticException {@code bitLength < 2} or {@code bitLength} is too large.
+     * @see    #bitLength()
+     */
+    public BigInteger(int bitLength, int certainty, @NonNull Random rnd) {
+        BigInteger prime;
+
+        if (bitLength < 2)
+            throw new ArithmeticException("bitLength < 2");
+        prime = (bitLength < SMALL_PRIME_THRESHOLD
+                                ? smallPrime(bitLength, certainty, rnd)
+                                : largePrime(bitLength, certainty, rnd));
+        signum = 1;
+        mag = prime.mag;
+    }
+
+    // Minimum size in bits that the requested prime number has
+    // before we use the large prime number generating algorithms.
+    // The cutoff of 95 was chosen empirically for best performance.
+    private static final int SMALL_PRIME_THRESHOLD = 95;
+
+    // Certainty required to meet the spec of probablePrime
+    private static final int DEFAULT_PRIME_CERTAINTY = 100;
+
+    /**
+     * Returns a positive BigInteger that is probably prime, with the
+     * specified bitLength. The probability that a BigInteger returned
+     * by this method is composite does not exceed 2<sup>-100</sup>.
+     *
+     * @param  bitLength bitLength of the returned BigInteger.
+     * @param  rnd source of random bits used to select candidates to be
+     *         tested for primality.
+     * @return a BigInteger of {@code bitLength} bits that is probably prime
+     * @throws ArithmeticException {@code bitLength < 2} or {@code bitLength} is too large.
+     * @see    #bitLength()
+     * @since 1.4
+     */
+    @NonNull public static BigInteger probablePrime(int bitLength, @NonNull Random rnd) {
+        if (bitLength < 2)
+            throw new ArithmeticException("bitLength < 2");
+
+        return (bitLength < SMALL_PRIME_THRESHOLD ?
+                smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
+                largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
+    }
+
+    /**
+     * Find a random number of the specified bitLength that is probably prime.
+     * This method is used for smaller primes, its performance degrades on
+     * larger bitlengths.
+     *
+     * This method assumes bitLength > 1.
+     */
+    private static BigInteger smallPrime(int bitLength, int certainty, @NonNull Random rnd) {
+        int magLen = (bitLength + 31) >>> 5;
+        int temp[] = new int[magLen];
+        int highBit = 1 << ((bitLength+31) & 0x1f);  // High bit of high int
+        int highMask = (highBit << 1) - 1;  // Bits to keep in high int
+
+        while (true) {
+            // Construct a candidate
+            for (int i=0; i < magLen; i++)
+                temp[i] = rnd.nextInt();
+            temp[0] = (temp[0] & highMask) | highBit;  // Ensure exact length
+            if (bitLength > 2)
+                temp[magLen-1] |= 1;  // Make odd if bitlen > 2
+
+            BigInteger p = new BigInteger(temp, 1);
+
+            // Do cheap "pre-test" if applicable
+            if (bitLength > 6) {
+                long r = p.remainder(SMALL_PRIME_PRODUCT).longValue();
+                if ((r%3==0)  || (r%5==0)  || (r%7==0)  || (r%11==0) ||
+                    (r%13==0) || (r%17==0) || (r%19==0) || (r%23==0) ||
+                    (r%29==0) || (r%31==0) || (r%37==0) || (r%41==0))
+                    continue; // Candidate is composite; try another
+            }
+
+            // All candidates of bitLength 2 and 3 are prime by this point
+            if (bitLength < 4)
+                return p;
+
+            // Do expensive test if we survive pre-test (or it's inapplicable)
+            if (p.primeToCertainty(certainty, rnd))
+                return p;
+        }
+    }
+
+    private static final BigInteger SMALL_PRIME_PRODUCT
+                       = valueOf(3L*5*7*11*13*17*19*23*29*31*37*41);
+
+    /**
+     * Find a random number of the specified bitLength that is probably prime.
+     * This method is more appropriate for larger bitlengths since it uses
+     * a sieve to eliminate most composites before using a more expensive
+     * test.
+     */
+    private static BigInteger largePrime(int bitLength, int certainty, @NonNull Random rnd) {
+        BigInteger p;
+        p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
+        p.mag[p.mag.length-1] &= 0xfffffffe;
+
+        // Use a sieve length likely to contain the next prime number
+        int searchLen = getPrimeSearchLen(bitLength);
+        BitSieve searchSieve = new BitSieve(p, searchLen);
+        BigInteger candidate = searchSieve.retrieve(p, certainty, rnd);
+
+        while ((candidate == null) || (candidate.bitLength() != bitLength)) {
+            p = p.add(BigInteger.valueOf(2*searchLen));
+            if (p.bitLength() != bitLength)
+                p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
+            p.mag[p.mag.length-1] &= 0xfffffffe;
+            searchSieve = new BitSieve(p, searchLen);
+            candidate = searchSieve.retrieve(p, certainty, rnd);
+        }
+        return candidate;
+    }
+
+   /**
+    * Returns the first integer greater than this {@code BigInteger} that
+    * is probably prime.  The probability that the number returned by this
+    * method is composite does not exceed 2<sup>-100</sup>. This method will
+    * never skip over a prime when searching: if it returns {@code p}, there
+    * is no prime {@code q} such that {@code this < q < p}.
+    *
+    * @return the first integer greater than this {@code BigInteger} that
+    *         is probably prime.
+    * @throws ArithmeticException {@code this < 0} or {@code this} is too large.
+    * @since 1.5
+    */
+    @NonNull public BigInteger nextProbablePrime() {
+        if (this.signum < 0)
+            throw new ArithmeticException("start < 0: " + this);
+
+        // Handle trivial cases
+        if ((this.signum == 0) || this.equals(ONE))
+            return TWO;
+
+        BigInteger result = this.add(ONE);
+
+        // Fastpath for small numbers
+        if (result.bitLength() < SMALL_PRIME_THRESHOLD) {
+
+            // Ensure an odd number
+            if (!result.testBit(0))
+                result = result.add(ONE);
+
+            while (true) {
+                // Do cheap "pre-test" if applicable
+                if (result.bitLength() > 6) {
+                    long r = result.remainder(SMALL_PRIME_PRODUCT).longValue();
+                    if ((r%3==0)  || (r%5==0)  || (r%7==0)  || (r%11==0) ||
+                        (r%13==0) || (r%17==0) || (r%19==0) || (r%23==0) ||
+                        (r%29==0) || (r%31==0) || (r%37==0) || (r%41==0)) {
+                        result = result.add(TWO);
+                        continue; // Candidate is composite; try another
+                    }
+                }
+
+                // All candidates of bitLength 2 and 3 are prime by this point
+                if (result.bitLength() < 4)
+                    return result;
+
+                // The expensive test
+                if (result.primeToCertainty(DEFAULT_PRIME_CERTAINTY, null))
+                    return result;
+
+                result = result.add(TWO);
+            }
+        }
+
+        // Start at previous even number
+        if (result.testBit(0))
+            result = result.subtract(ONE);
+
+        // Looking for the next large prime
+        int searchLen = getPrimeSearchLen(result.bitLength());
+
+        while (true) {
+           BitSieve searchSieve = new BitSieve(result, searchLen);
+           BigInteger candidate = searchSieve.retrieve(result,
+                                                 DEFAULT_PRIME_CERTAINTY, null);
+           if (candidate != null)
+               return candidate;
+           result = result.add(BigInteger.valueOf(2 * searchLen));
+        }
+    }
+
+    private static int getPrimeSearchLen(int bitLength) {
+        if (bitLength > PRIME_SEARCH_BIT_LENGTH_LIMIT + 1) {
+            throw new ArithmeticException("Prime search implementation restriction on bitLength");
+        }
+        return bitLength / 20 * 64;
+    }
+
+    /**
+     * Returns {@code true} if this BigInteger is probably prime,
+     * {@code false} if it's definitely composite.
+     *
+     * This method assumes bitLength > 2.
+     *
+     * @param  certainty a measure of the uncertainty that the caller is
+     *         willing to tolerate: if the call returns {@code true}
+     *         the probability that this BigInteger is prime exceeds
+     *         {@code (1 - 1/2<sup>certainty</sup>)}.  The execution time of
+     *         this method is proportional to the value of this parameter.
+     * @return {@code true} if this BigInteger is probably prime,
+     *         {@code false} if it's definitely composite.
+     */
+    boolean primeToCertainty(int certainty, @NonNull Random random) {
+        int rounds = 0;
+        int n = (Math.min(certainty, Integer.MAX_VALUE-1)+1)/2;
+
+        // The relationship between the certainty and the number of rounds
+        // we perform is given in the draft standard ANSI X9.80, "PRIME
+        // NUMBER GENERATION, PRIMALITY TESTING, AND PRIMALITY CERTIFICATES".
+        int sizeInBits = this.bitLength();
+        if (sizeInBits < 100) {
+            rounds = 50;
+            rounds = n < rounds ? n : rounds;
+            return passesMillerRabin(rounds, random);
+        }
+
+        if (sizeInBits < 256) {
+            rounds = 27;
+        } else if (sizeInBits < 512) {
+            rounds = 15;
+        } else if (sizeInBits < 768) {
+            rounds = 8;
+        } else if (sizeInBits < 1024) {
+            rounds = 4;
+        } else {
+            rounds = 2;
+        }
+        rounds = n < rounds ? n : rounds;
+
+        return passesMillerRabin(rounds, random) && passesLucasLehmer();
+    }
+
+    /**
+     * Returns true iff this BigInteger is a Lucas-Lehmer probable prime.
+     *
+     * The following assumptions are made:
+     * This BigInteger is a positive, odd number.
+     */
+    private boolean passesLucasLehmer() {
+        BigInteger thisPlusOne = this.add(ONE);
+
+        // Step 1
+        int d = 5;
+        while (jacobiSymbol(d, this) != -1) {
+            // 5, -7, 9, -11, ...
+            d = (d < 0) ? Math.abs(d)+2 : -(d+2);
+        }
+
+        // Step 2
+        BigInteger u = lucasLehmerSequence(d, thisPlusOne, this);
+
+        // Step 3
+        return u.mod(this).equals(ZERO);
+    }
+
+    /**
+     * Computes Jacobi(p,n).
+     * Assumes n positive, odd, n>=3.
+     */
+    private static int jacobiSymbol(int p, @NonNull BigInteger n) {
+        if (p == 0)
+            return 0;
+
+        // Algorithm and comments adapted from Colin Plumb's C library.
+        int j = 1;
+        int u = n.mag[n.mag.length-1];
+
+        // Make p positive
+        if (p < 0) {
+            p = -p;
+            int n8 = u & 7;
+            if ((n8 == 3) || (n8 == 7))
+                j = -j; // 3 (011) or 7 (111) mod 8
+        }
+
+        // Get rid of factors of 2 in p
+        while ((p & 3) == 0)
+            p >>= 2;
+        if ((p & 1) == 0) {
+            p >>= 1;
+            if (((u ^ (u>>1)) & 2) != 0)
+                j = -j; // 3 (011) or 5 (101) mod 8
+        }
+        if (p == 1)
+            return j;
+        // Then, apply quadratic reciprocity
+        if ((p & u & 2) != 0)   // p = u = 3 (mod 4)?
+            j = -j;
+        // And reduce u mod p
+        u = n.mod(BigInteger.valueOf(p)).intValue();
+
+        // Now compute Jacobi(u,p), u < p
+        while (u != 0) {
+            while ((u & 3) == 0)
+                u >>= 2;
+            if ((u & 1) == 0) {
+                u >>= 1;
+                if (((p ^ (p>>1)) & 2) != 0)
+                    j = -j;     // 3 (011) or 5 (101) mod 8
+            }
+            if (u == 1)
+                return j;
+            // Now both u and p are odd, so use quadratic reciprocity
+            assert (u < p);
+            int t = u; u = p; p = t;
+            if ((u & p & 2) != 0) // u = p = 3 (mod 4)?
+                j = -j;
+            // Now u >= p, so it can be reduced
+            u %= p;
+        }
+        return 0;
+    }
+
+    @NonNull private static BigInteger lucasLehmerSequence(int z, @NonNull BigInteger k, @NonNull BigInteger n) {
+        BigInteger d = BigInteger.valueOf(z);
+        BigInteger u = ONE; BigInteger u2;
+        BigInteger v = ONE; BigInteger v2;
+
+        for (int i=k.bitLength()-2; i >= 0; i--) {
+            u2 = u.multiply(v).mod(n);
+
+            v2 = v.square().add(d.multiply(u.square())).mod(n);
+            if (v2.testBit(0))
+                v2 = v2.subtract(n);
+
+            v2 = v2.shiftRight(1);
+
+            u = u2; v = v2;
+            if (k.testBit(i)) {
+                u2 = u.add(v).mod(n);
+                if (u2.testBit(0))
+                    u2 = u2.subtract(n);
+
+                u2 = u2.shiftRight(1);
+                v2 = v.add(d.multiply(u)).mod(n);
+                if (v2.testBit(0))
+                    v2 = v2.subtract(n);
+                v2 = v2.shiftRight(1);
+
+                u = u2; v = v2;
+            }
+        }
+        return u;
+    }
+
+    /**
+     * Returns true iff this BigInteger passes the specified number of
+     * Miller-Rabin tests. This test is taken from the DSA spec (NIST FIPS
+     * 186-2).
+     *
+     * The following assumptions are made:
+     * This BigInteger is a positive, odd number greater than 2.
+     * iterations<=50.
+     */
+    private boolean passesMillerRabin(int iterations, @NonNull Random rnd) {
+        // Find a and m such that m is odd and this == 1 + 2**a * m
+        BigInteger thisMinusOne = this.subtract(ONE);
+        BigInteger m = thisMinusOne;
+        int a = m.getLowestSetBit();
+        m = m.shiftRight(a);
+
+        // Do the tests
+        if (rnd == null) {
+            rnd = ThreadLocalRandom.current();
+        }
+        for (int i=0; i < iterations; i++) {
+            // Generate a uniform random on (1, this)
+            BigInteger b;
+            do {
+                b = new BigInteger(this.bitLength(), rnd);
+            } while (b.compareTo(ONE) <= 0 || b.compareTo(this) >= 0);
+
+            int j = 0;
+            BigInteger z = b.modPow(m, this);
+            while (!((j == 0 && z.equals(ONE)) || z.equals(thisMinusOne))) {
+                if (j > 0 && z.equals(ONE) || ++j == a)
+                    return false;
+                z = z.modPow(TWO, this);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This internal constructor differs from its public cousin
+     * with the arguments reversed in two ways: it assumes that its
+     * arguments are correct, and it doesn't copy the magnitude array.
+     */
+    BigInteger(int[] magnitude, int signum) {
+        this.signum = (magnitude.length == 0 ? 0 : signum);
+        this.mag = magnitude;
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    /**
+     * This private constructor is for internal use and assumes that its
+     * arguments are correct.
+     */
+    private BigInteger(byte[] magnitude, int signum) {
+        this.signum = (magnitude.length == 0 ? 0 : signum);
+        this.mag = stripLeadingZeroBytes(magnitude);
+        if (mag.length >= MAX_MAG_LENGTH) {
+            checkRange();
+        }
+    }
+
+    /**
+     * Throws an {@code ArithmeticException} if the {@code BigInteger} would be
+     * out of the supported range.
+     *
+     * @throws ArithmeticException if {@code this} exceeds the supported range.
+     */
+    private void checkRange() {
+        if (mag.length > MAX_MAG_LENGTH || mag.length == MAX_MAG_LENGTH && mag[0] < 0) {
+            reportOverflow();
+        }
+    }
+
+    private static void reportOverflow() {
+        throw new ArithmeticException("BigInteger would overflow supported range");
+    }
+
+    //Static Factory Methods
+
+    /**
+     * Returns a BigInteger whose value is equal to that of the
+     * specified {@code long}.  This "static factory method" is
+     * provided in preference to a ({@code long}) constructor
+     * because it allows for reuse of frequently used BigIntegers.
+     *
+     * @param  val value of the BigInteger to return.
+     * @return a BigInteger with the specified value.
+     */
+    @NonNull public static BigInteger valueOf(long val) {
+        // If -MAX_CONSTANT < val < MAX_CONSTANT, return stashed constant
+        if (val == 0)
+            return ZERO;
+        if (val > 0 && val <= MAX_CONSTANT)
+            return posConst[(int) val];
+        else if (val < 0 && val >= -MAX_CONSTANT)
+            return negConst[(int) -val];
+
+        return new BigInteger(val);
+    }
+
+    /**
+     * Constructs a BigInteger with the specified value, which may not be zero.
+     */
+    @NonNull private BigInteger(long val) {
+        if (val < 0) {
+            val = -val;
+            signum = -1;
+        } else {
+            signum = 1;
+        }
+
+        int highWord = (int)(val >>> 32);
+        if (highWord == 0) {
+            mag = new int[1];
+            mag[0] = (int)val;
+        } else {
+            mag = new int[2];
+            mag[0] = highWord;
+            mag[1] = (int)val;
+        }
+    }
+
+    /**
+     * Returns a BigInteger with the given two's complement representation.
+     * Assumes that the input array will not be modified (the returned
+     * BigInteger will reference the input array if feasible).
+     */
+    @NonNull private static BigInteger valueOf(int val[]) {
+        return (val[0] > 0 ? new BigInteger(val, 1) : new BigInteger(val));
+    }
+
+    // Constants
+
+    /**
+     * Initialize static constant array when class is loaded.
+     */
+    private final static int MAX_CONSTANT = 16;
+    private static BigInteger posConst[] = new BigInteger[MAX_CONSTANT+1];
+    private static BigInteger negConst[] = new BigInteger[MAX_CONSTANT+1];
+
+    /**
+     * The cache of powers of each radix.  This allows us to not have to
+     * recalculate powers of radix^(2^n) more than once.  This speeds
+     * Schoenhage recursive base conversion significantly.
+     */
+    private static volatile BigInteger[][] powerCache;
+
+    /** The cache of logarithms of radices for base conversion. */
+    private static final double[] logCache;
+
+    /** The natural log of 2.  This is used in computing cache indices. */
+    private static final double LOG_TWO = Math.log(2.0);
+
+    static {
+        assert 0 < KARATSUBA_THRESHOLD
+            && KARATSUBA_THRESHOLD < TOOM_COOK_THRESHOLD
+            && TOOM_COOK_THRESHOLD < Integer.MAX_VALUE
+            && 0 < KARATSUBA_SQUARE_THRESHOLD
+            && KARATSUBA_SQUARE_THRESHOLD < TOOM_COOK_SQUARE_THRESHOLD
+            && TOOM_COOK_SQUARE_THRESHOLD < Integer.MAX_VALUE :
+            "Algorithm thresholds are inconsistent";
+
+        for (int i = 1; i <= MAX_CONSTANT; i++) {
+            int[] magnitude = new int[1];
+            magnitude[0] = i;
+            posConst[i] = new BigInteger(magnitude,  1);
+            negConst[i] = new BigInteger(magnitude, -1);
+        }
+
+        /*
+         * Initialize the cache of radix^(2^x) values used for base conversion
+         * with just the very first value.  Additional values will be created
+         * on demand.
+         */
+        powerCache = new BigInteger[Character.MAX_RADIX+1][];
+        logCache = new double[Character.MAX_RADIX+1];
+
+        for (int i=Character.MIN_RADIX; i <= Character.MAX_RADIX; i++) {
+            powerCache[i] = new BigInteger[] { BigInteger.valueOf(i) };
+            logCache[i] = Math.log(i);
+        }
+    }
+
+    /**
+     * The BigInteger constant zero.
+     *
+     * @since   1.2
+     */
+    @NonNull public static final BigInteger ZERO = new BigInteger(new int[0], 0);
+
+    /**
+     * The BigInteger constant one.
+     *
+     * @since   1.2
+     */
+    @NonNull public static final BigInteger ONE = valueOf(1);
+
+    /**
+     * The BigInteger constant two.  (Not exported.)
+     */
+    @NonNull private static final BigInteger TWO = valueOf(2);
+
+    /**
+     * The BigInteger constant -1.  (Not exported.)
+     */
+    @NonNull private static final BigInteger NEGATIVE_ONE = valueOf(-1);
+
+    /**
+     * The BigInteger constant ten.
+     *
+     * @since   1.5
+     */
+    @NonNull public static final BigInteger TEN = valueOf(10);
+
+    // Arithmetic Operations
+
+    /**
+     * Returns a BigInteger whose value is {@code (this + val)}.
+     *
+     * @param  val value to be added to this BigInteger.
+     * @return {@code this + val}
+     */
+    @NonNull public BigInteger add(@NonNull BigInteger val) {
+        if (val.signum == 0)
+            return this;
+        if (signum == 0)
+            return val;
+        if (val.signum == signum)
+            return new BigInteger(add(mag, val.mag), signum);
+
+        int cmp = compareMagnitude(val);
+        if (cmp == 0)
+            return ZERO;
+        int[] resultMag = (cmp > 0 ? subtract(mag, val.mag)
+                           : subtract(val.mag, mag));
+        resultMag = trustedStripLeadingZeroInts(resultMag);
+
+        return new BigInteger(resultMag, cmp == signum ? 1 : -1);
+    }
+
+    /**
+     * Package private methods used by BigDecimal code to add a BigInteger
+     * with a long. Assumes val is not equal to INFLATED.
+     */
+    @NonNull BigInteger add(long val) {
+        if (val == 0)
+            return this;
+        if (signum == 0)
+            return valueOf(val);
+        if (Long.signum(val) == signum)
+            return new BigInteger(add(mag, Math.abs(val)), signum);
+        int cmp = compareMagnitude(val);
+        if (cmp == 0)
+            return ZERO;
+        int[] resultMag = (cmp > 0 ? subtract(mag, Math.abs(val)) : subtract(Math.abs(val), mag));
+        resultMag = trustedStripLeadingZeroInts(resultMag);
+        return new BigInteger(resultMag, cmp == signum ? 1 : -1);
+    }
+
+    /**
+     * Adds the contents of the int array x and long value val. This
+     * method allocates a new int array to hold the answer and returns
+     * a reference to that array.  Assumes x.length &gt; 0 and val is
+     * non-negative
+     */
+    private static int[] add(int[] x, long val) {
+        int[] y;
+        long sum = 0;
+        int xIndex = x.length;
+        int[] result;
+        int highWord = (int)(val >>> 32);
+        if (highWord == 0) {
+            result = new int[xIndex];
+            sum = (x[--xIndex] & LONG_MASK) + val;
+            result[xIndex] = (int)sum;
+        } else {
+            if (xIndex == 1) {
+                result = new int[2];
+                sum = val  + (x[0] & LONG_MASK);
+                result[1] = (int)sum;
+                result[0] = (int)(sum >>> 32);
+                return result;
+            } else {
+                result = new int[xIndex];
+                sum = (x[--xIndex] & LONG_MASK) + (val & LONG_MASK);
+                result[xIndex] = (int)sum;
+                sum = (x[--xIndex] & LONG_MASK) + (highWord & LONG_MASK) + (sum >>> 32);
+                result[xIndex] = (int)sum;
+            }
+        }
+        // Copy remainder of longer number while carry propagation is required
+        boolean carry = (sum >>> 32 != 0);
+        while (xIndex > 0 && carry)
+            carry = ((result[--xIndex] = x[xIndex] + 1) == 0);
+        // Copy remainder of longer number
+        while (xIndex > 0)
+            result[--xIndex] = x[xIndex];
+        // Grow result if necessary
+        if (carry) {
+            int bigger[] = new int[result.length + 1];
+            System.arraycopy(result, 0, bigger, 1, result.length);
+            bigger[0] = 0x01;
+            return bigger;
+        }
+        return result;
+    }
+
+    /**
+     * Adds the contents of the int arrays x and y. This method allocates
+     * a new int array to hold the answer and returns a reference to that
+     * array.
+     */
+    private static int[] add(int[] x, int[] y) {
+        // If x is shorter, swap the two arrays
+        if (x.length < y.length) {
+            int[] tmp = x;
+            x = y;
+            y = tmp;
+        }
+
+        int xIndex = x.length;
+        int yIndex = y.length;
+        int result[] = new int[xIndex];
+        long sum = 0;
+        if (yIndex == 1) {
+            sum = (x[--xIndex] & LONG_MASK) + (y[0] & LONG_MASK) ;
+            result[xIndex] = (int)sum;
+        } else {
+            // Add common parts of both numbers
+            while (yIndex > 0) {
+                sum = (x[--xIndex] & LONG_MASK) +
+                      (y[--yIndex] & LONG_MASK) + (sum >>> 32);
+                result[xIndex] = (int)sum;
+            }
+        }
+        // Copy remainder of longer number while carry propagation is required
+        boolean carry = (sum >>> 32 != 0);
+        while (xIndex > 0 && carry)
+            carry = ((result[--xIndex] = x[xIndex] + 1) == 0);
+
+        // Copy remainder of longer number
+        while (xIndex > 0)
+            result[--xIndex] = x[xIndex];
+
+        // Grow result if necessary
+        if (carry) {
+            int bigger[] = new int[result.length + 1];
+            System.arraycopy(result, 0, bigger, 1, result.length);
+            bigger[0] = 0x01;
+            return bigger;
+        }
+        return result;
+    }
+
+    private static int[] subtract(long val, int[] little) {
+        int highWord = (int)(val >>> 32);
+        if (highWord == 0) {
+            int result[] = new int[1];
+            result[0] = (int)(val - (little[0] & LONG_MASK));
+            return result;
+        } else {
+            int result[] = new int[2];
+            if (little.length == 1) {
+                long difference = ((int)val & LONG_MASK) - (little[0] & LONG_MASK);
+                result[1] = (int)difference;
+                // Subtract remainder of longer number while borrow propagates
+                boolean borrow = (difference >> 32 != 0);
+                if (borrow) {
+                    result[0] = highWord - 1;
+                } else {        // Copy remainder of longer number
+                    result[0] = highWord;
+                }
+                return result;
+            } else { // little.length == 2
+                long difference = ((int)val & LONG_MASK) - (little[1] & LONG_MASK);
+                result[1] = (int)difference;
+                difference = (highWord & LONG_MASK) - (little[0] & LONG_MASK) + (difference >> 32);
+                result[0] = (int)difference;
+                return result;
+            }
+        }
+    }
+
+    /**
+     * Subtracts the contents of the second argument (val) from the
+     * first (big).  The first int array (big) must represent a larger number
+     * than the second.  This method allocates the space necessary to hold the
+     * answer.
+     * assumes val &gt;= 0
+     */
+    private static int[] subtract(int[] big, long val) {
+        int highWord = (int)(val >>> 32);
+        int bigIndex = big.length;
+        int result[] = new int[bigIndex];
+        long difference = 0;
+
+        if (highWord == 0) {
+            difference = (big[--bigIndex] & LONG_MASK) - val;
+            result[bigIndex] = (int)difference;
+        } else {
+            difference = (big[--bigIndex] & LONG_MASK) - (val & LONG_MASK);
+            result[bigIndex] = (int)difference;
+            difference = (big[--bigIndex] & LONG_MASK) - (highWord & LONG_MASK) + (difference >> 32);
+            result[bigIndex] = (int)difference;
+        }
+
+        // Subtract remainder of longer number while borrow propagates
+        boolean borrow = (difference >> 32 != 0);
+        while (bigIndex > 0 && borrow)
+            borrow = ((result[--bigIndex] = big[bigIndex] - 1) == -1);
+
+        // Copy remainder of longer number
+        while (bigIndex > 0)
+            result[--bigIndex] = big[bigIndex];
+
+        return result;
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this - val)}.
+     *
+     * @param  val value to be subtracted from this BigInteger.
+     * @return {@code this - val}
+     */
+    @NonNull public BigInteger subtract(@NonNull BigInteger val) {
+        if (val.signum == 0)
+            return this;
+        if (signum == 0)
+            return val.negate();
+        if (val.signum != signum)
+            return new BigInteger(add(mag, val.mag), signum);
+
+        int cmp = compareMagnitude(val);
+        if (cmp == 0)
+            return ZERO;
+        int[] resultMag = (cmp > 0 ? subtract(mag, val.mag)
+                           : subtract(val.mag, mag));
+        resultMag = trustedStripLeadingZeroInts(resultMag);
+        return new BigInteger(resultMag, cmp == signum ? 1 : -1);
+    }
+
+    /**
+     * Subtracts the contents of the second int arrays (little) from the
+     * first (big).  The first int array (big) must represent a larger number
+     * than the second.  This method allocates the space necessary to hold the
+     * answer.
+     */
+    private static int[] subtract(int[] big, int[] little) {
+        int bigIndex = big.length;
+        int result[] = new int[bigIndex];
+        int littleIndex = little.length;
+        long difference = 0;
+
+        // Subtract common parts of both numbers
+        while (littleIndex > 0) {
+            difference = (big[--bigIndex] & LONG_MASK) -
+                         (little[--littleIndex] & LONG_MASK) +
+                         (difference >> 32);
+            result[bigIndex] = (int)difference;
+        }
+
+        // Subtract remainder of longer number while borrow propagates
+        boolean borrow = (difference >> 32 != 0);
+        while (bigIndex > 0 && borrow)
+            borrow = ((result[--bigIndex] = big[bigIndex] - 1) == -1);
+
+        // Copy remainder of longer number
+        while (bigIndex > 0)
+            result[--bigIndex] = big[bigIndex];
+
+        return result;
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this * val)}.
+     *
+     * @implNote An implementation may offer better algorithmic
+     * performance when {@code val == this}.
+     *
+     * @param  val value to be multiplied by this BigInteger.
+     * @return {@code this * val}
+     */
+    @NonNull public BigInteger multiply(@NonNull BigInteger val) {
+        return multiply(val, false);
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this * val)}.  If
+     * the invocation is recursive certain overflow checks are skipped.
+     *
+     * @param  val value to be multiplied by this BigInteger.
+     * @param  isRecursion whether this is a recursive invocation
+     * @return {@code this * val}
+     */
+    @NonNull private BigInteger multiply(@NonNull BigInteger val, boolean isRecursion) {
+        if (val.signum == 0 || signum == 0)
+            return ZERO;
+
+        int xlen = mag.length;
+
+        // BEGIN Android-changed: Fall back to the boringssl implementation for
+        // large arguments.
+        int ylen = val.mag.length;
+
+        final int BORINGSSL_MUL_THRESHOLD = 50;
+
+        int resultSign = signum == val.signum ? 1 : -1;
+        if ((xlen < BORINGSSL_MUL_THRESHOLD) || (ylen < BORINGSSL_MUL_THRESHOLD)) {
+            if (val == this && xlen > MULTIPLY_SQUARE_THRESHOLD) {
+                // Helps less than boringssl fallback; prefer that.
+                return square();
+            }
+
+            if (val.mag.length == 1) {
+                return multiplyByInt(mag,val.mag[0], resultSign);
+            }
+            if (mag.length == 1) {
+                return multiplyByInt(val.mag,mag[0], resultSign);
+            }
+            int[] result = multiplyToLen(mag, xlen,
+                                         val.mag, ylen, null);
+            result = trustedStripLeadingZeroInts(result);
+            return new BigInteger(result, resultSign);
+        } else {
+            long xBN = 0, yBN = 0, resultBN = 0;
+            try {
+                xBN = bigEndInts2NewBN(mag, /* neg= */false);
+                yBN = bigEndInts2NewBN(val.mag, /* neg= */false);
+                resultBN = NativeBN.BN_new();
+                NativeBN.BN_mul(resultBN, xBN, yBN);
+                return new BigInteger(resultSign, bn2BigEndInts(resultBN));
+            } finally {
+                NativeBN.BN_free(xBN);
+                NativeBN.BN_free(yBN);
+                NativeBN.BN_free(resultBN);
+            }
+
+            /*
+            if ((xlen < TOOM_COOK_THRESHOLD) && (ylen < TOOM_COOK_THRESHOLD)) {
+                return multiplyKaratsuba(this, val);
+            } else {
+                //
+                // In "Hacker's Delight" section 2-13, p.33, it is explained
+                // that if x and y are unsigned 32-bit quantities and m and n
+                // are their respective numbers of leading zeros within 32 bits,
+                // then the number of leading zeros within their product as a
+                // 64-bit unsigned quantity is either m + n or m + n + 1. If
+                // their product is not to overflow, it cannot exceed 32 bits,
+                // and so the number of leading zeros of the product within 64
+                // bits must be at least 32, i.e., the leftmost set bit is at
+                // zero-relative position 31 or less.
+                //
+                // From the above there are three cases:
+                //
+                //     m + n    leftmost set bit    condition
+                //     -----    ----------------    ---------
+                //     >= 32    x <= 64 - 32 = 32   no overflow
+                //     == 31    x >= 64 - 32 = 32   possible overflow
+                //     <= 30    x >= 64 - 31 = 33   definite overflow
+                //
+                // The "possible overflow" condition cannot be detected by
+                // examning data lengths alone and requires further calculation.
+                //
+                // By analogy, if 'this' and 'val' have m and n as their
+                // respective numbers of leading zeros within 32*MAX_MAG_LENGTH
+                // bits, then:
+                //
+                //     m + n >= 32*MAX_MAG_LENGTH        no overflow
+                //     m + n == 32*MAX_MAG_LENGTH - 1    possible overflow
+                //     m + n <= 32*MAX_MAG_LENGTH - 2    definite overflow
+                //
+                // Note however that if the number of ints in the result
+                // were to be MAX_MAG_LENGTH and mag[0] < 0, then there would
+                // be overflow. As a result the leftmost bit (of mag[0]) cannot
+                // be used and the constraints must be adjusted by one bit to:
+                //
+                //     m + n >  32*MAX_MAG_LENGTH        no overflow
+                //     m + n == 32*MAX_MAG_LENGTH        possible overflow
+                //     m + n <  32*MAX_MAG_LENGTH        definite overflow
+                //
+                // The foregoing leading zero-based discussion is for clarity
+                // only. The actual calculations use the estimated bit length
+                // of the product as this is more natural to the internal
+                // array representation of the magnitude which has no leading
+                // zero elements.
+                //
+                if (!isRecursion) {
+                    // The bitLength() instance method is not used here as we
+                    // are only considering the magnitudes as non-negative. The
+                    // Toom-Cook multiplication algorithm determines the sign
+                    // at its end from the two signum values.
+                    if (bitLength(mag, mag.length) +
+                        bitLength(val.mag, val.mag.length) >
+                        32L*MAX_MAG_LENGTH) {
+                        reportOverflow();
+                    }
+                }
+
+                return multiplyToomCook3(this, val);
+            }
+            */
+        }
+    }
+
+    @NonNull private static BigInteger multiplyByInt(int[] x, int y, int sign) {
+        if (Integer.bitCount(y) == 1) {
+            return new BigInteger(shiftLeft(x,Integer.numberOfTrailingZeros(y)), sign);
+        }
+        int xlen = x.length;
+        int[] rmag =  new int[xlen + 1];
+        long carry = 0;
+        long yl = y & LONG_MASK;
+        int rstart = rmag.length - 1;
+        for (int i = xlen - 1; i >= 0; i--) {
+            long product = (x[i] & LONG_MASK) * yl + carry;
+            rmag[rstart--] = (int)product;
+            carry = product >>> 32;
+        }
+        if (carry == 0L) {
+            rmag = java.util.Arrays.copyOfRange(rmag, 1, rmag.length);
+        } else {
+            rmag[rstart] = (int)carry;
+        }
+        return new BigInteger(rmag, sign);
+    }
+
+    /**
+     * Package private methods used by BigDecimal code to multiply a BigInteger
+     * with a long. Assumes v is not equal to INFLATED.
+     */
+    @NonNull BigInteger multiply(long v) {
+        if (v == 0 || signum == 0)
+          return ZERO;
+        if (v == BigDecimal.INFLATED)
+            return multiply(BigInteger.valueOf(v));
+        int rsign = (v > 0 ? signum : -signum);
+        if (v < 0)
+            v = -v;
+        long dh = v >>> 32;      // higher order bits
+        long dl = v & LONG_MASK; // lower order bits
+
+        int xlen = mag.length;
+        int[] value = mag;
+        int[] rmag = (dh == 0L) ? (new int[xlen + 1]) : (new int[xlen + 2]);
+        long carry = 0;
+        int rstart = rmag.length - 1;
+        for (int i = xlen - 1; i >= 0; i--) {
+            long product = (value[i] & LONG_MASK) * dl + carry;
+            rmag[rstart--] = (int)product;
+            carry = product >>> 32;
+        }
+        rmag[rstart] = (int)carry;
+        if (dh != 0L) {
+            carry = 0;
+            rstart = rmag.length - 2;
+            for (int i = xlen - 1; i >= 0; i--) {
+                long product = (value[i] & LONG_MASK) * dh +
+                    (rmag[rstart] & LONG_MASK) + carry;
+                rmag[rstart--] = (int)product;
+                carry = product >>> 32;
+            }
+            rmag[0] = (int)carry;
+        }
+        if (carry == 0L)
+            rmag = java.util.Arrays.copyOfRange(rmag, 1, rmag.length);
+        return new BigInteger(rmag, rsign);
+    }
+
+    /**
+     * Multiplies int arrays x and y to the specified lengths and places
+     * the result into z. There will be no leading zeros in the resultant array.
+     */
+    private static int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z) {
+        int xstart = xlen - 1;
+        int ystart = ylen - 1;
+
+        if (z == null || z.length < (xlen+ ylen))
+             z = new int[xlen+ylen];
+
+        long carry = 0;
+        for (int j=ystart, k=ystart+1+xstart; j >= 0; j--, k--) {
+            long product = (y[j] & LONG_MASK) *
+                           (x[xstart] & LONG_MASK) + carry;
+            z[k] = (int)product;
+            carry = product >>> 32;
+        }
+        z[xstart] = (int)carry;
+
+        for (int i = xstart-1; i >= 0; i--) {
+            carry = 0;
+            for (int j=ystart, k=ystart+1+i; j >= 0; j--, k--) {
+                long product = (y[j] & LONG_MASK) *
+                               (x[i] & LONG_MASK) +
+                               (z[k] & LONG_MASK) + carry;
+                z[k] = (int)product;
+                carry = product >>> 32;
+            }
+            z[i] = (int)carry;
+        }
+        return z;
+    }
+
+    /**
+     * Multiplies two BigIntegers using the Karatsuba multiplication
+     * algorithm.  This is a recursive divide-and-conquer algorithm which is
+     * more efficient for large numbers than what is commonly called the
+     * "grade-school" algorithm used in multiplyToLen.  If the numbers to be
+     * multiplied have length n, the "grade-school" algorithm has an
+     * asymptotic complexity of O(n^2).  In contrast, the Karatsuba algorithm
+     * has complexity of O(n^(log2(3))), or O(n^1.585).  It achieves this
+     * increased performance by doing 3 multiplies instead of 4 when
+     * evaluating the product.  As it has some overhead, should be used when
+     * both numbers are larger than a certain threshold (found
+     * experimentally).
+     *
+     * See:  http://en.wikipedia.org/wiki/Karatsuba_algorithm
+     */
+    @NonNull private static BigInteger multiplyKaratsuba(@NonNull BigInteger x, @NonNull BigInteger y) {
+        int xlen = x.mag.length;
+        int ylen = y.mag.length;
+
+        // The number of ints in each half of the number.
+        int half = (Math.max(xlen, ylen)+1) / 2;
+
+        // xl and yl are the lower halves of x and y respectively,
+        // xh and yh are the upper halves.
+        BigInteger xl = x.getLower(half);
+        BigInteger xh = x.getUpper(half);
+        BigInteger yl = y.getLower(half);
+        BigInteger yh = y.getUpper(half);
+
+        BigInteger p1 = xh.multiply(yh);  // p1 = xh*yh
+        BigInteger p2 = xl.multiply(yl);  // p2 = xl*yl
+
+        // p3=(xh+xl)*(yh+yl)
+        BigInteger p3 = xh.add(xl).multiply(yh.add(yl));
+
+        // result = p1 * 2^(32*2*half) + (p3 - p1 - p2) * 2^(32*half) + p2
+        BigInteger result = p1.shiftLeft(32*half).add(p3.subtract(p1).subtract(p2)).shiftLeft(32*half).add(p2);
+
+        if (x.signum != y.signum) {
+            return result.negate();
+        } else {
+            return result;
+        }
+    }
+
+    /**
+     * Multiplies two BigIntegers using a 3-way Toom-Cook multiplication
+     * algorithm.  This is a recursive divide-and-conquer algorithm which is
+     * more efficient for large numbers than what is commonly called the
+     * "grade-school" algorithm used in multiplyToLen.  If the numbers to be
+     * multiplied have length n, the "grade-school" algorithm has an
+     * asymptotic complexity of O(n^2).  In contrast, 3-way Toom-Cook has a
+     * complexity of about O(n^1.465).  It achieves this increased asymptotic
+     * performance by breaking each number into three parts and by doing 5
+     * multiplies instead of 9 when evaluating the product.  Due to overhead
+     * (additions, shifts, and one division) in the Toom-Cook algorithm, it
+     * should only be used when both numbers are larger than a certain
+     * threshold (found experimentally).  This threshold is generally larger
+     * than that for Karatsuba multiplication, so this algorithm is generally
+     * only used when numbers become significantly larger.
+     *
+     * The algorithm used is the "optimal" 3-way Toom-Cook algorithm outlined
+     * by Marco Bodrato.
+     *
+     *  See: http://bodrato.it/toom-cook/
+     *       http://bodrato.it/papers/#WAIFI2007
+     *
+     * "Towards Optimal Toom-Cook Multiplication for Univariate and
+     * Multivariate Polynomials in Characteristic 2 and 0." by Marco BODRATO;
+     * In C.Carlet and B.Sunar, Eds., "WAIFI'07 proceedings", p. 116-133,
+     * LNCS #4547. Springer, Madrid, Spain, June 21-22, 2007.
+     *
+     */
+    @NonNull private static BigInteger multiplyToomCook3(@NonNull BigInteger a, @NonNull BigInteger b) {
+        int alen = a.mag.length;
+        int blen = b.mag.length;
+
+        int largest = Math.max(alen, blen);
+
+        // k is the size (in ints) of the lower-order slices.
+        int k = (largest+2)/3;   // Equal to ceil(largest/3)
+
+        // r is the size (in ints) of the highest-order slice.
+        int r = largest - 2*k;
+
+        // Obtain slices of the numbers. a2 and b2 are the most significant
+        // bits of the numbers a and b, and a0 and b0 the least significant.
+        BigInteger a0, a1, a2, b0, b1, b2;
+        a2 = a.getToomSlice(k, r, 0, largest);
+        a1 = a.getToomSlice(k, r, 1, largest);
+        a0 = a.getToomSlice(k, r, 2, largest);
+        b2 = b.getToomSlice(k, r, 0, largest);
+        b1 = b.getToomSlice(k, r, 1, largest);
+        b0 = b.getToomSlice(k, r, 2, largest);
+
+        BigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1, db1;
+
+        v0 = a0.multiply(b0, true);
+        da1 = a2.add(a0);
+        db1 = b2.add(b0);
+        vm1 = da1.subtract(a1).multiply(db1.subtract(b1), true);
+        da1 = da1.add(a1);
+        db1 = db1.add(b1);
+        v1 = da1.multiply(db1, true);
+        v2 = da1.add(a2).shiftLeft(1).subtract(a0).multiply(
+             db1.add(b2).shiftLeft(1).subtract(b0), true);
+        vinf = a2.multiply(b2, true);
+
+        // The algorithm requires two divisions by 2 and one by 3.
+        // All divisions are known to be exact, that is, they do not produce
+        // remainders, and all results are positive.  The divisions by 2 are
+        // implemented as right shifts which are relatively efficient, leaving
+        // only an exact division by 3, which is done by a specialized
+        // linear-time algorithm.
+        t2 = v2.subtract(vm1).exactDivideBy3();
+        tm1 = v1.subtract(vm1).shiftRight(1);
+        t1 = v1.subtract(v0);
+        t2 = t2.subtract(t1).shiftRight(1);
+        t1 = t1.subtract(tm1).subtract(vinf);
+        t2 = t2.subtract(vinf.shiftLeft(1));
+        tm1 = tm1.subtract(t2);
+
+        // Number of bits to shift left.
+        int ss = k*32;
+
+        BigInteger result = vinf.shiftLeft(ss).add(t2).shiftLeft(ss).add(t1).shiftLeft(ss).add(tm1).shiftLeft(ss).add(v0);
+
+        if (a.signum != b.signum) {
+            return result.negate();
+        } else {
+            return result;
+        }
+    }
+
+
+    /**
+     * Returns a slice of a BigInteger for use in Toom-Cook multiplication.
+     *
+     * @param lowerSize The size of the lower-order bit slices.
+     * @param upperSize The size of the higher-order bit slices.
+     * @param slice The index of which slice is requested, which must be a
+     * number from 0 to size-1. Slice 0 is the highest-order bits, and slice
+     * size-1 are the lowest-order bits. Slice 0 may be of different size than
+     * the other slices.
+     * @param fullsize The size of the larger integer array, used to align
+     * slices to the appropriate position when multiplying different-sized
+     * numbers.
+     */
+    @NonNull private BigInteger getToomSlice(int lowerSize, int upperSize, int slice,
+                                    int fullsize) {
+        int start, end, sliceSize, len, offset;
+
+        len = mag.length;
+        offset = fullsize - len;
+
+        if (slice == 0) {
+            start = 0 - offset;
+            end = upperSize - 1 - offset;
+        } else {
+            start = upperSize + (slice-1)*lowerSize - offset;
+            end = start + lowerSize - 1;
+        }
+
+        if (start < 0) {
+            start = 0;
+        }
+        if (end < 0) {
+           return ZERO;
+        }
+
+        sliceSize = (end-start) + 1;
+
+        if (sliceSize <= 0) {
+            return ZERO;
+        }
+
+        // While performing Toom-Cook, all slices are positive and
+        // the sign is adjusted when the final number is composed.
+        if (start == 0 && sliceSize >= len) {
+            return this.abs();
+        }
+
+        int intSlice[] = new int[sliceSize];
+        System.arraycopy(mag, start, intSlice, 0, sliceSize);
+
+        return new BigInteger(trustedStripLeadingZeroInts(intSlice), 1);
+    }
+
+    /**
+     * Does an exact division (that is, the remainder is known to be zero)
+     * of the specified number by 3.  This is used in Toom-Cook
+     * multiplication.  This is an efficient algorithm that runs in linear
+     * time.  If the argument is not exactly divisible by 3, results are
+     * undefined.  Note that this is expected to be called with positive
+     * arguments only.
+     */
+    @NonNull private BigInteger exactDivideBy3() {
+        int len = mag.length;
+        int[] result = new int[len];
+        long x, w, q, borrow;
+        borrow = 0L;
+        for (int i=len-1; i >= 0; i--) {
+            x = (mag[i] & LONG_MASK);
+            w = x - borrow;
+            if (borrow > x) {      // Did we make the number go negative?
+                borrow = 1L;
+            } else {
+                borrow = 0L;
+            }
+
+            // 0xAAAAAAAB is the modular inverse of 3 (mod 2^32).  Thus,
+            // the effect of this is to divide by 3 (mod 2^32).
+            // This is much faster than division on most architectures.
+            q = (w * 0xAAAAAAABL) & LONG_MASK;
+            result[i] = (int) q;
+
+            // Now check the borrow. The second check can of course be
+            // eliminated if the first fails.
+            if (q >= 0x55555556L) {
+                borrow++;
+                if (q >= 0xAAAAAAABL)
+                    borrow++;
+            }
+        }
+        result = trustedStripLeadingZeroInts(result);
+        return new BigInteger(result, signum);
+    }
+
+    /**
+     * Returns a new BigInteger representing n lower ints of the number.
+     * This is used by Karatsuba multiplication and Karatsuba squaring.
+     */
+    @NonNull private BigInteger getLower(int n) {
+        int len = mag.length;
+
+        if (len <= n) {
+            return abs();
+        }
+
+        int lowerInts[] = new int[n];
+        System.arraycopy(mag, len-n, lowerInts, 0, n);
+
+        return new BigInteger(trustedStripLeadingZeroInts(lowerInts), 1);
+    }
+
+    /**
+     * Returns a new BigInteger representing mag.length-n upper
+     * ints of the number.  This is used by Karatsuba multiplication and
+     * Karatsuba squaring.
+     */
+    @NonNull private BigInteger getUpper(int n) {
+        int len = mag.length;
+
+        if (len <= n) {
+            return ZERO;
+        }
+
+        int upperLen = len - n;
+        int upperInts[] = new int[upperLen];
+        System.arraycopy(mag, 0, upperInts, 0, upperLen);
+
+        return new BigInteger(trustedStripLeadingZeroInts(upperInts), 1);
+    }
+
+    // Squaring
+
+    /**
+     * Returns a BigInteger whose value is {@code (this<sup>2</sup>)}.
+     *
+     * @return {@code this<sup>2</sup>}
+     */
+    @NonNull private BigInteger square() {
+        return square(false);
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this<sup>2</sup>)}. If
+     * the invocation is recursive certain overflow checks are skipped.
+     *
+     * @param isRecursion whether this is a recursive invocation
+     * @return {@code this<sup>2</sup>}
+     */
+    @NonNull private BigInteger square(boolean isRecursion) {
+        if (signum == 0) {
+            return ZERO;
+        }
+        int len = mag.length;
+
+        if (len < KARATSUBA_SQUARE_THRESHOLD) {
+            int[] z = squareToLen(mag, len, null);
+            return new BigInteger(trustedStripLeadingZeroInts(z), 1);
+        } else {
+            if (len < TOOM_COOK_SQUARE_THRESHOLD) {
+                return squareKaratsuba();
+            } else {
+                //
+                // For a discussion of overflow detection see multiply()
+                //
+                if (!isRecursion) {
+                    if (bitLength(mag, mag.length) > 16L*MAX_MAG_LENGTH) {
+                        reportOverflow();
+                    }
+                }
+
+                return squareToomCook3();
+            }
+        }
+    }
+
+    /**
+     * Squares the contents of the int array x. The result is placed into the
+     * int array z.  The contents of x are not changed.
+     */
+    private static final int[] squareToLen(int[] x, int len, int[] z) {
+         int zlen = len << 1;
+         if (z == null || z.length < zlen)
+             z = new int[zlen];
+
+         // Execute checks before calling intrinsified method.
+         implSquareToLenChecks(x, len, z, zlen);
+         return implSquareToLen(x, len, z, zlen);
+     }
+
+     /**
+      * Parameters validation.
+      */
+     private static void implSquareToLenChecks(int[] x, int len, int[] z, int zlen) throws RuntimeException {
+         if (len < 1) {
+             throw new IllegalArgumentException("invalid input length: " + len);
+         }
+         if (len > x.length) {
+             throw new IllegalArgumentException("input length out of bound: " +
+                                        len + " > " + x.length);
+         }
+         if (len * 2 > z.length) {
+             throw new IllegalArgumentException("input length out of bound: " +
+                                        (len * 2) + " > " + z.length);
+         }
+         if (zlen < 1) {
+             throw new IllegalArgumentException("invalid input length: " + zlen);
+         }
+         if (zlen > z.length) {
+             throw new IllegalArgumentException("input length out of bound: " +
+                                        len + " > " + z.length);
+         }
+     }
+
+     /**
+      * Java Runtime may use intrinsic for this method.
+      */
+     private static final int[] implSquareToLen(int[] x, int len, int[] z, int zlen) {
+        /*
+         * The algorithm used here is adapted from Colin Plumb's C library.
+         * Technique: Consider the partial products in the multiplication
+         * of "abcde" by itself:
+         *
+         *               a  b  c  d  e
+         *            *  a  b  c  d  e
+         *          ==================
+         *              ae be ce de ee
+         *           ad bd cd dd de
+         *        ac bc cc cd ce
+         *     ab bb bc bd be
+         *  aa ab ac ad ae
+         *
+         * Note that everything above the main diagonal:
+         *              ae be ce de = (abcd) * e
+         *           ad bd cd       = (abc) * d
+         *        ac bc             = (ab) * c
+         *     ab                   = (a) * b
+         *
+         * is a copy of everything below the main diagonal:
+         *                       de
+         *                 cd ce
+         *           bc bd be
+         *     ab ac ad ae
+         *
+         * Thus, the sum is 2 * (off the diagonal) + diagonal.
+         *
+         * This is accumulated beginning with the diagonal (which
+         * consist of the squares of the digits of the input), which is then
+         * divided by two, the off-diagonal added, and multiplied by two
+         * again.  The low bit is simply a copy of the low bit of the
+         * input, so it doesn't need special care.
+         */
+
+        // Store the squares, right shifted one bit (i.e., divided by 2)
+        int lastProductLowWord = 0;
+        for (int j=0, i=0; j < len; j++) {
+            long piece = (x[j] & LONG_MASK);
+            long product = piece * piece;
+            z[i++] = (lastProductLowWord << 31) | (int)(product >>> 33);
+            z[i++] = (int)(product >>> 1);
+            lastProductLowWord = (int)product;
+        }
+
+        // Add in off-diagonal sums
+        for (int i=len, offset=1; i > 0; i--, offset+=2) {
+            int t = x[i-1];
+            t = mulAdd(z, x, offset, i-1, t);
+            addOne(z, offset-1, i, t);
+        }
+
+        // Shift back up and set low bit
+        primitiveLeftShift(z, zlen, 1);
+        z[zlen-1] |= x[len-1] & 1;
+
+        return z;
+    }
+
+    /**
+     * Squares a BigInteger using the Karatsuba squaring algorithm.  It should
+     * be used when both numbers are larger than a certain threshold (found
+     * experimentally).  It is a recursive divide-and-conquer algorithm that
+     * has better asymptotic performance than the algorithm used in
+     * squareToLen.
+     */
+    @NonNull private BigInteger squareKaratsuba() {
+        int half = (mag.length+1) / 2;
+
+        BigInteger xl = getLower(half);
+        BigInteger xh = getUpper(half);
+
+        BigInteger xhs = xh.square();  // xhs = xh^2
+        BigInteger xls = xl.square();  // xls = xl^2
+
+        // xh^2 << 64  +  (((xl+xh)^2 - (xh^2 + xl^2)) << 32) + xl^2
+        return xhs.shiftLeft(half*32).add(xl.add(xh).square().subtract(xhs.add(xls))).shiftLeft(half*32).add(xls);
+    }
+
+    /**
+     * Squares a BigInteger using the 3-way Toom-Cook squaring algorithm.  It
+     * should be used when both numbers are larger than a certain threshold
+     * (found experimentally).  It is a recursive divide-and-conquer algorithm
+     * that has better asymptotic performance than the algorithm used in
+     * squareToLen or squareKaratsuba.
+     */
+    @NonNull private BigInteger squareToomCook3() {
+        int len = mag.length;
+
+        // k is the size (in ints) of the lower-order slices.
+        int k = (len+2)/3;   // Equal to ceil(largest/3)
+
+        // r is the size (in ints) of the highest-order slice.
+        int r = len - 2*k;
+
+        // Obtain slices of the numbers. a2 is the most significant
+        // bits of the number, and a0 the least significant.
+        BigInteger a0, a1, a2;
+        a2 = getToomSlice(k, r, 0, len);
+        a1 = getToomSlice(k, r, 1, len);
+        a0 = getToomSlice(k, r, 2, len);
+        BigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1;
+
+        v0 = a0.square(true);
+        da1 = a2.add(a0);
+        vm1 = da1.subtract(a1).square(true);
+        da1 = da1.add(a1);
+        v1 = da1.square(true);
+        vinf = a2.square(true);
+        v2 = da1.add(a2).shiftLeft(1).subtract(a0).square(true);
+
+        // The algorithm requires two divisions by 2 and one by 3.
+        // All divisions are known to be exact, that is, they do not produce
+        // remainders, and all results are positive.  The divisions by 2 are
+        // implemented as right shifts which are relatively efficient, leaving
+        // only a division by 3.
+        // The division by 3 is done by an optimized algorithm for this case.
+        t2 = v2.subtract(vm1).exactDivideBy3();
+        tm1 = v1.subtract(vm1).shiftRight(1);
+        t1 = v1.subtract(v0);
+        t2 = t2.subtract(t1).shiftRight(1);
+        t1 = t1.subtract(tm1).subtract(vinf);
+        t2 = t2.subtract(vinf.shiftLeft(1));
+        tm1 = tm1.subtract(t2);
+
+        // Number of bits to shift left.
+        int ss = k*32;
+
+        return vinf.shiftLeft(ss).add(t2).shiftLeft(ss).add(t1).shiftLeft(ss).add(tm1).shiftLeft(ss).add(v0);
+    }
+
+    // Division
+
+
+    // BEGIN Android-modified: Fall back to boringssl for large problems.
+    private static final int BORINGSSL_DIV_THRESHOLD = 40;
+    private static final int BORINGSSL_DIV_OFFSET = 20;
+
+    /**
+     * Returns a BigInteger whose value is {@code (this / val)}.
+     *
+     * @param  val value by which this BigInteger is to be divided.
+     * @return {@code this / val}
+     * @throws ArithmeticException if {@code val} is zero.
+     */
+    @NonNull public BigInteger divide(@NonNull BigInteger val) {
+        // if (val.mag.length < BURNIKEL_ZIEGLER_THRESHOLD ||
+        //        mag.length - val.mag.length < BURNIKEL_ZIEGLER_OFFSET) {
+        if (mag.length < BORINGSSL_DIV_THRESHOLD ||
+                mag.length - val.mag.length < BORINGSSL_DIV_OFFSET) {
+            return divideKnuth(val);
+        } else {
+            return divideAndRemainder(val)[0];
+            // return divideBurnikelZiegler(val);
+        }
+    }
+    // END Android-modified: Fall back to boringssl for large problems.
+
+
+    /**
+     * Returns a BigInteger whose value is {@code (this / val)} using an O(n^2) algorithm from Knuth.
+     *
+     * @param  val value by which this BigInteger is to be divided.
+     * @return {@code this / val}
+     * @throws ArithmeticException if {@code val} is zero.
+     * @see MutableBigInteger#divideKnuth(MutableBigInteger, MutableBigInteger, boolean)
+     */
+    @NonNull private BigInteger divideKnuth(@NonNull BigInteger val) {
+        MutableBigInteger q = new MutableBigInteger(),
+                          a = new MutableBigInteger(this.mag),
+                          b = new MutableBigInteger(val.mag);
+
+        a.divideKnuth(b, q, false);
+        return q.toBigInteger(this.signum * val.signum);
+    }
+
+    /**
+     * Returns an array of two BigIntegers containing {@code (this / val)}
+     * followed by {@code (this % val)}.
+     *
+     * @param  val value by which this BigInteger is to be divided, and the
+     *         remainder computed.
+     * @return an array of two BigIntegers: the quotient {@code (this / val)}
+     *         is the initial element, and the remainder {@code (this % val)}
+     *         is the final element.
+     * @throws ArithmeticException if {@code val} is zero.
+     */
+    @NonNull public BigInteger[] divideAndRemainder(@NonNull BigInteger val) {
+        // BEGIN Android-modified: Fall back to boringssl for large problems.
+
+        // if (val.mag.length < BURNIKEL_ZIEGLER_THRESHOLD ||
+        //        mag.length - val.mag < BURNIKEL_ZIEGLER_OFFSET) {
+        if (val.mag.length < BORINGSSL_DIV_THRESHOLD ||
+                mag.length < BORINGSSL_DIV_OFFSET ||
+                mag.length - val.mag.length < BORINGSSL_DIV_OFFSET) {
+            return divideAndRemainderKnuth(val);
+        } else {
+            int quotSign = signum == val.signum ? 1 : -1;  // 0 divided doesn't get here.
+            long xBN = 0, yBN = 0, quotBN = 0, remBN = 0;
+            try {
+                xBN = bigEndInts2NewBN(mag, /* neg= */false);
+                yBN = bigEndInts2NewBN(val.mag, /* neg= */false);
+                quotBN = NativeBN.BN_new();
+                remBN = NativeBN.BN_new();
+                NativeBN.BN_div(quotBN, remBN, xBN, yBN);
+                BigInteger quotient = new BigInteger(quotSign, bn2BigEndInts(quotBN));
+                        // The sign of a zero quotient is fixed by the constructor.
+                BigInteger remainder = new BigInteger(signum, bn2BigEndInts(remBN));
+                BigInteger[] result = {quotient, remainder};
+                return result;
+            } finally {
+                NativeBN.BN_free(xBN);
+                NativeBN.BN_free(yBN);
+                NativeBN.BN_free(quotBN);
+                NativeBN.BN_free(remBN);
+            }
+            // return divideAndRemainderBurnikelZiegler(val);
+        }
+        // END Android-modified: Fall back to boringssl for large problems.
+    }
+
+    /** Long division */
+    @NonNull private BigInteger[] divideAndRemainderKnuth(@NonNull BigInteger val) {
+        BigInteger[] result = new BigInteger[2];
+        MutableBigInteger q = new MutableBigInteger(),
+                          a = new MutableBigInteger(this.mag),
+                          b = new MutableBigInteger(val.mag);
+        MutableBigInteger r = a.divideKnuth(b, q);
+        result[0] = q.toBigInteger(this.signum == val.signum ? 1 : -1);
+        result[1] = r.toBigInteger(this.signum);
+        return result;
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this % val)}.
+     *
+     * @param  val value by which this BigInteger is to be divided, and the
+     *         remainder computed.
+     * @return {@code this % val}
+     * @throws ArithmeticException if {@code val} is zero.
+     */
+    @NonNull public BigInteger remainder(@NonNull BigInteger val) {
+        // BEGIN Android-modified: Fall back to boringssl for large problems.
+        // if (val.mag.length < BURNIKEL_ZIEGLER_THRESHOLD ||
+        //        mag.length - val.mag.length < BURNIKEL_ZIEGLER_OFFSET) {
+        if (val.mag.length < BORINGSSL_DIV_THRESHOLD ||
+                mag.length - val.mag.length < BORINGSSL_DIV_THRESHOLD) {
+            return remainderKnuth(val);
+        } else {
+            return divideAndRemainder(val)[1];
+            // return remainderBurnikelZiegler(val);
+        }
+        // END Android-modified: Fall back to boringssl for large problems.
+    }
+
+    /** Long division */
+    @NonNull private BigInteger remainderKnuth(@NonNull BigInteger val) {
+        MutableBigInteger q = new MutableBigInteger(),
+                          a = new MutableBigInteger(this.mag),
+                          b = new MutableBigInteger(val.mag);
+
+        return a.divideKnuth(b, q).toBigInteger(this.signum);
+    }
+
+    /**
+     * Calculates {@code this / val} using the Burnikel-Ziegler algorithm.
+     * @param  val the divisor
+     * @return {@code this / val}
+     */
+    @NonNull private BigInteger divideBurnikelZiegler(@NonNull BigInteger val) {
+        return divideAndRemainderBurnikelZiegler(val)[0];
+    }
+
+    /**
+     * Calculates {@code this % val} using the Burnikel-Ziegler algorithm.
+     * @param val the divisor
+     * @return {@code this % val}
+     */
+    @NonNull private BigInteger remainderBurnikelZiegler(@NonNull BigInteger val) {
+        return divideAndRemainderBurnikelZiegler(val)[1];
+    }
+
+    /**
+     * Computes {@code this / val} and {@code this % val} using the
+     * Burnikel-Ziegler algorithm.
+     * @param val the divisor
+     * @return an array containing the quotient and remainder
+     */
+    @NonNull private BigInteger[] divideAndRemainderBurnikelZiegler(@NonNull BigInteger val) {
+        MutableBigInteger q = new MutableBigInteger();
+        MutableBigInteger r = new MutableBigInteger(this).divideAndRemainderBurnikelZiegler(new MutableBigInteger(val), q);
+        BigInteger qBigInt = q.isZero() ? ZERO : q.toBigInteger(signum*val.signum);
+        BigInteger rBigInt = r.isZero() ? ZERO : r.toBigInteger(signum);
+        return new BigInteger[] {qBigInt, rBigInt};
+    }
+
+    /**
+     * Returns a BigInteger whose value is <tt>(this<sup>exponent</sup>)</tt>.
+     * Note that {@code exponent} is an integer rather than a BigInteger.
+     *
+     * @param  exponent exponent to which this BigInteger is to be raised.
+     * @return <tt>this<sup>exponent</sup></tt>
+     * @throws ArithmeticException {@code exponent} is negative.  (This would
+     *         cause the operation to yield a non-integer value.)
+     */
+    @NonNull public BigInteger pow(int exponent) {
+        if (exponent < 0) {
+            throw new ArithmeticException("Negative exponent");
+        }
+        if (signum == 0) {
+            return (exponent == 0 ? ONE : this);
+        }
+
+        BigInteger partToSquare = this.abs();
+
+        // Factor out powers of two from the base, as the exponentiation of
+        // these can be done by left shifts only.
+        // The remaining part can then be exponentiated faster.  The
+        // powers of two will be multiplied back at the end.
+        int powersOfTwo = partToSquare.getLowestSetBit();
+        long bitsToShiftLong = (long)powersOfTwo * exponent;
+        if (bitsToShiftLong > Integer.MAX_VALUE) {
+            reportOverflow();
+        }
+        int bitsToShift = (int)bitsToShiftLong;
+
+        int remainingBits;
+
+        // Factor the powers of two out quickly by shifting right, if needed.
+        if (powersOfTwo > 0) {
+            partToSquare = partToSquare.shiftRight(powersOfTwo);
+            remainingBits = partToSquare.bitLength();
+            if (remainingBits == 1) {  // Nothing left but +/- 1?
+                if (signum < 0 && (exponent&1) == 1) {
+                    return NEGATIVE_ONE.shiftLeft(bitsToShift);
+                } else {
+                    return ONE.shiftLeft(bitsToShift);
+                }
+            }
+        } else {
+            remainingBits = partToSquare.bitLength();
+            if (remainingBits == 1) { // Nothing left but +/- 1?
+                if (signum < 0  && (exponent&1) == 1) {
+                    return NEGATIVE_ONE;
+                } else {
+                    return ONE;
+                }
+            }
+        }
+
+        // This is a quick way to approximate the size of the result,
+        // similar to doing log2[n] * exponent.  This will give an upper bound
+        // of how big the result can be, and which algorithm to use.
+        long scaleFactor = (long)remainingBits * exponent;
+
+        // Use slightly different algorithms for small and large operands.
+        // See if the result will safely fit into a long. (Largest 2^63-1)
+        if (partToSquare.mag.length == 1 && scaleFactor <= 62) {
+            // Small number algorithm.  Everything fits into a long.
+            int newSign = (signum <0  && (exponent&1) == 1 ? -1 : 1);
+            long result = 1;
+            long baseToPow2 = partToSquare.mag[0] & LONG_MASK;
+
+            int workingExponent = exponent;
+
+            // Perform exponentiation using repeated squaring trick
+            while (workingExponent != 0) {
+                if ((workingExponent & 1) == 1) {
+                    result = result * baseToPow2;
+                }
+
+                if ((workingExponent >>>= 1) != 0) {
+                    baseToPow2 = baseToPow2 * baseToPow2;
+                }
+            }
+
+            // Multiply back the powers of two (quickly, by shifting left)
+            if (powersOfTwo > 0) {
+                if (bitsToShift + scaleFactor <= 62) { // Fits in long?
+                    return valueOf((result << bitsToShift) * newSign);
+                } else {
+                    return valueOf(result*newSign).shiftLeft(bitsToShift);
+                }
+            } else {
+                return valueOf(result*newSign);
+            }
+        } else {
+            if ((long)bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) {
+                reportOverflow();
+            }
+
+            // Large number algorithm.  This is basically identical to
+            // the algorithm above, but calls multiply() and square()
+            // which may use more efficient algorithms for large numbers.
+            BigInteger answer = ONE;
+
+            int workingExponent = exponent;
+            // Perform exponentiation using repeated squaring trick
+            while (workingExponent != 0) {
+                if ((workingExponent & 1) == 1) {
+                    answer = answer.multiply(partToSquare);
+                }
+
+                if ((workingExponent >>>= 1) != 0) {
+                    partToSquare = partToSquare.square();
+                }
+            }
+            // Multiply back the (exponentiated) powers of two (quickly,
+            // by shifting left)
+            if (powersOfTwo > 0) {
+                answer = answer.shiftLeft(bitsToShift);
+            }
+
+            if (signum < 0 && (exponent&1) == 1) {
+                return answer.negate();
+            } else {
+                return answer;
+            }
+        }
+    }
+
+    /**
+     * Returns a BigInteger whose value is the greatest common divisor of
+     * {@code abs(this)} and {@code abs(val)}.  Returns 0 if
+     * {@code this == 0 && val == 0}.
+     *
+     * @param  val value with which the GCD is to be computed.
+     * @return {@code GCD(abs(this), abs(val))}
+     */
+    @NonNull public BigInteger gcd(@NonNull BigInteger val) {
+        if (val.signum == 0)
+            return this.abs();
+        else if (this.signum == 0)
+            return val.abs();
+
+        MutableBigInteger a = new MutableBigInteger(this);
+        MutableBigInteger b = new MutableBigInteger(val);
+
+        MutableBigInteger result = a.hybridGCD(b);
+
+        return result.toBigInteger(1);
+    }
+
+    /**
+     * Package private method to return bit length for an integer.
+     */
+    static int bitLengthForInt(int n) {
+        return 32 - Integer.numberOfLeadingZeros(n);
+    }
+
+    /**
+     * Left shift int array a up to len by n bits. Returns the array that
+     * results from the shift since space may have to be reallocated.
+     */
+    private static int[] leftShift(int[] a, int len, int n) {
+        int nInts = n >>> 5;
+        int nBits = n&0x1F;
+        int bitsInHighWord = bitLengthForInt(a[0]);
+
+        // If shift can be done without recopy, do so
+        if (n <= (32-bitsInHighWord)) {
+            primitiveLeftShift(a, len, nBits);
+            return a;
+        } else { // Array must be resized
+            if (nBits <= (32-bitsInHighWord)) {
+                int result[] = new int[nInts+len];
+                System.arraycopy(a, 0, result, 0, len);
+                primitiveLeftShift(result, result.length, nBits);
+                return result;
+            } else {
+                int result[] = new int[nInts+len+1];
+                System.arraycopy(a, 0, result, 0, len);
+                primitiveRightShift(result, result.length, 32 - nBits);
+                return result;
+            }
+        }
+    }
+
+    // shifts a up to len right n bits assumes no leading zeros, 0<n<32
+    static void primitiveRightShift(int[] a, int len, int n) {
+        int n2 = 32 - n;
+        for (int i=len-1, c=a[i]; i > 0; i--) {
+            int b = c;
+            c = a[i-1];
+            a[i] = (c << n2) | (b >>> n);
+        }
+        a[0] >>>= n;
+    }
+
+    // shifts a up to len left n bits assumes no leading zeros, 0<=n<32
+    static void primitiveLeftShift(int[] a, int len, int n) {
+        if (len == 0 || n == 0)
+            return;
+
+        int n2 = 32 - n;
+        for (int i=0, c=a[i], m=i+len-1; i < m; i++) {
+            int b = c;
+            c = a[i+1];
+            a[i] = (b << n) | (c >>> n2);
+        }
+        a[len-1] <<= n;
+    }
+
+    /**
+     * Calculate bitlength of contents of the first len elements an int array,
+     * assuming there are no leading zero ints.
+     */
+    private static int bitLength(int[] val, int len) {
+        if (len == 0)
+            return 0;
+        return ((len - 1) << 5) + bitLengthForInt(val[0]);
+    }
+
+    /**
+     * Returns a BigInteger whose value is the absolute value of this
+     * BigInteger.
+     *
+     * @return {@code abs(this)}
+     */
+    @NonNull public BigInteger abs() {
+        return (signum >= 0 ? this : this.negate());
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (-this)}.
+     *
+     * @return {@code -this}
+     */
+    @NonNull public BigInteger negate() {
+        return new BigInteger(this.mag, -this.signum);
+    }
+
+    /**
+     * Returns the signum function of this BigInteger.
+     *
+     * @return -1, 0 or 1 as the value of this BigInteger is negative, zero or
+     *         positive.
+     */
+    public int signum() {
+        return this.signum;
+    }
+
+    // Modular Arithmetic Operations
+
+    /**
+     * Returns a BigInteger whose value is {@code (this mod m}).  This method
+     * differs from {@code remainder} in that it always returns a
+     * <i>non-negative</i> BigInteger.
+     *
+     * @param  m the modulus.
+     * @return {@code this mod m}
+     * @throws ArithmeticException {@code m} &le; 0
+     * @see    #remainder
+     */
+    @NonNull public BigInteger mod(@NonNull BigInteger m) {
+        if (m.signum <= 0)
+            throw new ArithmeticException("BigInteger: modulus not positive");
+
+        BigInteger result = this.remainder(m);
+        return (result.signum >= 0 ? result : result.add(m));
+    }
+
+    // BEGIN Android-added: Support fallback to boringssl where it makes sense.
+    // The conversion itself takes linear time, so this only makes sense for largish superlinear
+    // operations.
+
+    private static int[] reverse(int[] arg) {
+      int len = arg.length;
+      int[] result = new int[len];
+      for (int i = 0; i < len; ++i) {
+        result[i] = arg[len - i - 1];
+      }
+      return result;
+    }
+
+    private static long /* BN */ bigEndInts2NewBN(int[] beArray, boolean neg) {
+      // The input is an array of ints arranged in big-endian order, i.e. most significant int
+      // first. BN deals with big-endian or little-endian byte arrays, so we need to reverse order.
+      int[] leArray = reverse(beArray);
+      long resultBN = NativeBN.BN_new();
+      NativeBN.litEndInts2bn(leArray, leArray.length, neg, resultBN);
+      return resultBN;
+    }
+
+    private int[] bn2BigEndInts(long bn) {
+      return reverse(NativeBN.bn2litEndInts(bn));
+    }
+
+    // END Android-added: Support fallback to boringssl.
+
+
+    /**
+     * Returns a BigInteger whose value is
+     * <tt>(this<sup>exponent</sup> mod m)</tt>.  (Unlike {@code pow}, this
+     * method permits negative exponents.)
+     *
+     * @param  exponent the exponent.
+     * @param  m the modulus.
+     * @return <tt>this<sup>exponent</sup> mod m</tt>
+     * @throws ArithmeticException {@code m} &le; 0 or the exponent is
+     *         negative and this BigInteger is not <i>relatively
+     *         prime</i> to {@code m}.
+     * @see    #modInverse
+     */
+    @NonNull public BigInteger modPow(@NonNull BigInteger exponent, @NonNull BigInteger m) {
+        if (m.signum <= 0)
+            throw new ArithmeticException("BigInteger: modulus not positive");
+
+        // Trivial cases
+        if (exponent.signum == 0)
+            return (m.equals(ONE) ? ZERO : ONE);
+
+        if (this.equals(ONE))
+            return (m.equals(ONE) ? ZERO : ONE);
+
+        if (this.equals(ZERO) && exponent.signum >= 0)
+            return ZERO;
+
+        if (this.equals(negConst[1]) && (!exponent.testBit(0)))
+            return (m.equals(ONE) ? ZERO : ONE);
+
+        boolean invertResult;
+        if ((invertResult = (exponent.signum < 0)))
+            exponent = exponent.negate();
+
+        BigInteger base = (this.signum < 0 || this.compareTo(m) >= 0
+                           ? this.mod(m) : this);
+        BigInteger result;
+        // BEGIN Android-added: Fall back to the boringssl implementation, which
+        // is usually faster.
+        final int BORINGSSL_MOD_EXP_THRESHOLD = 3;
+        if (m.mag.length >= BORINGSSL_MOD_EXP_THRESHOLD) {
+            long baseBN = 0, expBN = 0, modBN = 0, resultBN = 0;
+            try {
+                baseBN = bigEndInts2NewBN(base.mag, /* neg= */false);
+                expBN = bigEndInts2NewBN(exponent.mag, /* neg= */false);
+                modBN = bigEndInts2NewBN(m.mag, /* neg= */false);
+                resultBN = NativeBN.BN_new();
+                NativeBN.BN_mod_exp(resultBN, baseBN, expBN, modBN);
+                result = new BigInteger(1, bn2BigEndInts(resultBN));
+                        // The sign of a zero result is fixed by the constructor.
+                return (invertResult ? result.modInverse(m) : result);
+            } finally {
+                NativeBN.BN_free(baseBN);
+                NativeBN.BN_free(expBN);
+                NativeBN.BN_free(modBN);
+                NativeBN.BN_free(resultBN);
+            }
+        }
+        // END Android-added: Fall back to the boringssl implementation.
+        if (m.testBit(0)) { // odd modulus
+            result = base.oddModPow(exponent, m);
+        } else {
+            /*
+             * Even modulus.  Tear it into an "odd part" (m1) and power of two
+             * (m2), exponentiate mod m1, manually exponentiate mod m2, and
+             * use Chinese Remainder Theorem to combine results.
+             */
+
+            // Tear m apart into odd part (m1) and power of 2 (m2)
+            int p = m.getLowestSetBit();   // Max pow of 2 that divides m
+
+            BigInteger m1 = m.shiftRight(p);  // m/2**p
+            BigInteger m2 = ONE.shiftLeft(p); // 2**p
+
+            // Calculate new base from m1
+            BigInteger base2 = (this.signum < 0 || this.compareTo(m1) >= 0
+                                ? this.mod(m1) : this);
+
+            // Calculate (base ** exponent) mod m1.
+            BigInteger a1 = (m1.equals(ONE) ? ZERO :
+                             base2.oddModPow(exponent, m1));
+
+            // Calculate (this ** exponent) mod m2
+            BigInteger a2 = base.modPow2(exponent, p);
+
+            // Combine results using Chinese Remainder Theorem
+            BigInteger y1 = m2.modInverse(m1);
+            BigInteger y2 = m1.modInverse(m2);
+
+            if (m.mag.length < MAX_MAG_LENGTH / 2) {
+                result = a1.multiply(m2).multiply(y1).add(a2.multiply(m1).multiply(y2)).mod(m);
+            } else {
+                MutableBigInteger t1 = new MutableBigInteger();
+                new MutableBigInteger(a1.multiply(m2)).multiply(new MutableBigInteger(y1), t1);
+                MutableBigInteger t2 = new MutableBigInteger();
+                new MutableBigInteger(a2.multiply(m1)).multiply(new MutableBigInteger(y2), t2);
+                t1.add(t2);
+                MutableBigInteger q = new MutableBigInteger();
+                result = t1.divide(new MutableBigInteger(m), q).toBigInteger();
+            }
+        }
+
+        return (invertResult ? result.modInverse(m) : result);
+    }
+
+    // Montgomery multiplication.  These are wrappers for
+    // implMontgomeryXX routines which are expected to be replaced by
+    // virtual machine intrinsics.  We don't use the intrinsics for
+    // very large operands: MONTGOMERY_INTRINSIC_THRESHOLD should be
+    // larger than any reasonable crypto key.
+    private static int[] montgomeryMultiply(int[] a, int[] b, int[] n, int len, long inv,
+                                            int[] product) {
+        implMontgomeryMultiplyChecks(a, b, n, len, product);
+        if (len > MONTGOMERY_INTRINSIC_THRESHOLD) {
+            // Very long argument: do not use an intrinsic
+            product = multiplyToLen(a, len, b, len, product);
+            return montReduce(product, n, len, (int)inv);
+        } else {
+            return implMontgomeryMultiply(a, b, n, len, inv, materialize(product, len));
+        }
+    }
+    private static int[] montgomerySquare(int[] a, int[] n, int len, long inv,
+                                          int[] product) {
+        implMontgomeryMultiplyChecks(a, a, n, len, product);
+        if (len > MONTGOMERY_INTRINSIC_THRESHOLD) {
+            // Very long argument: do not use an intrinsic
+            product = squareToLen(a, len, product);
+            return montReduce(product, n, len, (int)inv);
+        } else {
+            return implMontgomerySquare(a, n, len, inv, materialize(product, len));
+        }
+    }
+
+    // Range-check everything.
+    private static void implMontgomeryMultiplyChecks
+        (int[] a, int[] b, int[] n, int len, int[] product) throws RuntimeException {
+        if (len % 2 != 0) {
+            throw new IllegalArgumentException("input array length must be even: " + len);
+        }
+
+        if (len < 1) {
+            throw new IllegalArgumentException("invalid input length: " + len);
+        }
+
+        if (len > a.length ||
+            len > b.length ||
+            len > n.length ||
+            (product != null && len > product.length)) {
+            throw new IllegalArgumentException("input array length out of bound: " + len);
+        }
+    }
+
+    // Make sure that the int array z (which is expected to contain
+    // the result of a Montgomery multiplication) is present and
+    // sufficiently large.
+    private static int[] materialize(int[] z, int len) {
+         if (z == null || z.length < len)
+             z = new int[len];
+         return z;
+    }
+
+    // These methods are intended to be be replaced by virtual machine
+    // intrinsics.
+    private static int[] implMontgomeryMultiply(int[] a, int[] b, int[] n, int len,
+                                         long inv, int[] product) {
+        product = multiplyToLen(a, len, b, len, product);
+        return montReduce(product, n, len, (int)inv);
+    }
+    private static int[] implMontgomerySquare(int[] a, int[] n, int len,
+                                       long inv, int[] product) {
+        product = squareToLen(a, len, product);
+        return montReduce(product, n, len, (int)inv);
+    }
+
+    static int[] bnExpModThreshTable = {7, 25, 81, 241, 673, 1793,
+                                                Integer.MAX_VALUE}; // Sentinel
+
+    /**
+     * Returns a BigInteger whose value is x to the power of y mod z.
+     * Assumes: z is odd && x < z.
+     */
+    @NonNull private BigInteger oddModPow(@NonNull BigInteger y, @NonNull BigInteger z) {
+    /*
+     * The algorithm is adapted from Colin Plumb's C library.
+     *
+     * The window algorithm:
+     * The idea is to keep a running product of b1 = n^(high-order bits of exp)
+     * and then keep appending exponent bits to it.  The following patterns
+     * apply to a 3-bit window (k = 3):
+     * To append   0: square
+     * To append   1: square, multiply by n^1
+     * To append  10: square, multiply by n^1, square
+     * To append  11: square, square, multiply by n^3
+     * To append 100: square, multiply by n^1, square, square
+     * To append 101: square, square, square, multiply by n^5
+     * To append 110: square, square, multiply by n^3, square
+     * To append 111: square, square, square, multiply by n^7
+     *
+     * Since each pattern involves only one multiply, the longer the pattern
+     * the better, except that a 0 (no multiplies) can be appended directly.
+     * We precompute a table of odd powers of n, up to 2^k, and can then
+     * multiply k bits of exponent at a time.  Actually, assuming random
+     * exponents, there is on average one zero bit between needs to
+     * multiply (1/2 of the time there's none, 1/4 of the time there's 1,
+     * 1/8 of the time, there's 2, 1/32 of the time, there's 3, etc.), so
+     * you have to do one multiply per k+1 bits of exponent.
+     *
+     * The loop walks down the exponent, squaring the result buffer as
+     * it goes.  There is a wbits+1 bit lookahead buffer, buf, that is
+     * filled with the upcoming exponent bits.  (What is read after the
+     * end of the exponent is unimportant, but it is filled with zero here.)
+     * When the most-significant bit of this buffer becomes set, i.e.
+     * (buf & tblmask) != 0, we have to decide what pattern to multiply
+     * by, and when to do it.  We decide, remember to do it in future
+     * after a suitable number of squarings have passed (e.g. a pattern
+     * of "100" in the buffer requires that we multiply by n^1 immediately;
+     * a pattern of "110" calls for multiplying by n^3 after one more
+     * squaring), clear the buffer, and continue.
+     *
+     * When we start, there is one more optimization: the result buffer
+     * is implcitly one, so squaring it or multiplying by it can be
+     * optimized away.  Further, if we start with a pattern like "100"
+     * in the lookahead window, rather than placing n into the buffer
+     * and then starting to square it, we have already computed n^2
+     * to compute the odd-powers table, so we can place that into
+     * the buffer and save a squaring.
+     *
+     * This means that if you have a k-bit window, to compute n^z,
+     * where z is the high k bits of the exponent, 1/2 of the time
+     * it requires no squarings.  1/4 of the time, it requires 1
+     * squaring, ... 1/2^(k-1) of the time, it requires k-2 squarings.
+     * And the remaining 1/2^(k-1) of the time, the top k bits are a
+     * 1 followed by k-1 0 bits, so it again only requires k-2
+     * squarings, not k-1.  The average of these is 1.  Add that
+     * to the one squaring we have to do to compute the table,
+     * and you'll see that a k-bit window saves k-2 squarings
+     * as well as reducing the multiplies.  (It actually doesn't
+     * hurt in the case k = 1, either.)
+     */
+        // Special case for exponent of one
+        if (y.equals(ONE))
+            return this;
+
+        // Special case for base of zero
+        if (signum == 0)
+            return ZERO;
+
+        int[] base = mag.clone();
+        int[] exp = y.mag;
+        int[] mod = z.mag;
+        int modLen = mod.length;
+
+        // Make modLen even. It is conventional to use a cryptographic
+        // modulus that is 512, 768, 1024, or 2048 bits, so this code
+        // will not normally be executed. However, it is necessary for
+        // the correct functioning of the HotSpot intrinsics.
+        if ((modLen & 1) != 0) {
+            int[] x = new int[modLen + 1];
+            System.arraycopy(mod, 0, x, 1, modLen);
+            mod = x;
+            modLen++;
+        }
+
+        // Select an appropriate window size
+        int wbits = 0;
+        int ebits = bitLength(exp, exp.length);
+        // if exponent is 65537 (0x10001), use minimum window size
+        if ((ebits != 17) || (exp[0] != 65537)) {
+            while (ebits > bnExpModThreshTable[wbits]) {
+                wbits++;
+            }
+        }
+
+        // Calculate appropriate table size
+        int tblmask = 1 << wbits;
+
+        // Allocate table for precomputed odd powers of base in Montgomery form
+        int[][] table = new int[tblmask][];
+        for (int i=0; i < tblmask; i++)
+            table[i] = new int[modLen];
+
+        // Compute the modular inverse of the least significant 64-bit
+        // digit of the modulus
+        long n0 = (mod[modLen-1] & LONG_MASK) + ((mod[modLen-2] & LONG_MASK) << 32);
+        long inv = -MutableBigInteger.inverseMod64(n0);
+
+        // Convert base to Montgomery form
+        int[] a = leftShift(base, base.length, modLen << 5);
+
+        MutableBigInteger q = new MutableBigInteger(),
+                          a2 = new MutableBigInteger(a),
+                          b2 = new MutableBigInteger(mod);
+        b2.normalize(); // MutableBigInteger.divide() assumes that its
+                        // divisor is in normal form.
+
+        MutableBigInteger r= a2.divide(b2, q);
+        table[0] = r.toIntArray();
+
+        // Pad table[0] with leading zeros so its length is at least modLen
+        if (table[0].length < modLen) {
+           int offset = modLen - table[0].length;
+           int[] t2 = new int[modLen];
+           System.arraycopy(table[0], 0, t2, offset, table[0].length);
+           table[0] = t2;
+        }
+
+        // Set b to the square of the base
+        int[] b = montgomerySquare(table[0], mod, modLen, inv, null);
+
+        // Set t to high half of b
+        int[] t = Arrays.copyOf(b, modLen);
+
+        // Fill in the table with odd powers of the base
+        for (int i=1; i < tblmask; i++) {
+            table[i] = montgomeryMultiply(t, table[i-1], mod, modLen, inv, null);
+        }
+
+        // Pre load the window that slides over the exponent
+        int bitpos = 1 << ((ebits-1) & (32-1));
+
+        int buf = 0;
+        int elen = exp.length;
+        int eIndex = 0;
+        for (int i = 0; i <= wbits; i++) {
+            buf = (buf << 1) | (((exp[eIndex] & bitpos) != 0)?1:0);
+            bitpos >>>= 1;
+            if (bitpos == 0) {
+                eIndex++;
+                bitpos = 1 << (32-1);
+                elen--;
+            }
+        }
+
+        int multpos = ebits;
+
+        // The first iteration, which is hoisted out of the main loop
+        ebits--;
+        boolean isone = true;
+
+        multpos = ebits - wbits;
+        while ((buf & 1) == 0) {
+            buf >>>= 1;
+            multpos++;
+        }
+
+        int[] mult = table[buf >>> 1];
+
+        buf = 0;
+        if (multpos == ebits)
+            isone = false;
+
+        // The main loop
+        while (true) {
+            ebits--;
+            // Advance the window
+            buf <<= 1;
+
+            if (elen != 0) {
+                buf |= ((exp[eIndex] & bitpos) != 0) ? 1 : 0;
+                bitpos >>>= 1;
+                if (bitpos == 0) {
+                    eIndex++;
+                    bitpos = 1 << (32-1);
+                    elen--;
+                }
+            }
+
+            // Examine the window for pending multiplies
+            if ((buf & tblmask) != 0) {
+                multpos = ebits - wbits;
+                while ((buf & 1) == 0) {
+                    buf >>>= 1;
+                    multpos++;
+                }
+                mult = table[buf >>> 1];
+                buf = 0;
+            }
+
+            // Perform multiply
+            if (ebits == multpos) {
+                if (isone) {
+                    b = mult.clone();
+                    isone = false;
+                } else {
+                    t = b;
+                    a = montgomeryMultiply(t, mult, mod, modLen, inv, a);
+                    t = a; a = b; b = t;
+                }
+            }
+
+            // Check if done
+            if (ebits == 0)
+                break;
+
+            // Square the input
+            if (!isone) {
+                t = b;
+                a = montgomerySquare(t, mod, modLen, inv, a);
+                t = a; a = b; b = t;
+            }
+        }
+
+        // Convert result out of Montgomery form and return
+        int[] t2 = new int[2*modLen];
+        System.arraycopy(b, 0, t2, modLen, modLen);
+
+        b = montReduce(t2, mod, modLen, (int)inv);
+
+        t2 = Arrays.copyOf(b, modLen);
+
+        return new BigInteger(1, t2);
+    }
+
+    /**
+     * Montgomery reduce n, modulo mod.  This reduces modulo mod and divides
+     * by 2^(32*mlen). Adapted from Colin Plumb's C library.
+     */
+    private static int[] montReduce(int[] n, int[] mod, int mlen, int inv) {
+        int c=0;
+        int len = mlen;
+        int offset=0;
+
+        do {
+            int nEnd = n[n.length-1-offset];
+            int carry = mulAdd(n, mod, offset, mlen, inv * nEnd);
+            c += addOne(n, offset, mlen, carry);
+            offset++;
+        } while (--len > 0);
+
+        while (c > 0)
+            c += subN(n, mod, mlen);
+
+        while (intArrayCmpToLen(n, mod, mlen) >= 0)
+            subN(n, mod, mlen);
+
+        return n;
+    }
+
+
+    /*
+     * Returns -1, 0 or +1 as big-endian unsigned int array arg1 is less than,
+     * equal to, or greater than arg2 up to length len.
+     */
+    private static int intArrayCmpToLen(int[] arg1, int[] arg2, int len) {
+        for (int i=0; i < len; i++) {
+            long b1 = arg1[i] & LONG_MASK;
+            long b2 = arg2[i] & LONG_MASK;
+            if (b1 < b2)
+                return -1;
+            if (b1 > b2)
+                return 1;
+        }
+        return 0;
+    }
+
+    /**
+     * Subtracts two numbers of same length, returning borrow.
+     */
+    private static int subN(int[] a, int[] b, int len) {
+        long sum = 0;
+
+        while (--len >= 0) {
+            sum = (a[len] & LONG_MASK) -
+                 (b[len] & LONG_MASK) + (sum >> 32);
+            a[len] = (int)sum;
+        }
+
+        return (int)(sum >> 32);
+    }
+
+    /**
+     * Multiply an array by one word k and add to result, return the carry
+     */
+    static int mulAdd(int[] out, int[] in, int offset, int len, int k) {
+        implMulAddCheck(out, in, offset, len, k);
+        return implMulAdd(out, in, offset, len, k);
+    }
+
+    /**
+     * Parameters validation.
+     */
+    private static void implMulAddCheck(int[] out, int[] in, int offset, int len, int k) {
+        if (len > in.length) {
+            throw new IllegalArgumentException("input length is out of bound: " + len + " > " + in.length);
+        }
+        if (offset < 0) {
+            throw new IllegalArgumentException("input offset is invalid: " + offset);
+        }
+        if (offset > (out.length - 1)) {
+            throw new IllegalArgumentException("input offset is out of bound: " + offset + " > " + (out.length - 1));
+        }
+        if (len > (out.length - offset)) {
+            throw new IllegalArgumentException("input len is out of bound: " + len + " > " + (out.length - offset));
+        }
+    }
+
+    /**
+     * Java Runtime may use intrinsic for this method.
+     */
+    private static int implMulAdd(int[] out, int[] in, int offset, int len, int k) {
+        long kLong = k & LONG_MASK;
+        long carry = 0;
+
+        offset = out.length-offset - 1;
+        for (int j=len-1; j >= 0; j--) {
+            long product = (in[j] & LONG_MASK) * kLong +
+                           (out[offset] & LONG_MASK) + carry;
+            out[offset--] = (int)product;
+            carry = product >>> 32;
+        }
+        return (int)carry;
+    }
+
+    /**
+     * Add one word to the number a mlen words into a. Return the resulting
+     * carry.
+     */
+    static int addOne(int[] a, int offset, int mlen, int carry) {
+        offset = a.length-1-mlen-offset;
+        long t = (a[offset] & LONG_MASK) + (carry & LONG_MASK);
+
+        a[offset] = (int)t;
+        if ((t >>> 32) == 0)
+            return 0;
+        while (--mlen >= 0) {
+            if (--offset < 0) { // Carry out of number
+                return 1;
+            } else {
+                a[offset]++;
+                if (a[offset] != 0)
+                    return 0;
+            }
+        }
+        return 1;
+    }
+
+    /**
+     * Returns a BigInteger whose value is (this ** exponent) mod (2**p)
+     */
+    @NonNull private BigInteger modPow2(@NonNull BigInteger exponent, int p) {
+        /*
+         * Perform exponentiation using repeated squaring trick, chopping off
+         * high order bits as indicated by modulus.
+         */
+        BigInteger result = ONE;
+        BigInteger baseToPow2 = this.mod2(p);
+        int expOffset = 0;
+
+        int limit = exponent.bitLength();
+
+        if (this.testBit(0))
+           limit = (p-1) < limit ? (p-1) : limit;
+
+        while (expOffset < limit) {
+            if (exponent.testBit(expOffset))
+                result = result.multiply(baseToPow2).mod2(p);
+            expOffset++;
+            if (expOffset < limit)
+                baseToPow2 = baseToPow2.square().mod2(p);
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a BigInteger whose value is this mod(2**p).
+     * Assumes that this {@code BigInteger >= 0} and {@code p > 0}.
+     */
+    @NonNull private BigInteger mod2(int p) {
+        if (bitLength() <= p)
+            return this;
+
+        // Copy remaining ints of mag
+        int numInts = (p + 31) >>> 5;
+        int[] mag = new int[numInts];
+        System.arraycopy(this.mag, (this.mag.length - numInts), mag, 0, numInts);
+
+        // Mask out any excess bits
+        int excessBits = (numInts << 5) - p;
+        mag[0] &= (1L << (32-excessBits)) - 1;
+
+        return (mag[0] == 0 ? new BigInteger(1, mag) : new BigInteger(mag, 1));
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this}<sup>-1</sup> {@code mod m)}.
+     *
+     * @param  m the modulus.
+     * @return {@code this}<sup>-1</sup> {@code mod m}.
+     * @throws ArithmeticException {@code  m} &le; 0, or this BigInteger
+     *         has no multiplicative inverse mod m (that is, this BigInteger
+     *         is not <i>relatively prime</i> to m).
+     */
+    @NonNull public BigInteger modInverse(@NonNull BigInteger m) {
+        if (m.signum != 1)
+            throw new ArithmeticException("BigInteger: modulus not positive");
+
+        if (m.equals(ONE))
+            return ZERO;
+
+        // Calculate (this mod m)
+        BigInteger modVal = this;
+        if (signum < 0 || (this.compareMagnitude(m) >= 0))
+            modVal = this.mod(m);
+
+        if (modVal.equals(ONE))
+            return ONE;
+
+        MutableBigInteger a = new MutableBigInteger(modVal);
+        MutableBigInteger b = new MutableBigInteger(m);
+
+        MutableBigInteger result = a.mutableModInverse(b);
+        return result.toBigInteger(1);
+    }
+
+    // Shift Operations
+
+    /**
+     * Returns a BigInteger whose value is {@code (this << n)}.
+     * The shift distance, {@code n}, may be negative, in which case
+     * this method performs a right shift.
+     * (Computes <tt>floor(this * 2<sup>n</sup>)</tt>.)
+     *
+     * @param  n shift distance, in bits.
+     * @return {@code this << n}
+     * @see #shiftRight
+     */
+    @NonNull public BigInteger shiftLeft(int n) {
+        if (signum == 0)
+            return ZERO;
+        if (n > 0) {
+            return new BigInteger(shiftLeft(mag, n), signum);
+        } else if (n == 0) {
+            return this;
+        } else {
+            // Possible int overflow in (-n) is not a trouble,
+            // because shiftRightImpl considers its argument unsigned
+            return shiftRightImpl(-n);
+        }
+    }
+
+    /**
+     * Returns a magnitude array whose value is {@code (mag << n)}.
+     * The shift distance, {@code n}, is considered unnsigned.
+     * (Computes <tt>this * 2<sup>n</sup></tt>.)
+     *
+     * @param mag magnitude, the most-significant int ({@code mag[0]}) must be non-zero.
+     * @param  n unsigned shift distance, in bits.
+     * @return {@code mag << n}
+     */
+    private static int[] shiftLeft(int[] mag, int n) {
+        int nInts = n >>> 5;
+        int nBits = n & 0x1f;
+        int magLen = mag.length;
+        int newMag[] = null;
+
+        if (nBits == 0) {
+            newMag = new int[magLen + nInts];
+            System.arraycopy(mag, 0, newMag, 0, magLen);
+        } else {
+            int i = 0;
+            int nBits2 = 32 - nBits;
+            int highBits = mag[0] >>> nBits2;
+            if (highBits != 0) {
+                newMag = new int[magLen + nInts + 1];
+                newMag[i++] = highBits;
+            } else {
+                newMag = new int[magLen + nInts];
+            }
+            int j=0;
+            while (j < magLen-1)
+                newMag[i++] = mag[j++] << nBits | mag[j] >>> nBits2;
+            newMag[i] = mag[j] << nBits;
+        }
+        return newMag;
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this >> n)}.  Sign
+     * extension is performed.  The shift distance, {@code n}, may be
+     * negative, in which case this method performs a left shift.
+     * (Computes <tt>floor(this / 2<sup>n</sup>)</tt>.)
+     *
+     * @param  n shift distance, in bits.
+     * @return {@code this >> n}
+     * @see #shiftLeft
+     */
+    @NonNull public BigInteger shiftRight(int n) {
+        if (signum == 0)
+            return ZERO;
+        if (n > 0) {
+            return shiftRightImpl(n);
+        } else if (n == 0) {
+            return this;
+        } else {
+            // Possible int overflow in {@code -n} is not a trouble,
+            // because shiftLeft considers its argument unsigned
+            return new BigInteger(shiftLeft(mag, -n), signum);
+        }
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this >> n)}. The shift
+     * distance, {@code n}, is considered unsigned.
+     * (Computes <tt>floor(this * 2<sup>-n</sup>)</tt>.)
+     *
+     * @param  n unsigned shift distance, in bits.
+     * @return {@code this >> n}
+     */
+    @NonNull private BigInteger shiftRightImpl(int n) {
+        int nInts = n >>> 5;
+        int nBits = n & 0x1f;
+        int magLen = mag.length;
+        int newMag[] = null;
+
+        // Special case: entire contents shifted off the end
+        if (nInts >= magLen)
+            return (signum >= 0 ? ZERO : negConst[1]);
+
+        if (nBits == 0) {
+            int newMagLen = magLen - nInts;
+            newMag = Arrays.copyOf(mag, newMagLen);
+        } else {
+            int i = 0;
+            int highBits = mag[0] >>> nBits;
+            if (highBits != 0) {
+                newMag = new int[magLen - nInts];
+                newMag[i++] = highBits;
+            } else {
+                newMag = new int[magLen - nInts -1];
+            }
+
+            int nBits2 = 32 - nBits;
+            int j=0;
+            while (j < magLen - nInts - 1)
+                newMag[i++] = (mag[j++] << nBits2) | (mag[j] >>> nBits);
+        }
+
+        if (signum < 0) {
+            // Find out whether any one-bits were shifted off the end.
+            boolean onesLost = false;
+            for (int i=magLen-1, j=magLen-nInts; i >= j && !onesLost; i--)
+                onesLost = (mag[i] != 0);
+            if (!onesLost && nBits != 0)
+                onesLost = (mag[magLen - nInts - 1] << (32 - nBits) != 0);
+
+            if (onesLost)
+                newMag = javaIncrement(newMag);
+        }
+
+        return new BigInteger(newMag, signum);
+    }
+
+    int[] javaIncrement(int[] val) {
+        int lastSum = 0;
+        for (int i=val.length-1;  i >= 0 && lastSum == 0; i--)
+            lastSum = (val[i] += 1);
+        if (lastSum == 0) {
+            val = new int[val.length+1];
+            val[0] = 1;
+        }
+        return val;
+    }
+
+    // Bitwise Operations
+
+    /**
+     * Returns a BigInteger whose value is {@code (this & val)}.  (This
+     * method returns a negative BigInteger if and only if this and val are
+     * both negative.)
+     *
+     * @param val value to be AND'ed with this BigInteger.
+     * @return {@code this & val}
+     */
+    @NonNull public BigInteger and(@NonNull BigInteger val) {
+        int[] result = new int[Math.max(intLength(), val.intLength())];
+        for (int i=0; i < result.length; i++)
+            result[i] = (getInt(result.length-i-1)
+                         & val.getInt(result.length-i-1));
+
+        return valueOf(result);
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this | val)}.  (This method
+     * returns a negative BigInteger if and only if either this or val is
+     * negative.)
+     *
+     * @param val value to be OR'ed with this BigInteger.
+     * @return {@code this | val}
+     */
+    @NonNull public BigInteger or(@NonNull BigInteger val) {
+        int[] result = new int[Math.max(intLength(), val.intLength())];
+        for (int i=0; i < result.length; i++)
+            result[i] = (getInt(result.length-i-1)
+                         | val.getInt(result.length-i-1));
+
+        return valueOf(result);
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this ^ val)}.  (This method
+     * returns a negative BigInteger if and only if exactly one of this and
+     * val are negative.)
+     *
+     * @param val value to be XOR'ed with this BigInteger.
+     * @return {@code this ^ val}
+     */
+    @NonNull public BigInteger xor(@NonNull BigInteger val) {
+        int[] result = new int[Math.max(intLength(), val.intLength())];
+        for (int i=0; i < result.length; i++)
+            result[i] = (getInt(result.length-i-1)
+                         ^ val.getInt(result.length-i-1));
+
+        return valueOf(result);
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (~this)}.  (This method
+     * returns a negative value if and only if this BigInteger is
+     * non-negative.)
+     *
+     * @return {@code ~this}
+     */
+    @NonNull public BigInteger not() {
+        int[] result = new int[intLength()];
+        for (int i=0; i < result.length; i++)
+            result[i] = ~getInt(result.length-i-1);
+
+        return valueOf(result);
+    }
+
+    /**
+     * Returns a BigInteger whose value is {@code (this & ~val)}.  This
+     * method, which is equivalent to {@code and(val.not())}, is provided as
+     * a convenience for masking operations.  (This method returns a negative
+     * BigInteger if and only if {@code this} is negative and {@code val} is
+     * positive.)
+     *
+     * @param val value to be complemented and AND'ed with this BigInteger.
+     * @return {@code this & ~val}
+     */
+    @NonNull public BigInteger andNot(@NonNull BigInteger val) {
+        int[] result = new int[Math.max(intLength(), val.intLength())];
+        for (int i=0; i < result.length; i++)
+            result[i] = (getInt(result.length-i-1)
+                         & ~val.getInt(result.length-i-1));
+
+        return valueOf(result);
+    }
+
+
+    // Single Bit Operations
+
+    /**
+     * Returns {@code true} if and only if the designated bit is set.
+     * (Computes {@code ((this & (1<<n)) != 0)}.)
+     *
+     * @param  n index of bit to test.
+     * @return {@code true} if and only if the designated bit is set.
+     * @throws ArithmeticException {@code n} is negative.
+     */
+    public boolean testBit(int n) {
+        if (n < 0)
+            throw new ArithmeticException("Negative bit address");
+
+        return (getInt(n >>> 5) & (1 << (n & 31))) != 0;
+    }
+
+    /**
+     * Returns a BigInteger whose value is equivalent to this BigInteger
+     * with the designated bit set.  (Computes {@code (this | (1<<n))}.)
+     *
+     * @param  n index of bit to set.
+     * @return {@code this | (1<<n)}
+     * @throws ArithmeticException {@code n} is negative.
+     */
+    @NonNull public BigInteger setBit(int n) {
+        if (n < 0)
+            throw new ArithmeticException("Negative bit address");
+
+        int intNum = n >>> 5;
+        int[] result = new int[Math.max(intLength(), intNum+2)];
+
+        for (int i=0; i < result.length; i++)
+            result[result.length-i-1] = getInt(i);
+
+        result[result.length-intNum-1] |= (1 << (n & 31));
+
+        return valueOf(result);
+    }
+
+    /**
+     * Returns a BigInteger whose value is equivalent to this BigInteger
+     * with the designated bit cleared.
+     * (Computes {@code (this & ~(1<<n))}.)
+     *
+     * @param  n index of bit to clear.
+     * @return {@code this & ~(1<<n)}
+     * @throws ArithmeticException {@code n} is negative.
+     */
+    @NonNull public BigInteger clearBit(int n) {
+        if (n < 0)
+            throw new ArithmeticException("Negative bit address");
+
+        int intNum = n >>> 5;
+        int[] result = new int[Math.max(intLength(), ((n + 1) >>> 5) + 1)];
+
+        for (int i=0; i < result.length; i++)
+            result[result.length-i-1] = getInt(i);
+
+        result[result.length-intNum-1] &= ~(1 << (n & 31));
+
+        return valueOf(result);
+    }
+
+    /**
+     * Returns a BigInteger whose value is equivalent to this BigInteger
+     * with the designated bit flipped.
+     * (Computes {@code (this ^ (1<<n))}.)
+     *
+     * @param  n index of bit to flip.
+     * @return {@code this ^ (1<<n)}
+     * @throws ArithmeticException {@code n} is negative.
+     */
+    @NonNull public BigInteger flipBit(int n) {
+        if (n < 0)
+            throw new ArithmeticException("Negative bit address");
+
+        int intNum = n >>> 5;
+        int[] result = new int[Math.max(intLength(), intNum+2)];
+
+        for (int i=0; i < result.length; i++)
+            result[result.length-i-1] = getInt(i);
+
+        result[result.length-intNum-1] ^= (1 << (n & 31));
+
+        return valueOf(result);
+    }
+
+    /**
+     * Returns the index of the rightmost (lowest-order) one bit in this
+     * BigInteger (the number of zero bits to the right of the rightmost
+     * one bit).  Returns -1 if this BigInteger contains no one bits.
+     * (Computes {@code (this == 0? -1 : log2(this & -this))}.)
+     *
+     * @return index of the rightmost one bit in this BigInteger.
+     */
+    public int getLowestSetBit() {
+        @SuppressWarnings("deprecation") int lsb = lowestSetBit - 2;
+        if (lsb == -2) {  // lowestSetBit not initialized yet
+            lsb = 0;
+            if (signum == 0) {
+                lsb -= 1;
+            } else {
+                // Search for lowest order nonzero int
+                int i,b;
+                for (i=0; (b = getInt(i)) == 0; i++)
+                    ;
+                lsb += (i << 5) + Integer.numberOfTrailingZeros(b);
+            }
+            lowestSetBit = lsb + 2;
+        }
+        return lsb;
+    }
+
+
+    // Miscellaneous Bit Operations
+
+    /**
+     * Returns the number of bits in the minimal two's-complement
+     * representation of this BigInteger, <i>excluding</i> a sign bit.
+     * For positive BigIntegers, this is equivalent to the number of bits in
+     * the ordinary binary representation.  (Computes
+     * {@code (ceil(log2(this < 0 ? -this : this+1)))}.)
+     *
+     * @return number of bits in the minimal two's-complement
+     *         representation of this BigInteger, <i>excluding</i> a sign bit.
+     */
+    public int bitLength() {
+        @SuppressWarnings("deprecation") int n = bitLength - 1;
+        if (n == -1) { // bitLength not initialized yet
+            int[] m = mag;
+            int len = m.length;
+            if (len == 0) {
+                n = 0; // offset by one to initialize
+            }  else {
+                // Calculate the bit length of the magnitude
+                int magBitLength = ((len - 1) << 5) + bitLengthForInt(mag[0]);
+                 if (signum < 0) {
+                     // Check if magnitude is a power of two
+                     boolean pow2 = (Integer.bitCount(mag[0]) == 1);
+                     for (int i=1; i< len && pow2; i++)
+                         pow2 = (mag[i] == 0);
+
+                     n = (pow2 ? magBitLength - 1 : magBitLength);
+                 } else {
+                     n = magBitLength;
+                 }
+            }
+            bitLength = n + 1;
+        }
+        return n;
+    }
+
+    /**
+     * Returns the number of bits in the two's complement representation
+     * of this BigInteger that differ from its sign bit.  This method is
+     * useful when implementing bit-vector style sets atop BigIntegers.
+     *
+     * @return number of bits in the two's complement representation
+     *         of this BigInteger that differ from its sign bit.
+     */
+    public int bitCount() {
+        @SuppressWarnings("deprecation") int bc = bitCount - 1;
+        if (bc == -1) {  // bitCount not initialized yet
+            bc = 0;      // offset by one to initialize
+            // Count the bits in the magnitude
+            for (int i=0; i < mag.length; i++)
+                bc += Integer.bitCount(mag[i]);
+            if (signum < 0) {
+                // Count the trailing zeros in the magnitude
+                int magTrailingZeroCount = 0, j;
+                for (j=mag.length-1; mag[j] == 0; j--)
+                    magTrailingZeroCount += 32;
+                magTrailingZeroCount += Integer.numberOfTrailingZeros(mag[j]);
+                bc += magTrailingZeroCount - 1;
+            }
+            bitCount = bc + 1;
+        }
+        return bc;
+    }
+
+    // Primality Testing
+
+    /**
+     * Returns {@code true} if this BigInteger is probably prime,
+     * {@code false} if it's definitely composite.  If
+     * {@code certainty} is &le; 0, {@code true} is
+     * returned.
+     *
+     * @param  certainty a measure of the uncertainty that the caller is
+     *         willing to tolerate: if the call returns {@code true}
+     *         the probability that this BigInteger is prime exceeds
+     *         (1 - 1/2<sup>{@code certainty}</sup>).  The execution time of
+     *         this method is proportional to the value of this parameter.
+     * @return {@code true} if this BigInteger is probably prime,
+     *         {@code false} if it's definitely composite.
+     */
+    public boolean isProbablePrime(int certainty) {
+        if (certainty <= 0)
+            return true;
+        BigInteger w = this.abs();
+        if (w.equals(TWO))
+            return true;
+        if (!w.testBit(0) || w.equals(ONE))
+            return false;
+
+        return w.primeToCertainty(certainty, null);
+    }
+
+    // Comparison Operations
+
+    /**
+     * Compares this BigInteger with the specified BigInteger.  This
+     * method is provided in preference to individual methods for each
+     * of the six boolean comparison operators ({@literal <}, ==,
+     * {@literal >}, {@literal >=}, !=, {@literal <=}).  The suggested
+     * idiom for performing these comparisons is: {@code
+     * (x.compareTo(y)} &lt;<i>op</i>&gt; {@code 0)}, where
+     * &lt;<i>op</i>&gt; is one of the six comparison operators.
+     *
+     * @param  val BigInteger to which this BigInteger is to be compared.
+     * @return -1, 0 or 1 as this BigInteger is numerically less than, equal
+     *         to, or greater than {@code val}.
+     */
+    public int compareTo(@NonNull BigInteger val) {
+        if (signum == val.signum) {
+            switch (signum) {
+            case 1:
+                return compareMagnitude(val);
+            case -1:
+                return val.compareMagnitude(this);
+            default:
+                return 0;
+            }
+        }
+        return signum > val.signum ? 1 : -1;
+    }
+
+    /**
+     * Compares the magnitude array of this BigInteger with the specified
+     * BigInteger's. This is the version of compareTo ignoring sign.
+     *
+     * @param val BigInteger whose magnitude array to be compared.
+     * @return -1, 0 or 1 as this magnitude array is less than, equal to or
+     *         greater than the magnitude aray for the specified BigInteger's.
+     */
+    final int compareMagnitude(@NonNull BigInteger val) {
+        int[] m1 = mag;
+        int len1 = m1.length;
+        int[] m2 = val.mag;
+        int len2 = m2.length;
+        if (len1 < len2)
+            return -1;
+        if (len1 > len2)
+            return 1;
+        for (int i = 0; i < len1; i++) {
+            int a = m1[i];
+            int b = m2[i];
+            if (a != b)
+                return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
+        }
+        return 0;
+    }
+
+    /**
+     * Version of compareMagnitude that compares magnitude with long value.
+     * val can't be Long.MIN_VALUE.
+     */
+    final int compareMagnitude(long val) {
+        assert val != Long.MIN_VALUE;
+        int[] m1 = mag;
+        int len = m1.length;
+        if (len > 2) {
+            return 1;
+        }
+        if (val < 0) {
+            val = -val;
+        }
+        int highWord = (int)(val >>> 32);
+        if (highWord == 0) {
+            if (len < 1)
+                return -1;
+            if (len > 1)
+                return 1;
+            int a = m1[0];
+            int b = (int)val;
+            if (a != b) {
+                return ((a & LONG_MASK) < (b & LONG_MASK))? -1 : 1;
+            }
+            return 0;
+        } else {
+            if (len < 2)
+                return -1;
+            int a = m1[0];
+            int b = highWord;
+            if (a != b) {
+                return ((a & LONG_MASK) < (b & LONG_MASK))? -1 : 1;
+            }
+            a = m1[1];
+            b = (int)val;
+            if (a != b) {
+                return ((a & LONG_MASK) < (b & LONG_MASK))? -1 : 1;
+            }
+            return 0;
+        }
+    }
+
+    /**
+     * Compares this BigInteger with the specified Object for equality.
+     *
+     * @param  x Object to which this BigInteger is to be compared.
+     * @return {@code true} if and only if the specified Object is a
+     *         BigInteger whose value is numerically equal to this BigInteger.
+     */
+    public boolean equals(@NonNull Object x) {
+        // This test is just an optimization, which may or may not help
+        if (x == this)
+            return true;
+
+        if (!(x instanceof BigInteger))
+            return false;
+
+        BigInteger xInt = (BigInteger) x;
+        if (xInt.signum != signum)
+            return false;
+
+        int[] m = mag;
+        int len = m.length;
+        int[] xm = xInt.mag;
+        if (len != xm.length)
+            return false;
+
+        for (int i = 0; i < len; i++)
+            if (xm[i] != m[i])
+                return false;
+
+        return true;
+    }
+
+    /**
+     * Returns the minimum of this BigInteger and {@code val}.
+     *
+     * @param  val value with which the minimum is to be computed.
+     * @return the BigInteger whose value is the lesser of this BigInteger and
+     *         {@code val}.  If they are equal, either may be returned.
+     */
+    @NonNull public BigInteger min(@NonNull BigInteger val) {
+        return (compareTo(val) < 0 ? this : val);
+    }
+
+    /**
+     * Returns the maximum of this BigInteger and {@code val}.
+     *
+     * @param  val value with which the maximum is to be computed.
+     * @return the BigInteger whose value is the greater of this and
+     *         {@code val}.  If they are equal, either may be returned.
+     */
+    @NonNull public BigInteger max(@NonNull BigInteger val) {
+        return (compareTo(val) > 0 ? this : val);
+    }
+
+
+    // Hash Function
+
+    /**
+     * Returns the hash code for this BigInteger.
+     *
+     * @return hash code for this BigInteger.
+     */
+    public int hashCode() {
+        int hashCode = 0;
+
+        for (int i=0; i < mag.length; i++)
+            hashCode = (int)(31*hashCode + (mag[i] & LONG_MASK));
+
+        return hashCode * signum;
+    }
+
+    /**
+     * Returns the String representation of this BigInteger in the
+     * given radix.  If the radix is outside the range from {@link
+     * Character#MIN_RADIX} to {@link Character#MAX_RADIX} inclusive,
+     * it will default to 10 (as is the case for
+     * {@code Integer.toString}).  The digit-to-character mapping
+     * provided by {@code Character.forDigit} is used, and a minus
+     * sign is prepended if appropriate.  (This representation is
+     * compatible with the {@link #BigInteger(String, int) (String,
+     * int)} constructor.)
+     *
+     * @param  radix  radix of the String representation.
+     * @return String representation of this BigInteger in the given radix.
+     * @see    Integer#toString
+     * @see    Character#forDigit
+     * @see    #BigInteger(java.lang.String, int)
+     */
+    @NonNull public String toString(int radix) {
+        if (signum == 0)
+            return "0";
+        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
+            radix = 10;
+
+        // If it's small enough, use smallToString.
+        if (mag.length <= SCHOENHAGE_BASE_CONVERSION_THRESHOLD)
+           return smallToString(radix);
+
+        // Otherwise use recursive toString, which requires positive arguments.
+        // The results will be concatenated into this StringBuilder
+        StringBuilder sb = new StringBuilder();
+        if (signum < 0) {
+            toString(this.negate(), sb, radix, 0);
+            sb.insert(0, '-');
+        }
+        else
+            toString(this, sb, radix, 0);
+
+        return sb.toString();
+    }
+
+    /** This method is used to perform toString when arguments are small. */
+    @NonNull private String smallToString(int radix) {
+        if (signum == 0) {
+            return "0";
+        }
+
+        // Compute upper bound on number of digit groups and allocate space
+        int maxNumDigitGroups = (4*mag.length + 6)/7;
+        String digitGroup[] = new String[maxNumDigitGroups];
+
+        // Translate number to string, a digit group at a time
+        BigInteger tmp = this.abs();
+        int numGroups = 0;
+        while (tmp.signum != 0) {
+            BigInteger d = longRadix[radix];
+
+            MutableBigInteger q = new MutableBigInteger(),
+                              a = new MutableBigInteger(tmp.mag),
+                              b = new MutableBigInteger(d.mag);
+            MutableBigInteger r = a.divide(b, q);
+            BigInteger q2 = q.toBigInteger(tmp.signum * d.signum);
+            BigInteger r2 = r.toBigInteger(tmp.signum * d.signum);
+
+            digitGroup[numGroups++] = Long.toString(r2.longValue(), radix);
+            tmp = q2;
+        }
+
+        // Put sign (if any) and first digit group into result buffer
+        StringBuilder buf = new StringBuilder(numGroups*digitsPerLong[radix]+1);
+        if (signum < 0) {
+            buf.append('-');
+        }
+        buf.append(digitGroup[numGroups-1]);
+
+        // Append remaining digit groups padded with leading zeros
+        for (int i=numGroups-2; i >= 0; i--) {
+            // Prepend (any) leading zeros for this digit group
+            int numLeadingZeros = digitsPerLong[radix]-digitGroup[i].length();
+            if (numLeadingZeros != 0) {
+                buf.append(zeros[numLeadingZeros]);
+            }
+            buf.append(digitGroup[i]);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Converts the specified BigInteger to a string and appends to
+     * {@code sb}.  This implements the recursive Schoenhage algorithm
+     * for base conversions.
+     * <p/>
+     * See Knuth, Donald,  _The Art of Computer Programming_, Vol. 2,
+     * Answers to Exercises (4.4) Question 14.
+     *
+     * @param u      The number to convert to a string.
+     * @param sb     The StringBuilder that will be appended to in place.
+     * @param radix  The base to convert to.
+     * @param digits The minimum number of digits to pad to.
+     */
+    private static void toString(@NonNull BigInteger u, StringBuilder sb, int radix,
+                                 int digits) {
+        /* If we're smaller than a certain threshold, use the smallToString
+           method, padding with leading zeroes when necessary. */
+        if (u.mag.length <= SCHOENHAGE_BASE_CONVERSION_THRESHOLD) {
+            String s = u.smallToString(radix);
+
+            // Pad with internal zeros if necessary.
+            // Don't pad if we're at the beginning of the string.
+            if ((s.length() < digits) && (sb.length() > 0)) {
+                for (int i=s.length(); i < digits; i++) { // May be a faster way to
+                    sb.append('0');                    // do this?
+                }
+            }
+
+            sb.append(s);
+            return;
+        }
+
+        int b, n;
+        b = u.bitLength();
+
+        // Calculate a value for n in the equation radix^(2^n) = u
+        // and subtract 1 from that value.  This is used to find the
+        // cache index that contains the best value to divide u.
+        n = (int) Math.round(Math.log(b * LOG_TWO / logCache[radix]) / LOG_TWO - 1.0);
+        BigInteger v = getRadixConversionCache(radix, n);
+        BigInteger[] results;
+        results = u.divideAndRemainder(v);
+
+        int expectedDigits = 1 << n;
+
+        // Now recursively build the two halves of each number.
+        toString(results[0], sb, radix, digits-expectedDigits);
+        toString(results[1], sb, radix, expectedDigits);
+    }
+
+    /**
+     * Returns the value radix^(2^exponent) from the cache.
+     * If this value doesn't already exist in the cache, it is added.
+     * <p/>
+     * This could be changed to a more complicated caching method using
+     * {@code Future}.
+     */
+    @NonNull private static BigInteger getRadixConversionCache(int radix, int exponent) {
+        BigInteger[] cacheLine = powerCache[radix]; // volatile read
+        if (exponent < cacheLine.length) {
+            return cacheLine[exponent];
+        }
+
+        int oldLength = cacheLine.length;
+        cacheLine = Arrays.copyOf(cacheLine, exponent + 1);
+        for (int i = oldLength; i <= exponent; i++) {
+            cacheLine[i] = cacheLine[i - 1].pow(2);
+        }
+
+        BigInteger[][] pc = powerCache; // volatile read again
+        if (exponent >= pc[radix].length) {
+            pc = pc.clone();
+            pc[radix] = cacheLine;
+            powerCache = pc; // volatile write, publish
+        }
+        return cacheLine[exponent];
+    }
+
+    /* zero[i] is a string of i consecutive zeros. */
+    private static String zeros[] = new String[64];
+    static {
+        zeros[63] =
+            "000000000000000000000000000000000000000000000000000000000000000";
+        for (int i=0; i < 63; i++)
+            zeros[i] = zeros[63].substring(0, i);
+    }
+
+    /**
+     * Returns the decimal String representation of this BigInteger.
+     * The digit-to-character mapping provided by
+     * {@code Character.forDigit} is used, and a minus sign is
+     * prepended if appropriate.  (This representation is compatible
+     * with the {@link #BigInteger(String) (String)} constructor, and
+     * allows for String concatenation with Java's + operator.)
+     *
+     * @return decimal String representation of this BigInteger.
+     * @see    Character#forDigit
+     * @see    #BigInteger(java.lang.String)
+     */
+    @NonNull public String toString() {
+        return toString(10);
+    }
+
+    /**
+     * Returns a byte array containing the two's-complement
+     * representation of this BigInteger.  The byte array will be in
+     * <i>big-endian</i> byte-order: the most significant byte is in
+     * the zeroth element.  The array will contain the minimum number
+     * of bytes required to represent this BigInteger, including at
+     * least one sign bit, which is {@code (ceil((this.bitLength() +
+     * 1)/8))}.  (This representation is compatible with the
+     * {@link #BigInteger(byte[]) (byte[])} constructor.)
+     *
+     * @return a byte array containing the two's-complement representation of
+     *         this BigInteger.
+     * @see    #BigInteger(byte[])
+     */
+    public byte[] toByteArray() {
+        int byteLen = bitLength()/8 + 1;
+        byte[] byteArray = new byte[byteLen];
+
+        for (int i=byteLen-1, bytesCopied=4, nextInt=0, intIndex=0; i >= 0; i--) {
+            if (bytesCopied == 4) {
+                nextInt = getInt(intIndex++);
+                bytesCopied = 1;
+            } else {
+                nextInt >>>= 8;
+                bytesCopied++;
+            }
+            byteArray[i] = (byte)nextInt;
+        }
+        return byteArray;
+    }
+
+    /**
+     * Converts this BigInteger to an {@code int}.  This
+     * conversion is analogous to a
+     * <i>narrowing primitive conversion</i> from {@code long} to
+     * {@code int} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * if this BigInteger is too big to fit in an
+     * {@code int}, only the low-order 32 bits are returned.
+     * Note that this conversion can lose information about the
+     * overall magnitude of the BigInteger value as well as return a
+     * result with the opposite sign.
+     *
+     * @return this BigInteger converted to an {@code int}.
+     * @see #intValueExact()
+     */
+    public int intValue() {
+        int result = 0;
+        result = getInt(0);
+        return result;
+    }
+
+    /**
+     * Converts this BigInteger to a {@code long}.  This
+     * conversion is analogous to a
+     * <i>narrowing primitive conversion</i> from {@code long} to
+     * {@code int} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * if this BigInteger is too big to fit in a
+     * {@code long}, only the low-order 64 bits are returned.
+     * Note that this conversion can lose information about the
+     * overall magnitude of the BigInteger value as well as return a
+     * result with the opposite sign.
+     *
+     * @return this BigInteger converted to a {@code long}.
+     * @see #longValueExact()
+     */
+    public long longValue() {
+        long result = 0;
+
+        for (int i=1; i >= 0; i--)
+            result = (result << 32) + (getInt(i) & LONG_MASK);
+        return result;
+    }
+
+    /**
+     * Converts this BigInteger to a {@code float}.  This
+     * conversion is similar to the
+     * <i>narrowing primitive conversion</i> from {@code double} to
+     * {@code float} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * if this BigInteger has too great a magnitude
+     * to represent as a {@code float}, it will be converted to
+     * {@link Float#NEGATIVE_INFINITY} or {@link
+     * Float#POSITIVE_INFINITY} as appropriate.  Note that even when
+     * the return value is finite, this conversion can lose
+     * information about the precision of the BigInteger value.
+     *
+     * @return this BigInteger converted to a {@code float}.
+     */
+    public float floatValue() {
+        if (signum == 0) {
+            return 0.0f;
+        }
+
+        int exponent = ((mag.length - 1) << 5) + bitLengthForInt(mag[0]) - 1;
+
+        // exponent == floor(log2(abs(this)))
+        if (exponent < Long.SIZE - 1) {
+            return longValue();
+        } else if (exponent > Float.MAX_EXPONENT) {
+            return signum > 0 ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
+        }
+
+        /*
+         * We need the top SIGNIFICAND_WIDTH bits, including the "implicit"
+         * one bit. To make rounding easier, we pick out the top
+         * SIGNIFICAND_WIDTH + 1 bits, so we have one to help us round up or
+         * down. twiceSignifFloor will contain the top SIGNIFICAND_WIDTH + 1
+         * bits, and signifFloor the top SIGNIFICAND_WIDTH.
+         *
+         * It helps to consider the real number signif = abs(this) *
+         * 2^(SIGNIFICAND_WIDTH - 1 - exponent).
+         */
+        int shift = exponent - FloatConsts.SIGNIFICAND_WIDTH;
+
+        int twiceSignifFloor;
+        // twiceSignifFloor will be == abs().shiftRight(shift).intValue()
+        // We do the shift into an int directly to improve performance.
+
+        int nBits = shift & 0x1f;
+        int nBits2 = 32 - nBits;
+
+        if (nBits == 0) {
+            twiceSignifFloor = mag[0];
+        } else {
+            twiceSignifFloor = mag[0] >>> nBits;
+            if (twiceSignifFloor == 0) {
+                twiceSignifFloor = (mag[0] << nBits2) | (mag[1] >>> nBits);
+            }
+        }
+
+        int signifFloor = twiceSignifFloor >> 1;
+        signifFloor &= FloatConsts.SIGNIF_BIT_MASK; // remove the implied bit
+
+        /*
+         * We round up if either the fractional part of signif is strictly
+         * greater than 0.5 (which is true if the 0.5 bit is set and any lower
+         * bit is set), or if the fractional part of signif is >= 0.5 and
+         * signifFloor is odd (which is true if both the 0.5 bit and the 1 bit
+         * are set). This is equivalent to the desired HALF_EVEN rounding.
+         */
+        boolean increment = (twiceSignifFloor & 1) != 0
+                && ((signifFloor & 1) != 0 || abs().getLowestSetBit() < shift);
+        int signifRounded = increment ? signifFloor + 1 : signifFloor;
+        int bits = ((exponent + FloatConsts.EXP_BIAS))
+                << (FloatConsts.SIGNIFICAND_WIDTH - 1);
+        bits += signifRounded;
+        /*
+         * If signifRounded == 2^24, we'd need to set all of the significand
+         * bits to zero and add 1 to the exponent. This is exactly the behavior
+         * we get from just adding signifRounded to bits directly. If the
+         * exponent is Float.MAX_EXPONENT, we round up (correctly) to
+         * Float.POSITIVE_INFINITY.
+         */
+        bits |= signum & FloatConsts.SIGN_BIT_MASK;
+        return Float.intBitsToFloat(bits);
+    }
+
+    /**
+     * Converts this BigInteger to a {@code double}.  This
+     * conversion is similar to the
+     * <i>narrowing primitive conversion</i> from {@code double} to
+     * {@code float} as defined in section 5.1.3 of
+     * <cite>The Java&trade; Language Specification</cite>:
+     * if this BigInteger has too great a magnitude
+     * to represent as a {@code double}, it will be converted to
+     * {@link Double#NEGATIVE_INFINITY} or {@link
+     * Double#POSITIVE_INFINITY} as appropriate.  Note that even when
+     * the return value is finite, this conversion can lose
+     * information about the precision of the BigInteger value.
+     *
+     * @return this BigInteger converted to a {@code double}.
+     */
+    public double doubleValue() {
+        if (signum == 0) {
+            return 0.0;
+        }
+
+        int exponent = ((mag.length - 1) << 5) + bitLengthForInt(mag[0]) - 1;
+
+        // exponent == floor(log2(abs(this))Double)
+        if (exponent < Long.SIZE - 1) {
+            return longValue();
+        } else if (exponent > Double.MAX_EXPONENT) {
+            return signum > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+        }
+
+        /*
+         * We need the top SIGNIFICAND_WIDTH bits, including the "implicit"
+         * one bit. To make rounding easier, we pick out the top
+         * SIGNIFICAND_WIDTH + 1 bits, so we have one to help us round up or
+         * down. twiceSignifFloor will contain the top SIGNIFICAND_WIDTH + 1
+         * bits, and signifFloor the top SIGNIFICAND_WIDTH.
+         *
+         * It helps to consider the real number signif = abs(this) *
+         * 2^(SIGNIFICAND_WIDTH - 1 - exponent).
+         */
+        int shift = exponent - DoubleConsts.SIGNIFICAND_WIDTH;
+
+        long twiceSignifFloor;
+        // twiceSignifFloor will be == abs().shiftRight(shift).longValue()
+        // We do the shift into a long directly to improve performance.
+
+        int nBits = shift & 0x1f;
+        int nBits2 = 32 - nBits;
+
+        int highBits;
+        int lowBits;
+        if (nBits == 0) {
+            highBits = mag[0];
+            lowBits = mag[1];
+        } else {
+            highBits = mag[0] >>> nBits;
+            lowBits = (mag[0] << nBits2) | (mag[1] >>> nBits);
+            if (highBits == 0) {
+                highBits = lowBits;
+                lowBits = (mag[1] << nBits2) | (mag[2] >>> nBits);
+            }
+        }
+
+        twiceSignifFloor = ((highBits & LONG_MASK) << 32)
+                | (lowBits & LONG_MASK);
+
+        long signifFloor = twiceSignifFloor >> 1;
+        signifFloor &= DoubleConsts.SIGNIF_BIT_MASK; // remove the implied bit
+
+        /*
+         * We round up if either the fractional part of signif is strictly
+         * greater than 0.5 (which is true if the 0.5 bit is set and any lower
+         * bit is set), or if the fractional part of signif is >= 0.5 and
+         * signifFloor is odd (which is true if both the 0.5 bit and the 1 bit
+         * are set). This is equivalent to the desired HALF_EVEN rounding.
+         */
+        boolean increment = (twiceSignifFloor & 1) != 0
+                && ((signifFloor & 1) != 0 || abs().getLowestSetBit() < shift);
+        long signifRounded = increment ? signifFloor + 1 : signifFloor;
+        long bits = (long) ((exponent + DoubleConsts.EXP_BIAS))
+                << (DoubleConsts.SIGNIFICAND_WIDTH - 1);
+        bits += signifRounded;
+        /*
+         * If signifRounded == 2^53, we'd need to set all of the significand
+         * bits to zero and add 1 to the exponent. This is exactly the behavior
+         * we get from just adding signifRounded to bits directly. If the
+         * exponent is Double.MAX_EXPONENT, we round up (correctly) to
+         * Double.POSITIVE_INFINITY.
+         */
+        bits |= signum & DoubleConsts.SIGN_BIT_MASK;
+        return Double.longBitsToDouble(bits);
+    }
+
+    /**
+     * Returns a copy of the input array stripped of any leading zero bytes.
+     */
+    private static int[] stripLeadingZeroInts(int val[]) {
+        int vlen = val.length;
+        int keep;
+
+        // Find first nonzero byte
+        for (keep = 0; keep < vlen && val[keep] == 0; keep++)
+            ;
+        return java.util.Arrays.copyOfRange(val, keep, vlen);
+    }
+
+    /**
+     * Returns the input array stripped of any leading zero bytes.
+     * Since the source is trusted the copying may be skipped.
+     */
+    private static int[] trustedStripLeadingZeroInts(int val[]) {
+        int vlen = val.length;
+        int keep;
+
+        // Find first nonzero byte
+        for (keep = 0; keep < vlen && val[keep] == 0; keep++)
+            ;
+        return keep == 0 ? val : java.util.Arrays.copyOfRange(val, keep, vlen);
+    }
+
+    /**
+     * Returns a copy of the input array stripped of any leading zero bytes.
+     */
+    private static int[] stripLeadingZeroBytes(byte a[]) {
+        int byteLength = a.length;
+        int keep;
+
+        // Find first nonzero byte
+        for (keep = 0; keep < byteLength && a[keep] == 0; keep++)
+            ;
+
+        // Allocate new array and copy relevant part of input array
+        int intLength = ((byteLength - keep) + 3) >>> 2;
+        int[] result = new int[intLength];
+        int b = byteLength - 1;
+        for (int i = intLength-1; i >= 0; i--) {
+            result[i] = a[b--] & 0xff;
+            int bytesRemaining = b - keep + 1;
+            int bytesToTransfer = Math.min(3, bytesRemaining);
+            for (int j=8; j <= (bytesToTransfer << 3); j += 8)
+                result[i] |= ((a[b--] & 0xff) << j);
+        }
+        return result;
+    }
+
+    /**
+     * Takes an array a representing a negative 2's-complement number and
+     * returns the minimal (no leading zero bytes) unsigned whose value is -a.
+     */
+    private static int[] makePositive(byte a[]) {
+        int keep, k;
+        int byteLength = a.length;
+
+        // Find first non-sign (0xff) byte of input
+        for (keep=0; keep < byteLength && a[keep] == -1; keep++)
+            ;
+
+
+        /* Allocate output array.  If all non-sign bytes are 0x00, we must
+         * allocate space for one extra output byte. */
+        for (k=keep; k < byteLength && a[k] == 0; k++)
+            ;
+
+        int extraByte = (k == byteLength) ? 1 : 0;
+        int intLength = ((byteLength - keep + extraByte) + 3) >>> 2;
+        int result[] = new int[intLength];
+
+        /* Copy one's complement of input into output, leaving extra
+         * byte (if it exists) == 0x00 */
+        int b = byteLength - 1;
+        for (int i = intLength-1; i >= 0; i--) {
+            result[i] = a[b--] & 0xff;
+            int numBytesToTransfer = Math.min(3, b-keep+1);
+            if (numBytesToTransfer < 0)
+                numBytesToTransfer = 0;
+            for (int j=8; j <= 8*numBytesToTransfer; j += 8)
+                result[i] |= ((a[b--] & 0xff) << j);
+
+            // Mask indicates which bits must be complemented
+            int mask = -1 >>> (8*(3-numBytesToTransfer));
+            result[i] = ~result[i] & mask;
+        }
+
+        // Add one to one's complement to generate two's complement
+        for (int i=result.length-1; i >= 0; i--) {
+            result[i] = (int)((result[i] & LONG_MASK) + 1);
+            if (result[i] != 0)
+                break;
+        }
+
+        return result;
+    }
+
+    /**
+     * Takes an array a representing a negative 2's-complement number and
+     * returns the minimal (no leading zero ints) unsigned whose value is -a.
+     */
+    private static int[] makePositive(int a[]) {
+        int keep, j;
+
+        // Find first non-sign (0xffffffff) int of input
+        for (keep=0; keep < a.length && a[keep] == -1; keep++)
+            ;
+
+        /* Allocate output array.  If all non-sign ints are 0x00, we must
+         * allocate space for one extra output int. */
+        for (j=keep; j < a.length && a[j] == 0; j++)
+            ;
+        int extraInt = (j == a.length ? 1 : 0);
+        int result[] = new int[a.length - keep + extraInt];
+
+        /* Copy one's complement of input into output, leaving extra
+         * int (if it exists) == 0x00 */
+        for (int i = keep; i < a.length; i++)
+            result[i - keep + extraInt] = ~a[i];
+
+        // Add one to one's complement to generate two's complement
+        for (int i=result.length-1; ++result[i] == 0; i--)
+            ;
+
+        return result;
+    }
+
+    /*
+     * The following two arrays are used for fast String conversions.  Both
+     * are indexed by radix.  The first is the number of digits of the given
+     * radix that can fit in a Java long without "going negative", i.e., the
+     * highest integer n such that radix**n < 2**63.  The second is the
+     * "long radix" that tears each number into "long digits", each of which
+     * consists of the number of digits in the corresponding element in
+     * digitsPerLong (longRadix[i] = i**digitPerLong[i]).  Both arrays have
+     * nonsense values in their 0 and 1 elements, as radixes 0 and 1 are not
+     * used.
+     */
+    private static int digitsPerLong[] = {0, 0,
+        62, 39, 31, 27, 24, 22, 20, 19, 18, 18, 17, 17, 16, 16, 15, 15, 15, 14,
+        14, 14, 14, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12};
+
+    private static BigInteger longRadix[] = {null, null,
+        valueOf(0x4000000000000000L), valueOf(0x383d9170b85ff80bL),
+        valueOf(0x4000000000000000L), valueOf(0x6765c793fa10079dL),
+        valueOf(0x41c21cb8e1000000L), valueOf(0x3642798750226111L),
+        valueOf(0x1000000000000000L), valueOf(0x12bf307ae81ffd59L),
+        valueOf( 0xde0b6b3a7640000L), valueOf(0x4d28cb56c33fa539L),
+        valueOf(0x1eca170c00000000L), valueOf(0x780c7372621bd74dL),
+        valueOf(0x1e39a5057d810000L), valueOf(0x5b27ac993df97701L),
+        valueOf(0x1000000000000000L), valueOf(0x27b95e997e21d9f1L),
+        valueOf(0x5da0e1e53c5c8000L), valueOf( 0xb16a458ef403f19L),
+        valueOf(0x16bcc41e90000000L), valueOf(0x2d04b7fdd9c0ef49L),
+        valueOf(0x5658597bcaa24000L), valueOf( 0x6feb266931a75b7L),
+        valueOf( 0xc29e98000000000L), valueOf(0x14adf4b7320334b9L),
+        valueOf(0x226ed36478bfa000L), valueOf(0x383d9170b85ff80bL),
+        valueOf(0x5a3c23e39c000000L), valueOf( 0x4e900abb53e6b71L),
+        valueOf( 0x7600ec618141000L), valueOf( 0xaee5720ee830681L),
+        valueOf(0x1000000000000000L), valueOf(0x172588ad4f5f0981L),
+        valueOf(0x211e44f7d02c1000L), valueOf(0x2ee56725f06e5c71L),
+        valueOf(0x41c21cb8e1000000L)};
+
+    /*
+     * These two arrays are the integer analogue of above.
+     */
+    private static int digitsPerInt[] = {0, 0, 30, 19, 15, 13, 11,
+        11, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6,
+        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5};
+
+    private static int intRadix[] = {0, 0,
+        0x40000000, 0x4546b3db, 0x40000000, 0x48c27395, 0x159fd800,
+        0x75db9c97, 0x40000000, 0x17179149, 0x3b9aca00, 0xcc6db61,
+        0x19a10000, 0x309f1021, 0x57f6c100, 0xa2f1b6f,  0x10000000,
+        0x18754571, 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
+        0x6c20a40,  0x8d2d931,  0xb640000,  0xe8d4a51,  0x1269ae40,
+        0x17179149, 0x1cb91000, 0x23744899, 0x2b73a840, 0x34e63b41,
+        0x40000000, 0x4cfa3cc1, 0x5c13d840, 0x6d91b519, 0x39aa400
+    };
+
+    /**
+     * These routines provide access to the two's complement representation
+     * of BigIntegers.
+     */
+
+    /**
+     * Returns the length of the two's complement representation in ints,
+     * including space for at least one sign bit.
+     */
+    private int intLength() {
+        return (bitLength() >>> 5) + 1;
+    }
+
+    /* Returns sign bit */
+    private int signBit() {
+        return signum < 0 ? 1 : 0;
+    }
+
+    /* Returns an int of sign bits */
+    private int signInt() {
+        return signum < 0 ? -1 : 0;
+    }
+
+    /**
+     * Returns the specified int of the little-endian two's complement
+     * representation (int 0 is the least significant).  The int number can
+     * be arbitrarily high (values are logically preceded by infinitely many
+     * sign ints).
+     */
+    private int getInt(int n) {
+        if (n < 0)
+            return 0;
+        if (n >= mag.length)
+            return signInt();
+
+        int magInt = mag[mag.length-n-1];
+
+        return (signum >= 0 ? magInt :
+                (n <= firstNonzeroIntNum() ? -magInt : ~magInt));
+    }
+
+    /**
+     * Returns the index of the int that contains the first nonzero int in the
+     * little-endian binary representation of the magnitude (int 0 is the
+     * least significant). If the magnitude is zero, return value is undefined.
+     */
+    private int firstNonzeroIntNum() {
+        int fn = firstNonzeroIntNum - 2;
+        if (fn == -2) { // firstNonzeroIntNum not initialized yet
+            fn = 0;
+
+            // Search for the first nonzero int
+            int i;
+            int mlen = mag.length;
+            for (i = mlen - 1; i >= 0 && mag[i] == 0; i--)
+                ;
+            fn = mlen - i - 1;
+            firstNonzeroIntNum = fn + 2; // offset by two to initialize
+        }
+        return fn;
+    }
+
+    /** use serialVersionUID from JDK 1.1. for interoperability */
+    private static final long serialVersionUID = -8287574255936472291L;
+
+    /**
+     * Serializable fields for BigInteger.
+     *
+     * @serialField signum  int
+     *              signum of this BigInteger.
+     * @serialField magnitude int[]
+     *              magnitude array of this BigInteger.
+     * @serialField bitCount  int
+     *              number of bits in this BigInteger
+     * @serialField bitLength int
+     *              the number of bits in the minimal two's-complement
+     *              representation of this BigInteger
+     * @serialField lowestSetBit int
+     *              lowest set bit in the twos complement representation
+     */
+    private static final ObjectStreamField[] serialPersistentFields = {
+        new ObjectStreamField("signum", Integer.TYPE),
+        new ObjectStreamField("magnitude", byte[].class),
+        new ObjectStreamField("bitCount", Integer.TYPE),
+        new ObjectStreamField("bitLength", Integer.TYPE),
+        new ObjectStreamField("firstNonzeroByteNum", Integer.TYPE),
+        new ObjectStreamField("lowestSetBit", Integer.TYPE)
+        };
+
+    /**
+     * Reconstitute the {@code BigInteger} instance from a stream (that is,
+     * deserialize it). The magnitude is read in as an array of bytes
+     * for historical reasons, but it is converted to an array of ints
+     * and the byte array is discarded.
+     * Note:
+     * The current convention is to initialize the cache fields, bitCount,
+     * bitLength and lowestSetBit, to 0 rather than some other marker value.
+     * Therefore, no explicit action to set these fields needs to be taken in
+     * readObject because those fields already have a 0 value be default since
+     * defaultReadObject is not being used.
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+        /*
+         * In order to maintain compatibility with previous serialized forms,
+         * the magnitude of a BigInteger is serialized as an array of bytes.
+         * The magnitude field is used as a temporary store for the byte array
+         * that is deserialized. The cached computation fields should be
+         * transient but are serialized for compatibility reasons.
+         */
+
+        // prepare to read the alternate persistent fields
+        ObjectInputStream.GetField fields = s.readFields();
+
+        // Read the alternate persistent fields that we care about
+        int sign = fields.get("signum", -2);
+        byte[] magnitude = (byte[])fields.get("magnitude", null);
+
+        // Validate signum
+        if (sign < -1 || sign > 1) {
+            String message = "BigInteger: Invalid signum value";
+            if (fields.defaulted("signum"))
+                message = "BigInteger: Signum not present in stream";
+            throw new java.io.StreamCorruptedException(message);
+        }
+        int[] mag = stripLeadingZeroBytes(magnitude);
+        if ((mag.length == 0) != (sign == 0)) {
+            String message = "BigInteger: signum-magnitude mismatch";
+            if (fields.defaulted("magnitude"))
+                message = "BigInteger: Magnitude not present in stream";
+            throw new java.io.StreamCorruptedException(message);
+        }
+
+        // Commit final fields via Unsafe
+        UnsafeHolder.putSign(this, sign);
+
+        // Calculate mag field from magnitude and discard magnitude
+        UnsafeHolder.putMag(this, mag);
+        if (mag.length >= MAX_MAG_LENGTH) {
+            try {
+                checkRange();
+            } catch (ArithmeticException e) {
+                throw new java.io.StreamCorruptedException("BigInteger: Out of the supported range");
+            }
+        }
+    }
+
+    // Support for resetting final fields while deserializing
+    private static class UnsafeHolder {
+        private static final sun.misc.Unsafe unsafe;
+        private static final long signumOffset;
+        private static final long magOffset;
+        static {
+            try {
+                unsafe = sun.misc.Unsafe.getUnsafe();
+                signumOffset = unsafe.objectFieldOffset
+                    (BigInteger.class.getDeclaredField("signum"));
+                magOffset = unsafe.objectFieldOffset
+                    (BigInteger.class.getDeclaredField("mag"));
+            } catch (Exception ex) {
+                throw new ExceptionInInitializerError(ex);
+            }
+        }
+
+        static void putSign(BigInteger bi, int sign) {
+            unsafe.putIntVolatile(bi, signumOffset, sign);
+        }
+
+        static void putMag(BigInteger bi, int[] magnitude) {
+            unsafe.putObjectVolatile(bi, magOffset, magnitude);
+        }
+    }
+
+    /**
+     * Save the {@code BigInteger} instance to a stream.
+     * The magnitude of a BigInteger is serialized as a byte array for
+     * historical reasons.
+     *
+     * @serialData two necessary fields are written as well as obsolete
+     *             fields for compatibility with older versions.
+     */
+    private void writeObject(ObjectOutputStream s) throws IOException {
+        // set the values of the Serializable fields
+        ObjectOutputStream.PutField fields = s.putFields();
+        fields.put("signum", signum);
+        fields.put("magnitude", magSerializedForm());
+        // The values written for cached fields are compatible with older
+        // versions, but are ignored in readObject so don't otherwise matter.
+        // BEGIN Android-changed: Don't include the following fields.
+        // fields.put("bitCount", -1);
+        // fields.put("bitLength", -1);
+        // fields.put("lowestSetBit", -2);
+        // fields.put("firstNonzeroByteNum", -2);
+        // END Android-changed
+
+        // save them
+        s.writeFields();
+}
+
+    /**
+     * Returns the mag array as an array of bytes.
+     */
+    private byte[] magSerializedForm() {
+        int len = mag.length;
+
+        int bitLen = (len == 0 ? 0 : ((len - 1) << 5) + bitLengthForInt(mag[0]));
+        int byteLen = (bitLen + 7) >>> 3;
+        byte[] result = new byte[byteLen];
+
+        for (int i = byteLen - 1, bytesCopied = 4, intIndex = len - 1, nextInt = 0;
+             i >= 0; i--) {
+            if (bytesCopied == 4) {
+                nextInt = mag[intIndex--];
+                bytesCopied = 1;
+            } else {
+                nextInt >>>= 8;
+                bytesCopied++;
+            }
+            result[i] = (byte)nextInt;
+        }
+        return result;
+    }
+
+    /**
+     * Converts this {@code BigInteger} to a {@code long}, checking
+     * for lost information.  If the value of this {@code BigInteger}
+     * is out of the range of the {@code long} type, then an
+     * {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code BigInteger} converted to a {@code long}.
+     * @throws ArithmeticException if the value of {@code this} will
+     * not exactly fit in a {@code long}.
+     * @see BigInteger#longValue
+     * @since  1.8
+     */
+    public long longValueExact() {
+        if (mag.length <= 2 && bitLength() <= 63)
+            return longValue();
+        else
+            throw new ArithmeticException("BigInteger out of long range");
+    }
+
+    /**
+     * Converts this {@code BigInteger} to an {@code int}, checking
+     * for lost information.  If the value of this {@code BigInteger}
+     * is out of the range of the {@code int} type, then an
+     * {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code BigInteger} converted to an {@code int}.
+     * @throws ArithmeticException if the value of {@code this} will
+     * not exactly fit in a {@code int}.
+     * @see BigInteger#intValue
+     * @since  1.8
+     */
+    public int intValueExact() {
+        if (mag.length <= 1 && bitLength() <= 31)
+            return intValue();
+        else
+            throw new ArithmeticException("BigInteger out of int range");
+    }
+
+    /**
+     * Converts this {@code BigInteger} to a {@code short}, checking
+     * for lost information.  If the value of this {@code BigInteger}
+     * is out of the range of the {@code short} type, then an
+     * {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code BigInteger} converted to a {@code short}.
+     * @throws ArithmeticException if the value of {@code this} will
+     * not exactly fit in a {@code short}.
+     * @see BigInteger#shortValue
+     * @since  1.8
+     */
+    public short shortValueExact() {
+        if (mag.length <= 1 && bitLength() <= 31) {
+            int value = intValue();
+            if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
+                return shortValue();
+        }
+        throw new ArithmeticException("BigInteger out of short range");
+    }
+
+    /**
+     * Converts this {@code BigInteger} to a {@code byte}, checking
+     * for lost information.  If the value of this {@code BigInteger}
+     * is out of the range of the {@code byte} type, then an
+     * {@code ArithmeticException} is thrown.
+     *
+     * @return this {@code BigInteger} converted to a {@code byte}.
+     * @throws ArithmeticException if the value of {@code this} will
+     * not exactly fit in a {@code byte}.
+     * @see BigInteger#byteValue
+     * @since  1.8
+     */
+    public byte byteValueExact() {
+        if (mag.length <= 1 && bitLength() <= 31) {
+            int value = intValue();
+            if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
+                return byteValue();
+        }
+        throw new ArithmeticException("BigInteger out of byte range");
+    }
+}
diff --git a/ojluni/src/main/java/java/math/BitSieve.java b/ojluni/src/main/java/java/math/BitSieve.java
new file mode 100644
index 0000000..8d0d370
--- /dev/null
+++ b/ojluni/src/main/java/java/math/BitSieve.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.math;
+
+/**
+ * A simple bit sieve used for finding prime number candidates. Allows setting
+ * and clearing of bits in a storage array. The size of the sieve is assumed to
+ * be constant to reduce overhead. All the bits of a new bitSieve are zero, and
+ * bits are removed from it by setting them.
+ *
+ * To reduce storage space and increase efficiency, no even numbers are
+ * represented in the sieve (each bit in the sieve represents an odd number).
+ * The relationship between the index of a bit and the number it represents is
+ * given by
+ * N = offset + (2*index + 1);
+ * Where N is the integer represented by a bit in the sieve, offset is some
+ * even integer offset indicating where the sieve begins, and index is the
+ * index of a bit in the sieve array.
+ *
+ * @see     BigInteger
+ * @author  Michael McCloskey
+ * @since   1.3
+ */
+class BitSieve {
+    /**
+     * Stores the bits in this bitSieve.
+     */
+    private long bits[];
+
+    /**
+     * Length is how many bits this sieve holds.
+     */
+    private int length;
+
+    /**
+     * A small sieve used to filter out multiples of small primes in a search
+     * sieve.
+     */
+    private static BitSieve smallSieve = new BitSieve();
+
+    /**
+     * Construct a "small sieve" with a base of 0.  This constructor is
+     * used internally to generate the set of "small primes" whose multiples
+     * are excluded from sieves generated by the main (package private)
+     * constructor, BitSieve(BigInteger base, int searchLen).  The length
+     * of the sieve generated by this constructor was chosen for performance;
+     * it controls a tradeoff between how much time is spent constructing
+     * other sieves, and how much time is wasted testing composite candidates
+     * for primality.  The length was chosen experimentally to yield good
+     * performance.
+     */
+    private BitSieve() {
+        length = 150 * 64;
+        bits = new long[(unitIndex(length - 1) + 1)];
+
+        // Mark 1 as composite
+        set(0);
+        int nextIndex = 1;
+        int nextPrime = 3;
+
+        // Find primes and remove their multiples from sieve
+        do {
+            sieveSingle(length, nextIndex + nextPrime, nextPrime);
+            nextIndex = sieveSearch(length, nextIndex + 1);
+            nextPrime = 2*nextIndex + 1;
+        } while((nextIndex > 0) && (nextPrime < length));
+    }
+
+    /**
+     * Construct a bit sieve of searchLen bits used for finding prime number
+     * candidates. The new sieve begins at the specified base, which must
+     * be even.
+     */
+    BitSieve(BigInteger base, int searchLen) {
+        /*
+         * Candidates are indicated by clear bits in the sieve. As a candidates
+         * nonprimality is calculated, a bit is set in the sieve to eliminate
+         * it. To reduce storage space and increase efficiency, no even numbers
+         * are represented in the sieve (each bit in the sieve represents an
+         * odd number).
+         */
+        bits = new long[(unitIndex(searchLen-1) + 1)];
+        length = searchLen;
+        int start = 0;
+
+        int step = smallSieve.sieveSearch(smallSieve.length, start);
+        int convertedStep = (step *2) + 1;
+
+        // Construct the large sieve at an even offset specified by base
+        MutableBigInteger b = new MutableBigInteger(base);
+        MutableBigInteger q = new MutableBigInteger();
+        do {
+            // Calculate base mod convertedStep
+            start = b.divideOneWord(convertedStep, q);
+
+            // Take each multiple of step out of sieve
+            start = convertedStep - start;
+            if (start%2 == 0)
+                start += convertedStep;
+            sieveSingle(searchLen, (start-1)/2, convertedStep);
+
+            // Find next prime from small sieve
+            step = smallSieve.sieveSearch(smallSieve.length, step+1);
+            convertedStep = (step *2) + 1;
+        } while (step > 0);
+    }
+
+    /**
+     * Given a bit index return unit index containing it.
+     */
+    private static int unitIndex(int bitIndex) {
+        return bitIndex >>> 6;
+    }
+
+    /**
+     * Return a unit that masks the specified bit in its unit.
+     */
+    private static long bit(int bitIndex) {
+        return 1L << (bitIndex & ((1<<6) - 1));
+    }
+
+    /**
+     * Get the value of the bit at the specified index.
+     */
+    private boolean get(int bitIndex) {
+        int unitIndex = unitIndex(bitIndex);
+        return ((bits[unitIndex] & bit(bitIndex)) != 0);
+    }
+
+    /**
+     * Set the bit at the specified index.
+     */
+    private void set(int bitIndex) {
+        int unitIndex = unitIndex(bitIndex);
+        bits[unitIndex] |= bit(bitIndex);
+    }
+
+    /**
+     * This method returns the index of the first clear bit in the search
+     * array that occurs at or after start. It will not search past the
+     * specified limit. It returns -1 if there is no such clear bit.
+     */
+    private int sieveSearch(int limit, int start) {
+        if (start >= limit)
+            return -1;
+
+        int index = start;
+        do {
+            if (!get(index))
+                return index;
+            index++;
+        } while(index < limit-1);
+        return -1;
+    }
+
+    /**
+     * Sieve a single set of multiples out of the sieve. Begin to remove
+     * multiples of the specified step starting at the specified start index,
+     * up to the specified limit.
+     */
+    private void sieveSingle(int limit, int start, int step) {
+        while(start < limit) {
+            set(start);
+            start += step;
+        }
+    }
+
+    /**
+     * Test probable primes in the sieve and return successful candidates.
+     */
+    BigInteger retrieve(BigInteger initValue, int certainty, java.util.Random random) {
+        // Examine the sieve one long at a time to find possible primes
+        int offset = 1;
+        for (int i=0; i<bits.length; i++) {
+            long nextLong = ~bits[i];
+            for (int j=0; j<64; j++) {
+                if ((nextLong & 1) == 1) {
+                    BigInteger candidate = initValue.add(
+                                           BigInteger.valueOf(offset));
+                    if (candidate.primeToCertainty(certainty, random))
+                        return candidate;
+                }
+                nextLong >>>= 1;
+                offset+=2;
+            }
+        }
+        return null;
+    }
+}
diff --git a/ojluni/src/main/java/java/math/MathContext.java b/ojluni/src/main/java/java/math/MathContext.java
new file mode 100644
index 0000000..f9947d3
--- /dev/null
+++ b/ojluni/src/main/java/java/math/MathContext.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Portions Copyright IBM Corporation, 1997, 2001. All Rights Reserved.
+ */
+
+package java.math;
+import java.io.*;
+
+/**
+ * Immutable objects which encapsulate the context settings which
+ * describe certain rules for numerical operators, such as those
+ * implemented by the {@link BigDecimal} class.
+ *
+ * <p>The base-independent settings are:
+ * <ol>
+ * <li>{@code precision}:
+ * the number of digits to be used for an operation; results are
+ * rounded to this precision
+ *
+ * <li>{@code roundingMode}:
+ * a {@link RoundingMode} object which specifies the algorithm to be
+ * used for rounding.
+ * </ol>
+ *
+ * @see     BigDecimal
+ * @see     RoundingMode
+ * @author  Mike Cowlishaw
+ * @author  Joseph D. Darcy
+ * @since 1.5
+ */
+
+public final class MathContext implements Serializable {
+
+    /* ----- Constants ----- */
+
+    // defaults for constructors
+    private static final int DEFAULT_DIGITS = 9;
+    private static final RoundingMode DEFAULT_ROUNDINGMODE = RoundingMode.HALF_UP;
+    // Smallest values for digits (Maximum is Integer.MAX_VALUE)
+    private static final int MIN_DIGITS = 0;
+
+    // Serialization version
+    private static final long serialVersionUID = 5579720004786848255L;
+
+    /* ----- Public Properties ----- */
+    /**
+     *  A {@code MathContext} object whose settings have the values
+     *  required for unlimited precision arithmetic.
+     *  The values of the settings are:
+     *  <code>
+     *  precision=0 roundingMode=HALF_UP
+     *  </code>
+     */
+    public static final MathContext UNLIMITED =
+        new MathContext(0, RoundingMode.HALF_UP);
+
+    /**
+     *  A {@code MathContext} object with a precision setting
+     *  matching the IEEE 754R Decimal32 format, 7 digits, and a
+     *  rounding mode of {@link RoundingMode#HALF_EVEN HALF_EVEN}, the
+     *  IEEE 754R default.
+     */
+    public static final MathContext DECIMAL32 =
+        new MathContext(7, RoundingMode.HALF_EVEN);
+
+    /**
+     *  A {@code MathContext} object with a precision setting
+     *  matching the IEEE 754R Decimal64 format, 16 digits, and a
+     *  rounding mode of {@link RoundingMode#HALF_EVEN HALF_EVEN}, the
+     *  IEEE 754R default.
+     */
+    public static final MathContext DECIMAL64 =
+        new MathContext(16, RoundingMode.HALF_EVEN);
+
+    /**
+     *  A {@code MathContext} object with a precision setting
+     *  matching the IEEE 754R Decimal128 format, 34 digits, and a
+     *  rounding mode of {@link RoundingMode#HALF_EVEN HALF_EVEN}, the
+     *  IEEE 754R default.
+     */
+    public static final MathContext DECIMAL128 =
+        new MathContext(34, RoundingMode.HALF_EVEN);
+
+    /* ----- Shared Properties ----- */
+    /**
+     * The number of digits to be used for an operation.  A value of 0
+     * indicates that unlimited precision (as many digits as are
+     * required) will be used.  Note that leading zeros (in the
+     * coefficient of a number) are never significant.
+     *
+     * <p>{@code precision} will always be non-negative.
+     *
+     * @serial
+     */
+    final int precision;
+
+    /**
+     * The rounding algorithm to be used for an operation.
+     *
+     * @see RoundingMode
+     * @serial
+     */
+    final RoundingMode roundingMode;
+
+    /* ----- Constructors ----- */
+
+    /**
+     * Constructs a new {@code MathContext} with the specified
+     * precision and the {@link RoundingMode#HALF_UP HALF_UP} rounding
+     * mode.
+     *
+     * @param setPrecision The non-negative {@code int} precision setting.
+     * @throws IllegalArgumentException if the {@code setPrecision} parameter is less
+     *         than zero.
+     */
+    public MathContext(int setPrecision) {
+        this(setPrecision, DEFAULT_ROUNDINGMODE);
+        return;
+    }
+
+    /**
+     * Constructs a new {@code MathContext} with a specified
+     * precision and rounding mode.
+     *
+     * @param setPrecision The non-negative {@code int} precision setting.
+     * @param setRoundingMode The rounding mode to use.
+     * @throws IllegalArgumentException if the {@code setPrecision} parameter is less
+     *         than zero.
+     * @throws NullPointerException if the rounding mode argument is {@code null}
+     */
+    public MathContext(int setPrecision,
+                       RoundingMode setRoundingMode) {
+        if (setPrecision < MIN_DIGITS)
+            throw new IllegalArgumentException("Digits < 0");
+        if (setRoundingMode == null)
+            throw new NullPointerException("null RoundingMode");
+
+        precision = setPrecision;
+        roundingMode = setRoundingMode;
+        return;
+    }
+
+    /**
+     * Constructs a new {@code MathContext} from a string.
+     *
+     * The string must be in the same format as that produced by the
+     * {@link #toString} method.
+     *
+     * <p>An {@code IllegalArgumentException} is thrown if the precision
+     * section of the string is out of range ({@code < 0}) or the string is
+     * not in the format created by the {@link #toString} method.
+     *
+     * @param val The string to be parsed
+     * @throws IllegalArgumentException if the precision section is out of range
+     * or of incorrect format
+     * @throws NullPointerException if the argument is {@code null}
+     */
+    public MathContext(String val) {
+        boolean bad = false;
+        int setPrecision;
+        if (val == null)
+            throw new NullPointerException("null String");
+        try { // any error here is a string format problem
+            if (!val.startsWith("precision=")) throw new RuntimeException();
+            int fence = val.indexOf(' ');    // could be -1
+            int off = 10;                     // where value starts
+            setPrecision = Integer.parseInt(val.substring(10, fence));
+
+            if (!val.startsWith("roundingMode=", fence+1))
+                throw new RuntimeException();
+            off = fence + 1 + 13;
+            String str = val.substring(off, val.length());
+            roundingMode = RoundingMode.valueOf(str);
+        } catch (RuntimeException re) {
+            throw new IllegalArgumentException("bad string format");
+        }
+
+        if (setPrecision < MIN_DIGITS)
+            throw new IllegalArgumentException("Digits < 0");
+        // the other parameters cannot be invalid if we got here
+        precision = setPrecision;
+    }
+
+    /**
+     * Returns the {@code precision} setting.
+     * This value is always non-negative.
+     *
+     * @return an {@code int} which is the value of the {@code precision}
+     *         setting
+     */
+    public int getPrecision() {
+        return precision;
+    }
+
+    /**
+     * Returns the roundingMode setting.
+     * This will be one of
+     * {@link  RoundingMode#CEILING},
+     * {@link  RoundingMode#DOWN},
+     * {@link  RoundingMode#FLOOR},
+     * {@link  RoundingMode#HALF_DOWN},
+     * {@link  RoundingMode#HALF_EVEN},
+     * {@link  RoundingMode#HALF_UP},
+     * {@link  RoundingMode#UNNECESSARY}, or
+     * {@link  RoundingMode#UP}.
+     *
+     * @return a {@code RoundingMode} object which is the value of the
+     *         {@code roundingMode} setting
+     */
+
+    public RoundingMode getRoundingMode() {
+        return roundingMode;
+    }
+
+    /**
+     * Compares this {@code MathContext} with the specified
+     * {@code Object} for equality.
+     *
+     * @param  x {@code Object} to which this {@code MathContext} is to
+     *         be compared.
+     * @return {@code true} if and only if the specified {@code Object} is
+     *         a {@code MathContext} object which has exactly the same
+     *         settings as this object
+     */
+    public boolean equals(Object x){
+        MathContext mc;
+        if (!(x instanceof MathContext))
+            return false;
+        mc = (MathContext) x;
+        return mc.precision == this.precision
+            && mc.roundingMode == this.roundingMode; // no need for .equals()
+    }
+
+    /**
+     * Returns the hash code for this {@code MathContext}.
+     *
+     * @return hash code for this {@code MathContext}
+     */
+    public int hashCode() {
+        return this.precision + roundingMode.hashCode() * 59;
+    }
+
+    /**
+     * Returns the string representation of this {@code MathContext}.
+     * The {@code String} returned represents the settings of the
+     * {@code MathContext} object as two space-delimited words
+     * (separated by a single space character, <tt>'&#92;u0020'</tt>,
+     * and with no leading or trailing white space), as follows:
+     * <ol>
+     * <li>
+     * The string {@code "precision="}, immediately followed
+     * by the value of the precision setting as a numeric string as if
+     * generated by the {@link Integer#toString(int) Integer.toString}
+     * method.
+     *
+     * <li>
+     * The string {@code "roundingMode="}, immediately
+     * followed by the value of the {@code roundingMode} setting as a
+     * word.  This word will be the same as the name of the
+     * corresponding public constant in the {@link RoundingMode}
+     * enum.
+     * </ol>
+     * <p>
+     * For example:
+     * <pre>
+     * precision=9 roundingMode=HALF_UP
+     * </pre>
+     *
+     * Additional words may be appended to the result of
+     * {@code toString} in the future if more properties are added to
+     * this class.
+     *
+     * @return a {@code String} representing the context settings
+     */
+    public java.lang.String toString() {
+        return "precision=" +           precision + " " +
+               "roundingMode=" +        roundingMode.toString();
+    }
+
+    // Private methods
+
+    /**
+     * Reconstitute the {@code MathContext} instance from a stream (that is,
+     * deserialize it).
+     *
+     * @param s the stream being read.
+     */
+    private void readObject(java.io.ObjectInputStream s)
+        throws java.io.IOException, ClassNotFoundException {
+        s.defaultReadObject();     // read in all fields
+        // validate possibly bad fields
+        if (precision < MIN_DIGITS) {
+            String message = "MathContext: invalid digits in stream";
+            throw new java.io.StreamCorruptedException(message);
+        }
+        if (roundingMode == null) {
+            String message = "MathContext: null roundingMode in stream";
+            throw new java.io.StreamCorruptedException(message);
+        }
+    }
+
+}
diff --git a/ojluni/src/main/java/java/math/MutableBigInteger.java b/ojluni/src/main/java/java/math/MutableBigInteger.java
new file mode 100644
index 0000000..b9cb0fb
--- /dev/null
+++ b/ojluni/src/main/java/java/math/MutableBigInteger.java
@@ -0,0 +1,2263 @@
+/*
+ * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.math;
+
+/**
+ * A class used to represent multiprecision integers that makes efficient
+ * use of allocated space by allowing a number to occupy only part of
+ * an array so that the arrays do not have to be reallocated as often.
+ * When performing an operation with many iterations the array used to
+ * hold a number is only reallocated when necessary and does not have to
+ * be the same size as the number it represents. A mutable number allows
+ * calculations to occur on the same number without having to create
+ * a new number for every step of the calculation as occurs with
+ * BigIntegers.
+ *
+ * @see     BigInteger
+ * @author  Michael McCloskey
+ * @author  Timothy Buktu
+ * @since   1.3
+ */
+
+import static java.math.BigDecimal.INFLATED;
+import static java.math.BigInteger.LONG_MASK;
+import java.util.Arrays;
+
+class MutableBigInteger {
+    /**
+     * Holds the magnitude of this MutableBigInteger in big endian order.
+     * The magnitude may start at an offset into the value array, and it may
+     * end before the length of the value array.
+     */
+    int[] value;
+
+    /**
+     * The number of ints of the value array that are currently used
+     * to hold the magnitude of this MutableBigInteger. The magnitude starts
+     * at an offset and offset + intLen may be less than value.length.
+     */
+    int intLen;
+
+    /**
+     * The offset into the value array where the magnitude of this
+     * MutableBigInteger begins.
+     */
+    int offset = 0;
+
+    // Constants
+    /**
+     * MutableBigInteger with one element value array with the value 1. Used by
+     * BigDecimal divideAndRound to increment the quotient. Use this constant
+     * only when the method is not going to modify this object.
+     */
+    static final MutableBigInteger ONE = new MutableBigInteger(1);
+
+    /**
+     * The minimum {@code intLen} for cancelling powers of two before
+     * dividing.
+     * If the number of ints is less than this threshold,
+     * {@code divideKnuth} does not eliminate common powers of two from
+     * the dividend and divisor.
+     */
+    static final int KNUTH_POW2_THRESH_LEN = 6;
+
+    /**
+     * The minimum number of trailing zero ints for cancelling powers of two
+     * before dividing.
+     * If the dividend and divisor don't share at least this many zero ints
+     * at the end, {@code divideKnuth} does not eliminate common powers
+     * of two from the dividend and divisor.
+     */
+    static final int KNUTH_POW2_THRESH_ZEROS = 3;
+
+    // Constructors
+
+    /**
+     * The default constructor. An empty MutableBigInteger is created with
+     * a one word capacity.
+     */
+    MutableBigInteger() {
+        value = new int[1];
+        intLen = 0;
+    }
+
+    /**
+     * Construct a new MutableBigInteger with a magnitude specified by
+     * the int val.
+     */
+    MutableBigInteger(int val) {
+        value = new int[1];
+        intLen = 1;
+        value[0] = val;
+    }
+
+    /**
+     * Construct a new MutableBigInteger with the specified value array
+     * up to the length of the array supplied.
+     */
+    MutableBigInteger(int[] val) {
+        value = val;
+        intLen = val.length;
+    }
+
+    /**
+     * Construct a new MutableBigInteger with a magnitude equal to the
+     * specified BigInteger.
+     */
+    MutableBigInteger(BigInteger b) {
+        intLen = b.mag.length;
+        value = Arrays.copyOf(b.mag, intLen);
+    }
+
+    /**
+     * Construct a new MutableBigInteger with a magnitude equal to the
+     * specified MutableBigInteger.
+     */
+    MutableBigInteger(MutableBigInteger val) {
+        intLen = val.intLen;
+        value = Arrays.copyOfRange(val.value, val.offset, val.offset + intLen);
+    }
+
+    /**
+     * Makes this number an {@code n}-int number all of whose bits are ones.
+     * Used by Burnikel-Ziegler division.
+     * @param n number of ints in the {@code value} array
+     * @return a number equal to {@code ((1<<(32*n)))-1}
+     */
+    private void ones(int n) {
+        if (n > value.length)
+            value = new int[n];
+        Arrays.fill(value, -1);
+        offset = 0;
+        intLen = n;
+    }
+
+    /**
+     * Internal helper method to return the magnitude array. The caller is not
+     * supposed to modify the returned array.
+     */
+    private int[] getMagnitudeArray() {
+        if (offset > 0 || value.length != intLen)
+            return Arrays.copyOfRange(value, offset, offset + intLen);
+        return value;
+    }
+
+    /**
+     * Convert this MutableBigInteger to a long value. The caller has to make
+     * sure this MutableBigInteger can be fit into long.
+     */
+    private long toLong() {
+        assert (intLen <= 2) : "this MutableBigInteger exceeds the range of long";
+        if (intLen == 0)
+            return 0;
+        long d = value[offset] & LONG_MASK;
+        return (intLen == 2) ? d << 32 | (value[offset + 1] & LONG_MASK) : d;
+    }
+
+    /**
+     * Convert this MutableBigInteger to a BigInteger object.
+     */
+    BigInteger toBigInteger(int sign) {
+        if (intLen == 0 || sign == 0)
+            return BigInteger.ZERO;
+        return new BigInteger(getMagnitudeArray(), sign);
+    }
+
+    /**
+     * Converts this number to a nonnegative {@code BigInteger}.
+     */
+    BigInteger toBigInteger() {
+        normalize();
+        return toBigInteger(isZero() ? 0 : 1);
+    }
+
+    /**
+     * Convert this MutableBigInteger to BigDecimal object with the specified sign
+     * and scale.
+     */
+    BigDecimal toBigDecimal(int sign, int scale) {
+        if (intLen == 0 || sign == 0)
+            return BigDecimal.zeroValueOf(scale);
+        int[] mag = getMagnitudeArray();
+        int len = mag.length;
+        int d = mag[0];
+        // If this MutableBigInteger can't be fit into long, we need to
+        // make a BigInteger object for the resultant BigDecimal object.
+        if (len > 2 || (d < 0 && len == 2))
+            return new BigDecimal(new BigInteger(mag, sign), INFLATED, scale, 0);
+        long v = (len == 2) ?
+            ((mag[1] & LONG_MASK) | (d & LONG_MASK) << 32) :
+            d & LONG_MASK;
+        return BigDecimal.valueOf(sign == -1 ? -v : v, scale);
+    }
+
+    /**
+     * This is for internal use in converting from a MutableBigInteger
+     * object into a long value given a specified sign.
+     * returns INFLATED if value is not fit into long
+     */
+    long toCompactValue(int sign) {
+        if (intLen == 0 || sign == 0)
+            return 0L;
+        int[] mag = getMagnitudeArray();
+        int len = mag.length;
+        int d = mag[0];
+        // If this MutableBigInteger can not be fitted into long, we need to
+        // make a BigInteger object for the resultant BigDecimal object.
+        if (len > 2 || (d < 0 && len == 2))
+            return INFLATED;
+        long v = (len == 2) ?
+            ((mag[1] & LONG_MASK) | (d & LONG_MASK) << 32) :
+            d & LONG_MASK;
+        return sign == -1 ? -v : v;
+    }
+
+    /**
+     * Clear out a MutableBigInteger for reuse.
+     */
+    void clear() {
+        offset = intLen = 0;
+        for (int index=0, n=value.length; index < n; index++)
+            value[index] = 0;
+    }
+
+    /**
+     * Set a MutableBigInteger to zero, removing its offset.
+     */
+    void reset() {
+        offset = intLen = 0;
+    }
+
+    /**
+     * Compare the magnitude of two MutableBigIntegers. Returns -1, 0 or 1
+     * as this MutableBigInteger is numerically less than, equal to, or
+     * greater than <tt>b</tt>.
+     */
+    final int compare(MutableBigInteger b) {
+        int blen = b.intLen;
+        if (intLen < blen)
+            return -1;
+        if (intLen > blen)
+           return 1;
+
+        // Add Integer.MIN_VALUE to make the comparison act as unsigned integer
+        // comparison.
+        int[] bval = b.value;
+        for (int i = offset, j = b.offset; i < intLen + offset; i++, j++) {
+            int b1 = value[i] + 0x80000000;
+            int b2 = bval[j]  + 0x80000000;
+            if (b1 < b2)
+                return -1;
+            if (b1 > b2)
+                return 1;
+        }
+        return 0;
+    }
+
+    /**
+     * Returns a value equal to what {@code b.leftShift(32*ints); return compare(b);}
+     * would return, but doesn't change the value of {@code b}.
+     */
+    private int compareShifted(MutableBigInteger b, int ints) {
+        int blen = b.intLen;
+        int alen = intLen - ints;
+        if (alen < blen)
+            return -1;
+        if (alen > blen)
+           return 1;
+
+        // Add Integer.MIN_VALUE to make the comparison act as unsigned integer
+        // comparison.
+        int[] bval = b.value;
+        for (int i = offset, j = b.offset; i < alen + offset; i++, j++) {
+            int b1 = value[i] + 0x80000000;
+            int b2 = bval[j]  + 0x80000000;
+            if (b1 < b2)
+                return -1;
+            if (b1 > b2)
+                return 1;
+        }
+        return 0;
+    }
+
+    /**
+     * Compare this against half of a MutableBigInteger object (Needed for
+     * remainder tests).
+     * Assumes no leading unnecessary zeros, which holds for results
+     * from divide().
+     */
+    final int compareHalf(MutableBigInteger b) {
+        int blen = b.intLen;
+        int len = intLen;
+        if (len <= 0)
+            return blen <= 0 ? 0 : -1;
+        if (len > blen)
+            return 1;
+        if (len < blen - 1)
+            return -1;
+        int[] bval = b.value;
+        int bstart = 0;
+        int carry = 0;
+        // Only 2 cases left:len == blen or len == blen - 1
+        if (len != blen) { // len == blen - 1
+            if (bval[bstart] == 1) {
+                ++bstart;
+                carry = 0x80000000;
+            } else
+                return -1;
+        }
+        // compare values with right-shifted values of b,
+        // carrying shifted-out bits across words
+        int[] val = value;
+        for (int i = offset, j = bstart; i < len + offset;) {
+            int bv = bval[j++];
+            long hb = ((bv >>> 1) + carry) & LONG_MASK;
+            long v = val[i++] & LONG_MASK;
+            if (v != hb)
+                return v < hb ? -1 : 1;
+            carry = (bv & 1) << 31; // carray will be either 0x80000000 or 0
+        }
+        return carry == 0 ? 0 : -1;
+    }
+
+    /**
+     * Return the index of the lowest set bit in this MutableBigInteger. If the
+     * magnitude of this MutableBigInteger is zero, -1 is returned.
+     */
+    private final int getLowestSetBit() {
+        if (intLen == 0)
+            return -1;
+        int j, b;
+        for (j=intLen-1; (j > 0) && (value[j+offset] == 0); j--)
+            ;
+        b = value[j+offset];
+        if (b == 0)
+            return -1;
+        return ((intLen-1-j)<<5) + Integer.numberOfTrailingZeros(b);
+    }
+
+    /**
+     * Return the int in use in this MutableBigInteger at the specified
+     * index. This method is not used because it is not inlined on all
+     * platforms.
+     */
+    private final int getInt(int index) {
+        return value[offset+index];
+    }
+
+    /**
+     * Return a long which is equal to the unsigned value of the int in
+     * use in this MutableBigInteger at the specified index. This method is
+     * not used because it is not inlined on all platforms.
+     */
+    private final long getLong(int index) {
+        return value[offset+index] & LONG_MASK;
+    }
+
+    /**
+     * Ensure that the MutableBigInteger is in normal form, specifically
+     * making sure that there are no leading zeros, and that if the
+     * magnitude is zero, then intLen is zero.
+     */
+    final void normalize() {
+        if (intLen == 0) {
+            offset = 0;
+            return;
+        }
+
+        int index = offset;
+        if (value[index] != 0)
+            return;
+
+        int indexBound = index+intLen;
+        do {
+            index++;
+        } while(index < indexBound && value[index] == 0);
+
+        int numZeros = index - offset;
+        intLen -= numZeros;
+        offset = (intLen == 0 ?  0 : offset+numZeros);
+    }
+
+    /**
+     * If this MutableBigInteger cannot hold len words, increase the size
+     * of the value array to len words.
+     */
+    private final void ensureCapacity(int len) {
+        if (value.length < len) {
+            value = new int[len];
+            offset = 0;
+            intLen = len;
+        }
+    }
+
+    /**
+     * Convert this MutableBigInteger into an int array with no leading
+     * zeros, of a length that is equal to this MutableBigInteger's intLen.
+     */
+    int[] toIntArray() {
+        int[] result = new int[intLen];
+        for(int i=0; i < intLen; i++)
+            result[i] = value[offset+i];
+        return result;
+    }
+
+    /**
+     * Sets the int at index+offset in this MutableBigInteger to val.
+     * This does not get inlined on all platforms so it is not used
+     * as often as originally intended.
+     */
+    void setInt(int index, int val) {
+        value[offset + index] = val;
+    }
+
+    /**
+     * Sets this MutableBigInteger's value array to the specified array.
+     * The intLen is set to the specified length.
+     */
+    void setValue(int[] val, int length) {
+        value = val;
+        intLen = length;
+        offset = 0;
+    }
+
+    /**
+     * Sets this MutableBigInteger's value array to a copy of the specified
+     * array. The intLen is set to the length of the new array.
+     */
+    void copyValue(MutableBigInteger src) {
+        int len = src.intLen;
+        if (value.length < len)
+            value = new int[len];
+        System.arraycopy(src.value, src.offset, value, 0, len);
+        intLen = len;
+        offset = 0;
+    }
+
+    /**
+     * Sets this MutableBigInteger's value array to a copy of the specified
+     * array. The intLen is set to the length of the specified array.
+     */
+    void copyValue(int[] val) {
+        int len = val.length;
+        if (value.length < len)
+            value = new int[len];
+        System.arraycopy(val, 0, value, 0, len);
+        intLen = len;
+        offset = 0;
+    }
+
+    /**
+     * Returns true iff this MutableBigInteger has a value of one.
+     */
+    boolean isOne() {
+        return (intLen == 1) && (value[offset] == 1);
+    }
+
+    /**
+     * Returns true iff this MutableBigInteger has a value of zero.
+     */
+    boolean isZero() {
+        return (intLen == 0);
+    }
+
+    /**
+     * Returns true iff this MutableBigInteger is even.
+     */
+    boolean isEven() {
+        return (intLen == 0) || ((value[offset + intLen - 1] & 1) == 0);
+    }
+
+    /**
+     * Returns true iff this MutableBigInteger is odd.
+     */
+    boolean isOdd() {
+        return isZero() ? false : ((value[offset + intLen - 1] & 1) == 1);
+    }
+
+    /**
+     * Returns true iff this MutableBigInteger is in normal form. A
+     * MutableBigInteger is in normal form if it has no leading zeros
+     * after the offset, and intLen + offset <= value.length.
+     */
+    boolean isNormal() {
+        if (intLen + offset > value.length)
+            return false;
+        if (intLen == 0)
+            return true;
+        return (value[offset] != 0);
+    }
+
+    /**
+     * Returns a String representation of this MutableBigInteger in radix 10.
+     */
+    public String toString() {
+        BigInteger b = toBigInteger(1);
+        return b.toString();
+    }
+
+    /**
+     * Like {@link #rightShift(int)} but {@code n} can be greater than the length of the number.
+     */
+    void safeRightShift(int n) {
+        if (n/32 >= intLen) {
+            reset();
+        } else {
+            rightShift(n);
+        }
+    }
+
+    /**
+     * Right shift this MutableBigInteger n bits. The MutableBigInteger is left
+     * in normal form.
+     */
+    void rightShift(int n) {
+        if (intLen == 0)
+            return;
+        int nInts = n >>> 5;
+        int nBits = n & 0x1F;
+        this.intLen -= nInts;
+        if (nBits == 0)
+            return;
+        int bitsInHighWord = BigInteger.bitLengthForInt(value[offset]);
+        if (nBits >= bitsInHighWord) {
+            this.primitiveLeftShift(32 - nBits);
+            this.intLen--;
+        } else {
+            primitiveRightShift(nBits);
+        }
+    }
+
+    /**
+     * Like {@link #leftShift(int)} but {@code n} can be zero.
+     */
+    void safeLeftShift(int n) {
+        if (n > 0) {
+            leftShift(n);
+        }
+    }
+
+    /**
+     * Left shift this MutableBigInteger n bits.
+     */
+    void leftShift(int n) {
+        /*
+         * If there is enough storage space in this MutableBigInteger already
+         * the available space will be used. Space to the right of the used
+         * ints in the value array is faster to utilize, so the extra space
+         * will be taken from the right if possible.
+         */
+        if (intLen == 0)
+           return;
+        int nInts = n >>> 5;
+        int nBits = n&0x1F;
+        int bitsInHighWord = BigInteger.bitLengthForInt(value[offset]);
+
+        // If shift can be done without moving words, do so
+        if (n <= (32-bitsInHighWord)) {
+            primitiveLeftShift(nBits);
+            return;
+        }
+
+        int newLen = intLen + nInts +1;
+        if (nBits <= (32-bitsInHighWord))
+            newLen--;
+        if (value.length < newLen) {
+            // The array must grow
+            int[] result = new int[newLen];
+            for (int i=0; i < intLen; i++)
+                result[i] = value[offset+i];
+            setValue(result, newLen);
+        } else if (value.length - offset >= newLen) {
+            // Use space on right
+            for(int i=0; i < newLen - intLen; i++)
+                value[offset+intLen+i] = 0;
+        } else {
+            // Must use space on left
+            for (int i=0; i < intLen; i++)
+                value[i] = value[offset+i];
+            for (int i=intLen; i < newLen; i++)
+                value[i] = 0;
+            offset = 0;
+        }
+        intLen = newLen;
+        if (nBits == 0)
+            return;
+        if (nBits <= (32-bitsInHighWord))
+            primitiveLeftShift(nBits);
+        else
+            primitiveRightShift(32 -nBits);
+    }
+
+    /**
+     * A primitive used for division. This method adds in one multiple of the
+     * divisor a back to the dividend result at a specified offset. It is used
+     * when qhat was estimated too large, and must be adjusted.
+     */
+    private int divadd(int[] a, int[] result, int offset) {
+        long carry = 0;
+
+        for (int j=a.length-1; j >= 0; j--) {
+            long sum = (a[j] & LONG_MASK) +
+                       (result[j+offset] & LONG_MASK) + carry;
+            result[j+offset] = (int)sum;
+            carry = sum >>> 32;
+        }
+        return (int)carry;
+    }
+
+    /**
+     * This method is used for division. It multiplies an n word input a by one
+     * word input x, and subtracts the n word product from q. This is needed
+     * when subtracting qhat*divisor from dividend.
+     */
+    private int mulsub(int[] q, int[] a, int x, int len, int offset) {
+        long xLong = x & LONG_MASK;
+        long carry = 0;
+        offset += len;
+
+        for (int j=len-1; j >= 0; j--) {
+            long product = (a[j] & LONG_MASK) * xLong + carry;
+            long difference = q[offset] - product;
+            q[offset--] = (int)difference;
+            carry = (product >>> 32)
+                     + (((difference & LONG_MASK) >
+                         (((~(int)product) & LONG_MASK))) ? 1:0);
+        }
+        return (int)carry;
+    }
+
+    /**
+     * The method is the same as mulsun, except the fact that q array is not
+     * updated, the only result of the method is borrow flag.
+     */
+    private int mulsubBorrow(int[] q, int[] a, int x, int len, int offset) {
+        long xLong = x & LONG_MASK;
+        long carry = 0;
+        offset += len;
+        for (int j=len-1; j >= 0; j--) {
+            long product = (a[j] & LONG_MASK) * xLong + carry;
+            long difference = q[offset--] - product;
+            carry = (product >>> 32)
+                     + (((difference & LONG_MASK) >
+                         (((~(int)product) & LONG_MASK))) ? 1:0);
+        }
+        return (int)carry;
+    }
+
+    /**
+     * Right shift this MutableBigInteger n bits, where n is
+     * less than 32.
+     * Assumes that intLen > 0, n > 0 for speed
+     */
+    private final void primitiveRightShift(int n) {
+        int[] val = value;
+        int n2 = 32 - n;
+        for (int i=offset+intLen-1, c=val[i]; i > offset; i--) {
+            int b = c;
+            c = val[i-1];
+            val[i] = (c << n2) | (b >>> n);
+        }
+        val[offset] >>>= n;
+    }
+
+    /**
+     * Left shift this MutableBigInteger n bits, where n is
+     * less than 32.
+     * Assumes that intLen > 0, n > 0 for speed
+     */
+    private final void primitiveLeftShift(int n) {
+        int[] val = value;
+        int n2 = 32 - n;
+        for (int i=offset, c=val[i], m=i+intLen-1; i < m; i++) {
+            int b = c;
+            c = val[i+1];
+            val[i] = (b << n) | (c >>> n2);
+        }
+        val[offset+intLen-1] <<= n;
+    }
+
+    /**
+     * Returns a {@code BigInteger} equal to the {@code n}
+     * low ints of this number.
+     */
+    private BigInteger getLower(int n) {
+        if (isZero()) {
+            return BigInteger.ZERO;
+        } else if (intLen < n) {
+            return toBigInteger(1);
+        } else {
+            // strip zeros
+            int len = n;
+            while (len > 0 && value[offset+intLen-len] == 0)
+                len--;
+            int sign = len > 0 ? 1 : 0;
+            return new BigInteger(Arrays.copyOfRange(value, offset+intLen-len, offset+intLen), sign);
+        }
+    }
+
+    /**
+     * Discards all ints whose index is greater than {@code n}.
+     */
+    private void keepLower(int n) {
+        if (intLen >= n) {
+            offset += intLen - n;
+            intLen = n;
+        }
+    }
+
+    /**
+     * Adds the contents of two MutableBigInteger objects.The result
+     * is placed within this MutableBigInteger.
+     * The contents of the addend are not changed.
+     */
+    void add(MutableBigInteger addend) {
+        int x = intLen;
+        int y = addend.intLen;
+        int resultLen = (intLen > addend.intLen ? intLen : addend.intLen);
+        int[] result = (value.length < resultLen ? new int[resultLen] : value);
+
+        int rstart = result.length-1;
+        long sum;
+        long carry = 0;
+
+        // Add common parts of both numbers
+        while(x > 0 && y > 0) {
+            x--; y--;
+            sum = (value[x+offset] & LONG_MASK) +
+                (addend.value[y+addend.offset] & LONG_MASK) + carry;
+            result[rstart--] = (int)sum;
+            carry = sum >>> 32;
+        }
+
+        // Add remainder of the longer number
+        while(x > 0) {
+            x--;
+            if (carry == 0 && result == value && rstart == (x + offset))
+                return;
+            sum = (value[x+offset] & LONG_MASK) + carry;
+            result[rstart--] = (int)sum;
+            carry = sum >>> 32;
+        }
+        while(y > 0) {
+            y--;
+            sum = (addend.value[y+addend.offset] & LONG_MASK) + carry;
+            result[rstart--] = (int)sum;
+            carry = sum >>> 32;
+        }
+
+        if (carry > 0) { // Result must grow in length
+            resultLen++;
+            if (result.length < resultLen) {
+                int temp[] = new int[resultLen];
+                // Result one word longer from carry-out; copy low-order
+                // bits into new result.
+                System.arraycopy(result, 0, temp, 1, result.length);
+                temp[0] = 1;
+                result = temp;
+            } else {
+                result[rstart--] = 1;
+            }
+        }
+
+        value = result;
+        intLen = resultLen;
+        offset = result.length - resultLen;
+    }
+
+    /**
+     * Adds the value of {@code addend} shifted {@code n} ints to the left.
+     * Has the same effect as {@code addend.leftShift(32*ints); add(addend);}
+     * but doesn't change the value of {@code addend}.
+     */
+    void addShifted(MutableBigInteger addend, int n) {
+        if (addend.isZero()) {
+            return;
+        }
+
+        int x = intLen;
+        int y = addend.intLen + n;
+        int resultLen = (intLen > y ? intLen : y);
+        int[] result = (value.length < resultLen ? new int[resultLen] : value);
+
+        int rstart = result.length-1;
+        long sum;
+        long carry = 0;
+
+        // Add common parts of both numbers
+        while (x > 0 && y > 0) {
+            x--; y--;
+            int bval = y+addend.offset < addend.value.length ? addend.value[y+addend.offset] : 0;
+            sum = (value[x+offset] & LONG_MASK) +
+                (bval & LONG_MASK) + carry;
+            result[rstart--] = (int)sum;
+            carry = sum >>> 32;
+        }
+
+        // Add remainder of the longer number
+        while (x > 0) {
+            x--;
+            if (carry == 0 && result == value && rstart == (x + offset)) {
+                return;
+            }
+            sum = (value[x+offset] & LONG_MASK) + carry;
+            result[rstart--] = (int)sum;
+            carry = sum >>> 32;
+        }
+        while (y > 0) {
+            y--;
+            int bval = y+addend.offset < addend.value.length ? addend.value[y+addend.offset] : 0;
+            sum = (bval & LONG_MASK) + carry;
+            result[rstart--] = (int)sum;
+            carry = sum >>> 32;
+        }
+
+        if (carry > 0) { // Result must grow in length
+            resultLen++;
+            if (result.length < resultLen) {
+                int temp[] = new int[resultLen];
+                // Result one word longer from carry-out; copy low-order
+                // bits into new result.
+                System.arraycopy(result, 0, temp, 1, result.length);
+                temp[0] = 1;
+                result = temp;
+            } else {
+                result[rstart--] = 1;
+            }
+        }
+
+        value = result;
+        intLen = resultLen;
+        offset = result.length - resultLen;
+    }
+
+    /**
+     * Like {@link #addShifted(MutableBigInteger, int)} but {@code this.intLen} must
+     * not be greater than {@code n}. In other words, concatenates {@code this}
+     * and {@code addend}.
+     */
+    void addDisjoint(MutableBigInteger addend, int n) {
+        if (addend.isZero())
+            return;
+
+        int x = intLen;
+        int y = addend.intLen + n;
+        int resultLen = (intLen > y ? intLen : y);
+        int[] result;
+        if (value.length < resultLen)
+            result = new int[resultLen];
+        else {
+            result = value;
+            Arrays.fill(value, offset+intLen, value.length, 0);
+        }
+
+        int rstart = result.length-1;
+
+        // copy from this if needed
+        System.arraycopy(value, offset, result, rstart+1-x, x);
+        y -= x;
+        rstart -= x;
+
+        int len = Math.min(y, addend.value.length-addend.offset);
+        System.arraycopy(addend.value, addend.offset, result, rstart+1-y, len);
+
+        // zero the gap
+        for (int i=rstart+1-y+len; i < rstart+1; i++)
+            result[i] = 0;
+
+        value = result;
+        intLen = resultLen;
+        offset = result.length - resultLen;
+    }
+
+    /**
+     * Adds the low {@code n} ints of {@code addend}.
+     */
+    void addLower(MutableBigInteger addend, int n) {
+        MutableBigInteger a = new MutableBigInteger(addend);
+        if (a.offset + a.intLen >= n) {
+            a.offset = a.offset + a.intLen - n;
+            a.intLen = n;
+        }
+        a.normalize();
+        add(a);
+    }
+
+    /**
+     * Subtracts the smaller of this and b from the larger and places the
+     * result into this MutableBigInteger.
+     */
+    int subtract(MutableBigInteger b) {
+        MutableBigInteger a = this;
+
+        int[] result = value;
+        int sign = a.compare(b);
+
+        if (sign == 0) {
+            reset();
+            return 0;
+        }
+        if (sign < 0) {
+            MutableBigInteger tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        int resultLen = a.intLen;
+        if (result.length < resultLen)
+            result = new int[resultLen];
+
+        long diff = 0;
+        int x = a.intLen;
+        int y = b.intLen;
+        int rstart = result.length - 1;
+
+        // Subtract common parts of both numbers
+        while (y > 0) {
+            x--; y--;
+
+            diff = (a.value[x+a.offset] & LONG_MASK) -
+                   (b.value[y+b.offset] & LONG_MASK) - ((int)-(diff>>32));
+            result[rstart--] = (int)diff;
+        }
+        // Subtract remainder of longer number
+        while (x > 0) {
+            x--;
+            diff = (a.value[x+a.offset] & LONG_MASK) - ((int)-(diff>>32));
+            result[rstart--] = (int)diff;
+        }
+
+        value = result;
+        intLen = resultLen;
+        offset = value.length - resultLen;
+        normalize();
+        return sign;
+    }
+
+    /**
+     * Subtracts the smaller of a and b from the larger and places the result
+     * into the larger. Returns 1 if the answer is in a, -1 if in b, 0 if no
+     * operation was performed.
+     */
+    private int difference(MutableBigInteger b) {
+        MutableBigInteger a = this;
+        int sign = a.compare(b);
+        if (sign == 0)
+            return 0;
+        if (sign < 0) {
+            MutableBigInteger tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        long diff = 0;
+        int x = a.intLen;
+        int y = b.intLen;
+
+        // Subtract common parts of both numbers
+        while (y > 0) {
+            x--; y--;
+            diff = (a.value[a.offset+ x] & LONG_MASK) -
+                (b.value[b.offset+ y] & LONG_MASK) - ((int)-(diff>>32));
+            a.value[a.offset+x] = (int)diff;
+        }
+        // Subtract remainder of longer number
+        while (x > 0) {
+            x--;
+            diff = (a.value[a.offset+ x] & LONG_MASK) - ((int)-(diff>>32));
+            a.value[a.offset+x] = (int)diff;
+        }
+
+        a.normalize();
+        return sign;
+    }
+
+    /**
+     * Multiply the contents of two MutableBigInteger objects. The result is
+     * placed into MutableBigInteger z. The contents of y are not changed.
+     */
+    void multiply(MutableBigInteger y, MutableBigInteger z) {
+        int xLen = intLen;
+        int yLen = y.intLen;
+        int newLen = xLen + yLen;
+
+        // Put z into an appropriate state to receive product
+        if (z.value.length < newLen)
+            z.value = new int[newLen];
+        z.offset = 0;
+        z.intLen = newLen;
+
+        // The first iteration is hoisted out of the loop to avoid extra add
+        long carry = 0;
+        for (int j=yLen-1, k=yLen+xLen-1; j >= 0; j--, k--) {
+                long product = (y.value[j+y.offset] & LONG_MASK) *
+                               (value[xLen-1+offset] & LONG_MASK) + carry;
+                z.value[k] = (int)product;
+                carry = product >>> 32;
+        }
+        z.value[xLen-1] = (int)carry;
+
+        // Perform the multiplication word by word
+        for (int i = xLen-2; i >= 0; i--) {
+            carry = 0;
+            for (int j=yLen-1, k=yLen+i; j >= 0; j--, k--) {
+                long product = (y.value[j+y.offset] & LONG_MASK) *
+                               (value[i+offset] & LONG_MASK) +
+                               (z.value[k] & LONG_MASK) + carry;
+                z.value[k] = (int)product;
+                carry = product >>> 32;
+            }
+            z.value[i] = (int)carry;
+        }
+
+        // Remove leading zeros from product
+        z.normalize();
+    }
+
+    /**
+     * Multiply the contents of this MutableBigInteger by the word y. The
+     * result is placed into z.
+     */
+    void mul(int y, MutableBigInteger z) {
+        if (y == 1) {
+            z.copyValue(this);
+            return;
+        }
+
+        if (y == 0) {
+            z.clear();
+            return;
+        }
+
+        // Perform the multiplication word by word
+        long ylong = y & LONG_MASK;
+        int[] zval = (z.value.length < intLen+1 ? new int[intLen + 1]
+                                              : z.value);
+        long carry = 0;
+        for (int i = intLen-1; i >= 0; i--) {
+            long product = ylong * (value[i+offset] & LONG_MASK) + carry;
+            zval[i+1] = (int)product;
+            carry = product >>> 32;
+        }
+
+        if (carry == 0) {
+            z.offset = 1;
+            z.intLen = intLen;
+        } else {
+            z.offset = 0;
+            z.intLen = intLen + 1;
+            zval[0] = (int)carry;
+        }
+        z.value = zval;
+    }
+
+     /**
+     * This method is used for division of an n word dividend by a one word
+     * divisor. The quotient is placed into quotient. The one word divisor is
+     * specified by divisor.
+     *
+     * @return the remainder of the division is returned.
+     *
+     */
+    int divideOneWord(int divisor, MutableBigInteger quotient) {
+        long divisorLong = divisor & LONG_MASK;
+
+        // Special case of one word dividend
+        if (intLen == 1) {
+            long dividendValue = value[offset] & LONG_MASK;
+            int q = (int) (dividendValue / divisorLong);
+            int r = (int) (dividendValue - q * divisorLong);
+            quotient.value[0] = q;
+            quotient.intLen = (q == 0) ? 0 : 1;
+            quotient.offset = 0;
+            return r;
+        }
+
+        if (quotient.value.length < intLen)
+            quotient.value = new int[intLen];
+        quotient.offset = 0;
+        quotient.intLen = intLen;
+
+        // Normalize the divisor
+        int shift = Integer.numberOfLeadingZeros(divisor);
+
+        int rem = value[offset];
+        long remLong = rem & LONG_MASK;
+        if (remLong < divisorLong) {
+            quotient.value[0] = 0;
+        } else {
+            quotient.value[0] = (int)(remLong / divisorLong);
+            rem = (int) (remLong - (quotient.value[0] * divisorLong));
+            remLong = rem & LONG_MASK;
+        }
+        int xlen = intLen;
+        while (--xlen > 0) {
+            long dividendEstimate = (remLong << 32) |
+                    (value[offset + intLen - xlen] & LONG_MASK);
+            int q;
+            if (dividendEstimate >= 0) {
+                q = (int) (dividendEstimate / divisorLong);
+                rem = (int) (dividendEstimate - q * divisorLong);
+            } else {
+                long tmp = divWord(dividendEstimate, divisor);
+                q = (int) (tmp & LONG_MASK);
+                rem = (int) (tmp >>> 32);
+            }
+            quotient.value[intLen - xlen] = q;
+            remLong = rem & LONG_MASK;
+        }
+
+        quotient.normalize();
+        // Unnormalize
+        if (shift > 0)
+            return rem % divisor;
+        else
+            return rem;
+    }
+
+    /**
+     * Calculates the quotient of this div b and places the quotient in the
+     * provided MutableBigInteger objects and the remainder object is returned.
+     *
+     */
+    MutableBigInteger divide(MutableBigInteger b, MutableBigInteger quotient) {
+        return divide(b,quotient,true);
+    }
+
+    MutableBigInteger divide(MutableBigInteger b, MutableBigInteger quotient, boolean needRemainder) {
+        if (b.intLen < BigInteger.BURNIKEL_ZIEGLER_THRESHOLD ||
+                intLen - b.intLen < BigInteger.BURNIKEL_ZIEGLER_OFFSET) {
+            return divideKnuth(b, quotient, needRemainder);
+        } else {
+            return divideAndRemainderBurnikelZiegler(b, quotient);
+        }
+    }
+
+    /**
+     * @see #divideKnuth(MutableBigInteger, MutableBigInteger, boolean)
+     */
+    MutableBigInteger divideKnuth(MutableBigInteger b, MutableBigInteger quotient) {
+        return divideKnuth(b,quotient,true);
+    }
+
+    /**
+     * Calculates the quotient of this div b and places the quotient in the
+     * provided MutableBigInteger objects and the remainder object is returned.
+     *
+     * Uses Algorithm D in Knuth section 4.3.1.
+     * Many optimizations to that algorithm have been adapted from the Colin
+     * Plumb C library.
+     * It special cases one word divisors for speed. The content of b is not
+     * changed.
+     *
+     */
+    MutableBigInteger divideKnuth(MutableBigInteger b, MutableBigInteger quotient, boolean needRemainder) {
+        if (b.intLen == 0)
+            throw new ArithmeticException("BigInteger divide by zero");
+
+        // Dividend is zero
+        if (intLen == 0) {
+            quotient.intLen = quotient.offset = 0;
+            return needRemainder ? new MutableBigInteger() : null;
+        }
+
+        int cmp = compare(b);
+        // Dividend less than divisor
+        if (cmp < 0) {
+            quotient.intLen = quotient.offset = 0;
+            return needRemainder ? new MutableBigInteger(this) : null;
+        }
+        // Dividend equal to divisor
+        if (cmp == 0) {
+            quotient.value[0] = quotient.intLen = 1;
+            quotient.offset = 0;
+            return needRemainder ? new MutableBigInteger() : null;
+        }
+
+        quotient.clear();
+        // Special case one word divisor
+        if (b.intLen == 1) {
+            int r = divideOneWord(b.value[b.offset], quotient);
+            if(needRemainder) {
+                if (r == 0)
+                    return new MutableBigInteger();
+                return new MutableBigInteger(r);
+            } else {
+                return null;
+            }
+        }
+
+        // Cancel common powers of two if we're above the KNUTH_POW2_* thresholds
+        if (intLen >= KNUTH_POW2_THRESH_LEN) {
+            int trailingZeroBits = Math.min(getLowestSetBit(), b.getLowestSetBit());
+            if (trailingZeroBits >= KNUTH_POW2_THRESH_ZEROS*32) {
+                MutableBigInteger a = new MutableBigInteger(this);
+                b = new MutableBigInteger(b);
+                a.rightShift(trailingZeroBits);
+                b.rightShift(trailingZeroBits);
+                MutableBigInteger r = a.divideKnuth(b, quotient);
+                r.leftShift(trailingZeroBits);
+                return r;
+            }
+        }
+
+        return divideMagnitude(b, quotient, needRemainder);
+    }
+
+    /**
+     * Computes {@code this/b} and {@code this%b} using the
+     * <a href="http://cr.yp.to/bib/1998/burnikel.ps"> Burnikel-Ziegler algorithm</a>.
+     * This method implements algorithm 3 from pg. 9 of the Burnikel-Ziegler paper.
+     * The parameter beta was chosen to b 2<sup>32</sup> so almost all shifts are
+     * multiples of 32 bits.<br/>
+     * {@code this} and {@code b} must be nonnegative.
+     * @param b the divisor
+     * @param quotient output parameter for {@code this/b}
+     * @return the remainder
+     */
+    MutableBigInteger divideAndRemainderBurnikelZiegler(MutableBigInteger b, MutableBigInteger quotient) {
+        int r = intLen;
+        int s = b.intLen;
+
+        // Clear the quotient
+        quotient.offset = quotient.intLen = 0;
+
+        if (r < s) {
+            return this;
+        } else {
+            // Unlike Knuth division, we don't check for common powers of two here because
+            // BZ already runs faster if both numbers contain powers of two and cancelling them has no
+            // additional benefit.
+
+            // step 1: let m = min{2^k | (2^k)*BURNIKEL_ZIEGLER_THRESHOLD > s}
+            int m = 1 << (32-Integer.numberOfLeadingZeros(s/BigInteger.BURNIKEL_ZIEGLER_THRESHOLD));
+
+            int j = (s+m-1) / m;      // step 2a: j = ceil(s/m)
+            int n = j * m;            // step 2b: block length in 32-bit units
+            long n32 = 32L * n;         // block length in bits
+            int sigma = (int) Math.max(0, n32 - b.bitLength());   // step 3: sigma = max{T | (2^T)*B < beta^n}
+            MutableBigInteger bShifted = new MutableBigInteger(b);
+            bShifted.safeLeftShift(sigma);   // step 4a: shift b so its length is a multiple of n
+            MutableBigInteger aShifted = new MutableBigInteger (this);
+            aShifted.safeLeftShift(sigma);     // step 4b: shift a by the same amount
+
+            // step 5: t is the number of blocks needed to accommodate a plus one additional bit
+            int t = (int) ((aShifted.bitLength()+n32) / n32);
+            if (t < 2) {
+                t = 2;
+            }
+
+            // step 6: conceptually split a into blocks a[t-1], ..., a[0]
+            MutableBigInteger a1 = aShifted.getBlock(t-1, t, n);   // the most significant block of a
+
+            // step 7: z[t-2] = [a[t-1], a[t-2]]
+            MutableBigInteger z = aShifted.getBlock(t-2, t, n);    // the second to most significant block
+            z.addDisjoint(a1, n);   // z[t-2]
+
+            // do schoolbook division on blocks, dividing 2-block numbers by 1-block numbers
+            MutableBigInteger qi = new MutableBigInteger();
+            MutableBigInteger ri;
+            for (int i=t-2; i > 0; i--) {
+                // step 8a: compute (qi,ri) such that z=b*qi+ri
+                ri = z.divide2n1n(bShifted, qi);
+
+                // step 8b: z = [ri, a[i-1]]
+                z = aShifted.getBlock(i-1, t, n);   // a[i-1]
+                z.addDisjoint(ri, n);
+                quotient.addShifted(qi, i*n);   // update q (part of step 9)
+            }
+            // final iteration of step 8: do the loop one more time for i=0 but leave z unchanged
+            ri = z.divide2n1n(bShifted, qi);
+            quotient.add(qi);
+
+            ri.rightShift(sigma);   // step 9: a and b were shifted, so shift back
+            return ri;
+        }
+    }
+
+    /**
+     * This method implements algorithm 1 from pg. 4 of the Burnikel-Ziegler paper.
+     * It divides a 2n-digit number by a n-digit number.<br/>
+     * The parameter beta is 2<sup>32</sup> so all shifts are multiples of 32 bits.
+     * <br/>
+     * {@code this} must be a nonnegative number such that {@code this.bitLength() <= 2*b.bitLength()}
+     * @param b a positive number such that {@code b.bitLength()} is even
+     * @param quotient output parameter for {@code this/b}
+     * @return {@code this%b}
+     */
+    private MutableBigInteger divide2n1n(MutableBigInteger b, MutableBigInteger quotient) {
+        int n = b.intLen;
+
+        // step 1: base case
+        if (n%2 != 0 || n < BigInteger.BURNIKEL_ZIEGLER_THRESHOLD) {
+            return divideKnuth(b, quotient);
+        }
+
+        // step 2: view this as [a1,a2,a3,a4] where each ai is n/2 ints or less
+        MutableBigInteger aUpper = new MutableBigInteger(this);
+        aUpper.safeRightShift(32*(n/2));   // aUpper = [a1,a2,a3]
+        keepLower(n/2);   // this = a4
+
+        // step 3: q1=aUpper/b, r1=aUpper%b
+        MutableBigInteger q1 = new MutableBigInteger();
+        MutableBigInteger r1 = aUpper.divide3n2n(b, q1);
+
+        // step 4: quotient=[r1,this]/b, r2=[r1,this]%b
+        addDisjoint(r1, n/2);   // this = [r1,this]
+        MutableBigInteger r2 = divide3n2n(b, quotient);
+
+        // step 5: let quotient=[q1,quotient] and return r2
+        quotient.addDisjoint(q1, n/2);
+        return r2;
+    }
+
+    /**
+     * This method implements algorithm 2 from pg. 5 of the Burnikel-Ziegler paper.
+     * It divides a 3n-digit number by a 2n-digit number.<br/>
+     * The parameter beta is 2<sup>32</sup> so all shifts are multiples of 32 bits.<br/>
+     * <br/>
+     * {@code this} must be a nonnegative number such that {@code 2*this.bitLength() <= 3*b.bitLength()}
+     * @param quotient output parameter for {@code this/b}
+     * @return {@code this%b}
+     */
+    private MutableBigInteger divide3n2n(MutableBigInteger b, MutableBigInteger quotient) {
+        int n = b.intLen / 2;   // half the length of b in ints
+
+        // step 1: view this as [a1,a2,a3] where each ai is n ints or less; let a12=[a1,a2]
+        MutableBigInteger a12 = new MutableBigInteger(this);
+        a12.safeRightShift(32*n);
+
+        // step 2: view b as [b1,b2] where each bi is n ints or less
+        MutableBigInteger b1 = new MutableBigInteger(b);
+        b1.safeRightShift(n * 32);
+        BigInteger b2 = b.getLower(n);
+
+        MutableBigInteger r;
+        MutableBigInteger d;
+        if (compareShifted(b, n) < 0) {
+            // step 3a: if a1<b1, let quotient=a12/b1 and r=a12%b1
+            r = a12.divide2n1n(b1, quotient);
+
+            // step 4: d=quotient*b2
+            d = new MutableBigInteger(quotient.toBigInteger().multiply(b2));
+        } else {
+            // step 3b: if a1>=b1, let quotient=beta^n-1 and r=a12-b1*2^n+b1
+            quotient.ones(n);
+            a12.add(b1);
+            b1.leftShift(32*n);
+            a12.subtract(b1);
+            r = a12;
+
+            // step 4: d=quotient*b2=(b2 << 32*n) - b2
+            d = new MutableBigInteger(b2);
+            d.leftShift(32 * n);
+            d.subtract(new MutableBigInteger(b2));
+        }
+
+        // step 5: r = r*beta^n + a3 - d (paper says a4)
+        // However, don't subtract d until after the while loop so r doesn't become negative
+        r.leftShift(32 * n);
+        r.addLower(this, n);
+
+        // step 6: add b until r>=d
+        while (r.compare(d) < 0) {
+            r.add(b);
+            quotient.subtract(MutableBigInteger.ONE);
+        }
+        r.subtract(d);
+
+        return r;
+    }
+
+    /**
+     * Returns a {@code MutableBigInteger} containing {@code blockLength} ints from
+     * {@code this} number, starting at {@code index*blockLength}.<br/>
+     * Used by Burnikel-Ziegler division.
+     * @param index the block index
+     * @param numBlocks the total number of blocks in {@code this} number
+     * @param blockLength length of one block in units of 32 bits
+     * @return
+     */
+    private MutableBigInteger getBlock(int index, int numBlocks, int blockLength) {
+        int blockStart = index * blockLength;
+        if (blockStart >= intLen) {
+            return new MutableBigInteger();
+        }
+
+        int blockEnd;
+        if (index == numBlocks-1) {
+            blockEnd = intLen;
+        } else {
+            blockEnd = (index+1) * blockLength;
+        }
+        if (blockEnd > intLen) {
+            return new MutableBigInteger();
+        }
+
+        int[] newVal = Arrays.copyOfRange(value, offset+intLen-blockEnd, offset+intLen-blockStart);
+        return new MutableBigInteger(newVal);
+    }
+
+    /** @see BigInteger#bitLength() */
+    long bitLength() {
+        if (intLen == 0)
+            return 0;
+        return intLen*32L - Integer.numberOfLeadingZeros(value[offset]);
+    }
+
+    /**
+     * Internally used  to calculate the quotient of this div v and places the
+     * quotient in the provided MutableBigInteger object and the remainder is
+     * returned.
+     *
+     * @return the remainder of the division will be returned.
+     */
+    long divide(long v, MutableBigInteger quotient) {
+        if (v == 0)
+            throw new ArithmeticException("BigInteger divide by zero");
+
+        // Dividend is zero
+        if (intLen == 0) {
+            quotient.intLen = quotient.offset = 0;
+            return 0;
+        }
+        if (v < 0)
+            v = -v;
+
+        int d = (int)(v >>> 32);
+        quotient.clear();
+        // Special case on word divisor
+        if (d == 0)
+            return divideOneWord((int)v, quotient) & LONG_MASK;
+        else {
+            return divideLongMagnitude(v, quotient).toLong();
+        }
+    }
+
+    private static void copyAndShift(int[] src, int srcFrom, int srcLen, int[] dst, int dstFrom, int shift) {
+        int n2 = 32 - shift;
+        int c=src[srcFrom];
+        for (int i=0; i < srcLen-1; i++) {
+            int b = c;
+            c = src[++srcFrom];
+            dst[dstFrom+i] = (b << shift) | (c >>> n2);
+        }
+        dst[dstFrom+srcLen-1] = c << shift;
+    }
+
+    /**
+     * Divide this MutableBigInteger by the divisor.
+     * The quotient will be placed into the provided quotient object &
+     * the remainder object is returned.
+     */
+    private MutableBigInteger divideMagnitude(MutableBigInteger div,
+                                              MutableBigInteger quotient,
+                                              boolean needRemainder ) {
+        // assert div.intLen > 1
+        // D1 normalize the divisor
+        int shift = Integer.numberOfLeadingZeros(div.value[div.offset]);
+        // Copy divisor value to protect divisor
+        final int dlen = div.intLen;
+        int[] divisor;
+        MutableBigInteger rem; // Remainder starts as dividend with space for a leading zero
+        if (shift > 0) {
+            divisor = new int[dlen];
+            copyAndShift(div.value,div.offset,dlen,divisor,0,shift);
+            if (Integer.numberOfLeadingZeros(value[offset]) >= shift) {
+                int[] remarr = new int[intLen + 1];
+                rem = new MutableBigInteger(remarr);
+                rem.intLen = intLen;
+                rem.offset = 1;
+                copyAndShift(value,offset,intLen,remarr,1,shift);
+            } else {
+                int[] remarr = new int[intLen + 2];
+                rem = new MutableBigInteger(remarr);
+                rem.intLen = intLen+1;
+                rem.offset = 1;
+                int rFrom = offset;
+                int c=0;
+                int n2 = 32 - shift;
+                for (int i=1; i < intLen+1; i++,rFrom++) {
+                    int b = c;
+                    c = value[rFrom];
+                    remarr[i] = (b << shift) | (c >>> n2);
+                }
+                remarr[intLen+1] = c << shift;
+            }
+        } else {
+            divisor = Arrays.copyOfRange(div.value, div.offset, div.offset + div.intLen);
+            rem = new MutableBigInteger(new int[intLen + 1]);
+            System.arraycopy(value, offset, rem.value, 1, intLen);
+            rem.intLen = intLen;
+            rem.offset = 1;
+        }
+
+        int nlen = rem.intLen;
+
+        // Set the quotient size
+        final int limit = nlen - dlen + 1;
+        if (quotient.value.length < limit) {
+            quotient.value = new int[limit];
+            quotient.offset = 0;
+        }
+        quotient.intLen = limit;
+        int[] q = quotient.value;
+
+
+        // Must insert leading 0 in rem if its length did not change
+        if (rem.intLen == nlen) {
+            rem.offset = 0;
+            rem.value[0] = 0;
+            rem.intLen++;
+        }
+
+        int dh = divisor[0];
+        long dhLong = dh & LONG_MASK;
+        int dl = divisor[1];
+
+        // D2 Initialize j
+        for (int j=0; j < limit-1; j++) {
+            // D3 Calculate qhat
+            // estimate qhat
+            int qhat = 0;
+            int qrem = 0;
+            boolean skipCorrection = false;
+            int nh = rem.value[j+rem.offset];
+            int nh2 = nh + 0x80000000;
+            int nm = rem.value[j+1+rem.offset];
+
+            if (nh == dh) {
+                qhat = ~0;
+                qrem = nh + nm;
+                skipCorrection = qrem + 0x80000000 < nh2;
+            } else {
+                long nChunk = (((long)nh) << 32) | (nm & LONG_MASK);
+                if (nChunk >= 0) {
+                    qhat = (int) (nChunk / dhLong);
+                    qrem = (int) (nChunk - (qhat * dhLong));
+                } else {
+                    long tmp = divWord(nChunk, dh);
+                    qhat = (int) (tmp & LONG_MASK);
+                    qrem = (int) (tmp >>> 32);
+                }
+            }
+
+            if (qhat == 0)
+                continue;
+
+            if (!skipCorrection) { // Correct qhat
+                long nl = rem.value[j+2+rem.offset] & LONG_MASK;
+                long rs = ((qrem & LONG_MASK) << 32) | nl;
+                long estProduct = (dl & LONG_MASK) * (qhat & LONG_MASK);
+
+                if (unsignedLongCompare(estProduct, rs)) {
+                    qhat--;
+                    qrem = (int)((qrem & LONG_MASK) + dhLong);
+                    if ((qrem & LONG_MASK) >=  dhLong) {
+                        estProduct -= (dl & LONG_MASK);
+                        rs = ((qrem & LONG_MASK) << 32) | nl;
+                        if (unsignedLongCompare(estProduct, rs))
+                            qhat--;
+                    }
+                }
+            }
+
+            // D4 Multiply and subtract
+            rem.value[j+rem.offset] = 0;
+            int borrow = mulsub(rem.value, divisor, qhat, dlen, j+rem.offset);
+
+            // D5 Test remainder
+            if (borrow + 0x80000000 > nh2) {
+                // D6 Add back
+                divadd(divisor, rem.value, j+1+rem.offset);
+                qhat--;
+            }
+
+            // Store the quotient digit
+            q[j] = qhat;
+        } // D7 loop on j
+        // D3 Calculate qhat
+        // estimate qhat
+        int qhat = 0;
+        int qrem = 0;
+        boolean skipCorrection = false;
+        int nh = rem.value[limit - 1 + rem.offset];
+        int nh2 = nh + 0x80000000;
+        int nm = rem.value[limit + rem.offset];
+
+        if (nh == dh) {
+            qhat = ~0;
+            qrem = nh + nm;
+            skipCorrection = qrem + 0x80000000 < nh2;
+        } else {
+            long nChunk = (((long) nh) << 32) | (nm & LONG_MASK);
+            if (nChunk >= 0) {
+                qhat = (int) (nChunk / dhLong);
+                qrem = (int) (nChunk - (qhat * dhLong));
+            } else {
+                long tmp = divWord(nChunk, dh);
+                qhat = (int) (tmp & LONG_MASK);
+                qrem = (int) (tmp >>> 32);
+            }
+        }
+        if (qhat != 0) {
+            if (!skipCorrection) { // Correct qhat
+                long nl = rem.value[limit + 1 + rem.offset] & LONG_MASK;
+                long rs = ((qrem & LONG_MASK) << 32) | nl;
+                long estProduct = (dl & LONG_MASK) * (qhat & LONG_MASK);
+
+                if (unsignedLongCompare(estProduct, rs)) {
+                    qhat--;
+                    qrem = (int) ((qrem & LONG_MASK) + dhLong);
+                    if ((qrem & LONG_MASK) >= dhLong) {
+                        estProduct -= (dl & LONG_MASK);
+                        rs = ((qrem & LONG_MASK) << 32) | nl;
+                        if (unsignedLongCompare(estProduct, rs))
+                            qhat--;
+                    }
+                }
+            }
+
+
+            // D4 Multiply and subtract
+            int borrow;
+            rem.value[limit - 1 + rem.offset] = 0;
+            if(needRemainder)
+                borrow = mulsub(rem.value, divisor, qhat, dlen, limit - 1 + rem.offset);
+            else
+                borrow = mulsubBorrow(rem.value, divisor, qhat, dlen, limit - 1 + rem.offset);
+
+            // D5 Test remainder
+            if (borrow + 0x80000000 > nh2) {
+                // D6 Add back
+                if(needRemainder)
+                    divadd(divisor, rem.value, limit - 1 + 1 + rem.offset);
+                qhat--;
+            }
+
+            // Store the quotient digit
+            q[(limit - 1)] = qhat;
+        }
+
+
+        if (needRemainder) {
+            // D8 Unnormalize
+            if (shift > 0)
+                rem.rightShift(shift);
+            rem.normalize();
+        }
+        quotient.normalize();
+        return needRemainder ? rem : null;
+    }
+
+    /**
+     * Divide this MutableBigInteger by the divisor represented by positive long
+     * value. The quotient will be placed into the provided quotient object &
+     * the remainder object is returned.
+     */
+    private MutableBigInteger divideLongMagnitude(long ldivisor, MutableBigInteger quotient) {
+        // Remainder starts as dividend with space for a leading zero
+        MutableBigInteger rem = new MutableBigInteger(new int[intLen + 1]);
+        System.arraycopy(value, offset, rem.value, 1, intLen);
+        rem.intLen = intLen;
+        rem.offset = 1;
+
+        int nlen = rem.intLen;
+
+        int limit = nlen - 2 + 1;
+        if (quotient.value.length < limit) {
+            quotient.value = new int[limit];
+            quotient.offset = 0;
+        }
+        quotient.intLen = limit;
+        int[] q = quotient.value;
+
+        // D1 normalize the divisor
+        int shift = Long.numberOfLeadingZeros(ldivisor);
+        if (shift > 0) {
+            ldivisor<<=shift;
+            rem.leftShift(shift);
+        }
+
+        // Must insert leading 0 in rem if its length did not change
+        if (rem.intLen == nlen) {
+            rem.offset = 0;
+            rem.value[0] = 0;
+            rem.intLen++;
+        }
+
+        int dh = (int)(ldivisor >>> 32);
+        long dhLong = dh & LONG_MASK;
+        int dl = (int)(ldivisor & LONG_MASK);
+
+        // D2 Initialize j
+        for (int j = 0; j < limit; j++) {
+            // D3 Calculate qhat
+            // estimate qhat
+            int qhat = 0;
+            int qrem = 0;
+            boolean skipCorrection = false;
+            int nh = rem.value[j + rem.offset];
+            int nh2 = nh + 0x80000000;
+            int nm = rem.value[j + 1 + rem.offset];
+
+            if (nh == dh) {
+                qhat = ~0;
+                qrem = nh + nm;
+                skipCorrection = qrem + 0x80000000 < nh2;
+            } else {
+                long nChunk = (((long) nh) << 32) | (nm & LONG_MASK);
+                if (nChunk >= 0) {
+                    qhat = (int) (nChunk / dhLong);
+                    qrem = (int) (nChunk - (qhat * dhLong));
+                } else {
+                    long tmp = divWord(nChunk, dh);
+                    qhat =(int)(tmp & LONG_MASK);
+                    qrem = (int)(tmp>>>32);
+                }
+            }
+
+            if (qhat == 0)
+                continue;
+
+            if (!skipCorrection) { // Correct qhat
+                long nl = rem.value[j + 2 + rem.offset] & LONG_MASK;
+                long rs = ((qrem & LONG_MASK) << 32) | nl;
+                long estProduct = (dl & LONG_MASK) * (qhat & LONG_MASK);
+
+                if (unsignedLongCompare(estProduct, rs)) {
+                    qhat--;
+                    qrem = (int) ((qrem & LONG_MASK) + dhLong);
+                    if ((qrem & LONG_MASK) >= dhLong) {
+                        estProduct -= (dl & LONG_MASK);
+                        rs = ((qrem & LONG_MASK) << 32) | nl;
+                        if (unsignedLongCompare(estProduct, rs))
+                            qhat--;
+                    }
+                }
+            }
+
+            // D4 Multiply and subtract
+            rem.value[j + rem.offset] = 0;
+            int borrow = mulsubLong(rem.value, dh, dl, qhat,  j + rem.offset);
+
+            // D5 Test remainder
+            if (borrow + 0x80000000 > nh2) {
+                // D6 Add back
+                divaddLong(dh,dl, rem.value, j + 1 + rem.offset);
+                qhat--;
+            }
+
+            // Store the quotient digit
+            q[j] = qhat;
+        } // D7 loop on j
+
+        // D8 Unnormalize
+        if (shift > 0)
+            rem.rightShift(shift);
+
+        quotient.normalize();
+        rem.normalize();
+        return rem;
+    }
+
+    /**
+     * A primitive used for division by long.
+     * Specialized version of the method divadd.
+     * dh is a high part of the divisor, dl is a low part
+     */
+    private int divaddLong(int dh, int dl, int[] result, int offset) {
+        long carry = 0;
+
+        long sum = (dl & LONG_MASK) + (result[1+offset] & LONG_MASK);
+        result[1+offset] = (int)sum;
+
+        sum = (dh & LONG_MASK) + (result[offset] & LONG_MASK) + carry;
+        result[offset] = (int)sum;
+        carry = sum >>> 32;
+        return (int)carry;
+    }
+
+    /**
+     * This method is used for division by long.
+     * Specialized version of the method sulsub.
+     * dh is a high part of the divisor, dl is a low part
+     */
+    private int mulsubLong(int[] q, int dh, int dl, int x, int offset) {
+        long xLong = x & LONG_MASK;
+        offset += 2;
+        long product = (dl & LONG_MASK) * xLong;
+        long difference = q[offset] - product;
+        q[offset--] = (int)difference;
+        long carry = (product >>> 32)
+                 + (((difference & LONG_MASK) >
+                     (((~(int)product) & LONG_MASK))) ? 1:0);
+        product = (dh & LONG_MASK) * xLong + carry;
+        difference = q[offset] - product;
+        q[offset--] = (int)difference;
+        carry = (product >>> 32)
+                 + (((difference & LONG_MASK) >
+                     (((~(int)product) & LONG_MASK))) ? 1:0);
+        return (int)carry;
+    }
+
+    /**
+     * Compare two longs as if they were unsigned.
+     * Returns true iff one is bigger than two.
+     */
+    private boolean unsignedLongCompare(long one, long two) {
+        return (one+Long.MIN_VALUE) > (two+Long.MIN_VALUE);
+    }
+
+    /**
+     * This method divides a long quantity by an int to estimate
+     * qhat for two multi precision numbers. It is used when
+     * the signed value of n is less than zero.
+     * Returns long value where high 32 bits contain remainder value and
+     * low 32 bits contain quotient value.
+     */
+    static long divWord(long n, int d) {
+        long dLong = d & LONG_MASK;
+        long r;
+        long q;
+        if (dLong == 1) {
+            q = (int)n;
+            r = 0;
+            return (r << 32) | (q & LONG_MASK);
+        }
+
+        // Approximate the quotient and remainder
+        q = (n >>> 1) / (dLong >>> 1);
+        r = n - q*dLong;
+
+        // Correct the approximation
+        while (r < 0) {
+            r += dLong;
+            q--;
+        }
+        while (r >= dLong) {
+            r -= dLong;
+            q++;
+        }
+        // n - q*dlong == r && 0 <= r <dLong, hence we're done.
+        return (r << 32) | (q & LONG_MASK);
+    }
+
+    /**
+     * Calculate GCD of this and b. This and b are changed by the computation.
+     */
+    MutableBigInteger hybridGCD(MutableBigInteger b) {
+        // Use Euclid's algorithm until the numbers are approximately the
+        // same length, then use the binary GCD algorithm to find the GCD.
+        MutableBigInteger a = this;
+        MutableBigInteger q = new MutableBigInteger();
+
+        while (b.intLen != 0) {
+            if (Math.abs(a.intLen - b.intLen) < 2)
+                return a.binaryGCD(b);
+
+            MutableBigInteger r = a.divide(b, q);
+            a = b;
+            b = r;
+        }
+        return a;
+    }
+
+    /**
+     * Calculate GCD of this and v.
+     * Assumes that this and v are not zero.
+     */
+    private MutableBigInteger binaryGCD(MutableBigInteger v) {
+        // Algorithm B from Knuth section 4.5.2
+        MutableBigInteger u = this;
+        MutableBigInteger r = new MutableBigInteger();
+
+        // step B1
+        int s1 = u.getLowestSetBit();
+        int s2 = v.getLowestSetBit();
+        int k = (s1 < s2) ? s1 : s2;
+        if (k != 0) {
+            u.rightShift(k);
+            v.rightShift(k);
+        }
+
+        // step B2
+        boolean uOdd = (k == s1);
+        MutableBigInteger t = uOdd ? v: u;
+        int tsign = uOdd ? -1 : 1;
+
+        int lb;
+        while ((lb = t.getLowestSetBit()) >= 0) {
+            // steps B3 and B4
+            t.rightShift(lb);
+            // step B5
+            if (tsign > 0)
+                u = t;
+            else
+                v = t;
+
+            // Special case one word numbers
+            if (u.intLen < 2 && v.intLen < 2) {
+                int x = u.value[u.offset];
+                int y = v.value[v.offset];
+                x  = binaryGcd(x, y);
+                r.value[0] = x;
+                r.intLen = 1;
+                r.offset = 0;
+                if (k > 0)
+                    r.leftShift(k);
+                return r;
+            }
+
+            // step B6
+            if ((tsign = u.difference(v)) == 0)
+                break;
+            t = (tsign >= 0) ? u : v;
+        }
+
+        if (k > 0)
+            u.leftShift(k);
+        return u;
+    }
+
+    /**
+     * Calculate GCD of a and b interpreted as unsigned integers.
+     */
+    static int binaryGcd(int a, int b) {
+        if (b == 0)
+            return a;
+        if (a == 0)
+            return b;
+
+        // Right shift a & b till their last bits equal to 1.
+        int aZeros = Integer.numberOfTrailingZeros(a);
+        int bZeros = Integer.numberOfTrailingZeros(b);
+        a >>>= aZeros;
+        b >>>= bZeros;
+
+        int t = (aZeros < bZeros ? aZeros : bZeros);
+
+        while (a != b) {
+            if ((a+0x80000000) > (b+0x80000000)) {  // a > b as unsigned
+                a -= b;
+                a >>>= Integer.numberOfTrailingZeros(a);
+            } else {
+                b -= a;
+                b >>>= Integer.numberOfTrailingZeros(b);
+            }
+        }
+        return a<<t;
+    }
+
+    /**
+     * Returns the modInverse of this mod p. This and p are not affected by
+     * the operation.
+     */
+    MutableBigInteger mutableModInverse(MutableBigInteger p) {
+        // Modulus is odd, use Schroeppel's algorithm
+        if (p.isOdd())
+            return modInverse(p);
+
+        // Base and modulus are even, throw exception
+        if (isEven())
+            throw new ArithmeticException("BigInteger not invertible.");
+
+        // Get even part of modulus expressed as a power of 2
+        int powersOf2 = p.getLowestSetBit();
+
+        // Construct odd part of modulus
+        MutableBigInteger oddMod = new MutableBigInteger(p);
+        oddMod.rightShift(powersOf2);
+
+        if (oddMod.isOne())
+            return modInverseMP2(powersOf2);
+
+        // Calculate 1/a mod oddMod
+        MutableBigInteger oddPart = modInverse(oddMod);
+
+        // Calculate 1/a mod evenMod
+        MutableBigInteger evenPart = modInverseMP2(powersOf2);
+
+        // Combine the results using Chinese Remainder Theorem
+        MutableBigInteger y1 = modInverseBP2(oddMod, powersOf2);
+        MutableBigInteger y2 = oddMod.modInverseMP2(powersOf2);
+
+        MutableBigInteger temp1 = new MutableBigInteger();
+        MutableBigInteger temp2 = new MutableBigInteger();
+        MutableBigInteger result = new MutableBigInteger();
+
+        oddPart.leftShift(powersOf2);
+        oddPart.multiply(y1, result);
+
+        evenPart.multiply(oddMod, temp1);
+        temp1.multiply(y2, temp2);
+
+        result.add(temp2);
+        return result.divide(p, temp1);
+    }
+
+    /*
+     * Calculate the multiplicative inverse of this mod 2^k.
+     */
+    MutableBigInteger modInverseMP2(int k) {
+        if (isEven())
+            throw new ArithmeticException("Non-invertible. (GCD != 1)");
+
+        if (k > 64)
+            return euclidModInverse(k);
+
+        int t = inverseMod32(value[offset+intLen-1]);
+
+        if (k < 33) {
+            t = (k == 32 ? t : t & ((1 << k) - 1));
+            return new MutableBigInteger(t);
+        }
+
+        long pLong = (value[offset+intLen-1] & LONG_MASK);
+        if (intLen > 1)
+            pLong |=  ((long)value[offset+intLen-2] << 32);
+        long tLong = t & LONG_MASK;
+        tLong = tLong * (2 - pLong * tLong);  // 1 more Newton iter step
+        tLong = (k == 64 ? tLong : tLong & ((1L << k) - 1));
+
+        MutableBigInteger result = new MutableBigInteger(new int[2]);
+        result.value[0] = (int)(tLong >>> 32);
+        result.value[1] = (int)tLong;
+        result.intLen = 2;
+        result.normalize();
+        return result;
+    }
+
+    /**
+     * Returns the multiplicative inverse of val mod 2^32.  Assumes val is odd.
+     */
+    static int inverseMod32(int val) {
+        // Newton's iteration!
+        int t = val;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        return t;
+    }
+
+    /**
+     * Returns the multiplicative inverse of val mod 2^64.  Assumes val is odd.
+     */
+    static long inverseMod64(long val) {
+        // Newton's iteration!
+        long t = val;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        t *= 2 - val*t;
+        assert(t * val == 1);
+        return t;
+    }
+
+    /**
+     * Calculate the multiplicative inverse of 2^k mod mod, where mod is odd.
+     */
+    static MutableBigInteger modInverseBP2(MutableBigInteger mod, int k) {
+        // Copy the mod to protect original
+        return fixup(new MutableBigInteger(1), new MutableBigInteger(mod), k);
+    }
+
+    /**
+     * Calculate the multiplicative inverse of this modulo mod, where the mod
+     * argument is odd.  This and mod are not changed by the calculation.
+     *
+     * This method implements an algorithm due to Richard Schroeppel, that uses
+     * the same intermediate representation as Montgomery Reduction
+     * ("Montgomery Form").  The algorithm is described in an unpublished
+     * manuscript entitled "Fast Modular Reciprocals."
+     */
+    private MutableBigInteger modInverse(MutableBigInteger mod) {
+        MutableBigInteger p = new MutableBigInteger(mod);
+        MutableBigInteger f = new MutableBigInteger(this);
+        MutableBigInteger g = new MutableBigInteger(p);
+        SignedMutableBigInteger c = new SignedMutableBigInteger(1);
+        SignedMutableBigInteger d = new SignedMutableBigInteger();
+        MutableBigInteger temp = null;
+        SignedMutableBigInteger sTemp = null;
+
+        int k = 0;
+        // Right shift f k times until odd, left shift d k times
+        if (f.isEven()) {
+            int trailingZeros = f.getLowestSetBit();
+            f.rightShift(trailingZeros);
+            d.leftShift(trailingZeros);
+            k = trailingZeros;
+        }
+
+        // The Almost Inverse Algorithm
+        while (!f.isOne()) {
+            // If gcd(f, g) != 1, number is not invertible modulo mod
+            if (f.isZero())
+                throw new ArithmeticException("BigInteger not invertible.");
+
+            // If f < g exchange f, g and c, d
+            if (f.compare(g) < 0) {
+                temp = f; f = g; g = temp;
+                sTemp = d; d = c; c = sTemp;
+            }
+
+            // If f == g (mod 4)
+            if (((f.value[f.offset + f.intLen - 1] ^
+                 g.value[g.offset + g.intLen - 1]) & 3) == 0) {
+                f.subtract(g);
+                c.signedSubtract(d);
+            } else { // If f != g (mod 4)
+                f.add(g);
+                c.signedAdd(d);
+            }
+
+            // Right shift f k times until odd, left shift d k times
+            int trailingZeros = f.getLowestSetBit();
+            f.rightShift(trailingZeros);
+            d.leftShift(trailingZeros);
+            k += trailingZeros;
+        }
+
+        if (c.compare(p) >= 0) { // c has a larger magnitude than p
+            MutableBigInteger remainder = c.divide(p,
+                new MutableBigInteger());
+            // The previous line ignores the sign so we copy the data back
+            // into c which will restore the sign as needed (and converts
+            // it back to a SignedMutableBigInteger)
+            c.copyValue(remainder);
+        }
+
+        if (c.sign < 0) {
+            c.signedAdd(p);
+        }
+
+        return fixup(c, p, k);
+    }
+
+    /**
+     * The Fixup Algorithm
+     * Calculates X such that X = C * 2^(-k) (mod P)
+     * Assumes C<P and P is odd.
+     */
+    static MutableBigInteger fixup(MutableBigInteger c, MutableBigInteger p,
+                                                                      int k) {
+        MutableBigInteger temp = new MutableBigInteger();
+        // Set r to the multiplicative inverse of p mod 2^32
+        int r = -inverseMod32(p.value[p.offset+p.intLen-1]);
+
+        for (int i=0, numWords = k >> 5; i < numWords; i++) {
+            // V = R * c (mod 2^j)
+            int  v = r * c.value[c.offset + c.intLen-1];
+            // c = c + (v * p)
+            p.mul(v, temp);
+            c.add(temp);
+            // c = c / 2^j
+            c.intLen--;
+        }
+        int numBits = k & 0x1f;
+        if (numBits != 0) {
+            // V = R * c (mod 2^j)
+            int v = r * c.value[c.offset + c.intLen-1];
+            v &= ((1<<numBits) - 1);
+            // c = c + (v * p)
+            p.mul(v, temp);
+            c.add(temp);
+            // c = c / 2^j
+            c.rightShift(numBits);
+        }
+
+        // In theory, c may be greater than p at this point (Very rare!)
+        if (c.compare(p) >= 0)
+            c = c.divide(p, new MutableBigInteger());
+
+        return c;
+    }
+
+    /**
+     * Uses the extended Euclidean algorithm to compute the modInverse of base
+     * mod a modulus that is a power of 2. The modulus is 2^k.
+     */
+    MutableBigInteger euclidModInverse(int k) {
+        MutableBigInteger b = new MutableBigInteger(1);
+        b.leftShift(k);
+        MutableBigInteger mod = new MutableBigInteger(b);
+
+        MutableBigInteger a = new MutableBigInteger(this);
+        MutableBigInteger q = new MutableBigInteger();
+        MutableBigInteger r = b.divide(a, q);
+
+        MutableBigInteger swapper = b;
+        // swap b & r
+        b = r;
+        r = swapper;
+
+        MutableBigInteger t1 = new MutableBigInteger(q);
+        MutableBigInteger t0 = new MutableBigInteger(1);
+        MutableBigInteger temp = new MutableBigInteger();
+
+        while (!b.isOne()) {
+            r = a.divide(b, q);
+
+            if (r.intLen == 0)
+                throw new ArithmeticException("BigInteger not invertible.");
+
+            swapper = r;
+            a = swapper;
+
+            if (q.intLen == 1)
+                t1.mul(q.value[q.offset], temp);
+            else
+                q.multiply(t1, temp);
+            swapper = q;
+            q = temp;
+            temp = swapper;
+            t0.add(q);
+
+            if (a.isOne())
+                return t0;
+
+            r = b.divide(a, q);
+
+            if (r.intLen == 0)
+                throw new ArithmeticException("BigInteger not invertible.");
+
+            swapper = b;
+            b =  r;
+
+            if (q.intLen == 1)
+                t0.mul(q.value[q.offset], temp);
+            else
+                q.multiply(t0, temp);
+            swapper = q; q = temp; temp = swapper;
+
+            t1.add(q);
+        }
+        mod.subtract(t1);
+        return mod;
+    }
+}
diff --git a/ojluni/src/main/java/java/math/RoundingMode.java b/ojluni/src/main/java/java/math/RoundingMode.java
new file mode 100644
index 0000000..3a4fe97
--- /dev/null
+++ b/ojluni/src/main/java/java/math/RoundingMode.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Portions Copyright IBM Corporation, 2001. All Rights Reserved.
+ */
+package java.math;
+
+/**
+ * Specifies a <i>rounding behavior</i> for numerical operations
+ * capable of discarding precision. Each rounding mode indicates how
+ * the least significant returned digit of a rounded result is to be
+ * calculated.  If fewer digits are returned than the digits needed to
+ * represent the exact numerical result, the discarded digits will be
+ * referred to as the <i>discarded fraction</i> regardless the digits'
+ * contribution to the value of the number.  In other words,
+ * considered as a numerical value, the discarded fraction could have
+ * an absolute value greater than one.
+ *
+ * <p>Each rounding mode description includes a table listing how
+ * different two-digit decimal values would round to a one digit
+ * decimal value under the rounding mode in question.  The result
+ * column in the tables could be gotten by creating a
+ * {@code BigDecimal} number with the specified value, forming a
+ * {@link MathContext} object with the proper settings
+ * ({@code precision} set to {@code 1}, and the
+ * {@code roundingMode} set to the rounding mode in question), and
+ * calling {@link BigDecimal#round round} on this number with the
+ * proper {@code MathContext}.  A summary table showing the results
+ * of these rounding operations for all rounding modes appears below.
+ *
+ *<table border>
+ * <caption><b>Summary of Rounding Operations Under Different Rounding Modes</b></caption>
+ * <tr><th></th><th colspan=8>Result of rounding input to one digit with the given
+ *                           rounding mode</th>
+ * <tr valign=top>
+ * <th>Input Number</th>         <th>{@code UP}</th>
+ *                                           <th>{@code DOWN}</th>
+ *                                                        <th>{@code CEILING}</th>
+ *                                                                       <th>{@code FLOOR}</th>
+ *                                                                                    <th>{@code HALF_UP}</th>
+ *                                                                                                   <th>{@code HALF_DOWN}</th>
+ *                                                                                                                    <th>{@code HALF_EVEN}</th>
+ *                                                                                                                                     <th>{@code UNNECESSARY}</th>
+ *
+ * <tr align=right><td>5.5</td>  <td>6</td>  <td>5</td>    <td>6</td>    <td>5</td>  <td>6</td>      <td>5</td>       <td>6</td>       <td>throw {@code ArithmeticException}</td>
+ * <tr align=right><td>2.5</td>  <td>3</td>  <td>2</td>    <td>3</td>    <td>2</td>  <td>3</td>      <td>2</td>       <td>2</td>       <td>throw {@code ArithmeticException}</td>
+ * <tr align=right><td>1.6</td>  <td>2</td>  <td>1</td>    <td>2</td>    <td>1</td>  <td>2</td>      <td>2</td>       <td>2</td>       <td>throw {@code ArithmeticException}</td>
+ * <tr align=right><td>1.1</td>  <td>2</td>  <td>1</td>    <td>2</td>    <td>1</td>  <td>1</td>      <td>1</td>       <td>1</td>       <td>throw {@code ArithmeticException}</td>
+ * <tr align=right><td>1.0</td>  <td>1</td>  <td>1</td>    <td>1</td>    <td>1</td>  <td>1</td>      <td>1</td>       <td>1</td>       <td>1</td>
+ * <tr align=right><td>-1.0</td> <td>-1</td> <td>-1</td>   <td>-1</td>   <td>-1</td> <td>-1</td>     <td>-1</td>      <td>-1</td>      <td>-1</td>
+ * <tr align=right><td>-1.1</td> <td>-2</td> <td>-1</td>   <td>-1</td>   <td>-2</td> <td>-1</td>     <td>-1</td>      <td>-1</td>      <td>throw {@code ArithmeticException}</td>
+ * <tr align=right><td>-1.6</td> <td>-2</td> <td>-1</td>   <td>-1</td>   <td>-2</td> <td>-2</td>     <td>-2</td>      <td>-2</td>      <td>throw {@code ArithmeticException}</td>
+ * <tr align=right><td>-2.5</td> <td>-3</td> <td>-2</td>   <td>-2</td>   <td>-3</td> <td>-3</td>     <td>-2</td>      <td>-2</td>      <td>throw {@code ArithmeticException}</td>
+ * <tr align=right><td>-5.5</td> <td>-6</td> <td>-5</td>   <td>-5</td>   <td>-6</td> <td>-6</td>     <td>-5</td>      <td>-6</td>      <td>throw {@code ArithmeticException}</td>
+ *</table>
+ *
+ *
+ * <p>This {@code enum} is intended to replace the integer-based
+ * enumeration of rounding mode constants in {@link BigDecimal}
+ * ({@link BigDecimal#ROUND_UP}, {@link BigDecimal#ROUND_DOWN},
+ * etc. ).
+ *
+ * @see     BigDecimal
+ * @see     MathContext
+ * @author  Josh Bloch
+ * @author  Mike Cowlishaw
+ * @author  Joseph D. Darcy
+ * @since 1.5
+ */
+public enum RoundingMode {
+
+        /**
+         * Rounding mode to round away from zero.  Always increments the
+         * digit prior to a non-zero discarded fraction.  Note that this
+         * rounding mode never decreases the magnitude of the calculated
+         * value.
+         *
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode UP Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code UP} rounding
+         *<tr align=right><td>5.5</td>  <td>6</td>
+         *<tr align=right><td>2.5</td>  <td>3</td>
+         *<tr align=right><td>1.6</td>  <td>2</td>
+         *<tr align=right><td>1.1</td>  <td>2</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>-2</td>
+         *<tr align=right><td>-1.6</td> <td>-2</td>
+         *<tr align=right><td>-2.5</td> <td>-3</td>
+         *<tr align=right><td>-5.5</td> <td>-6</td>
+         *</table>
+         */
+    UP(BigDecimal.ROUND_UP),
+
+        /**
+         * Rounding mode to round towards zero.  Never increments the digit
+         * prior to a discarded fraction (i.e., truncates).  Note that this
+         * rounding mode never increases the magnitude of the calculated value.
+         *
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode DOWN Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code DOWN} rounding
+         *<tr align=right><td>5.5</td>  <td>5</td>
+         *<tr align=right><td>2.5</td>  <td>2</td>
+         *<tr align=right><td>1.6</td>  <td>1</td>
+         *<tr align=right><td>1.1</td>  <td>1</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>-1</td>
+         *<tr align=right><td>-1.6</td> <td>-1</td>
+         *<tr align=right><td>-2.5</td> <td>-2</td>
+         *<tr align=right><td>-5.5</td> <td>-5</td>
+         *</table>
+         */
+    DOWN(BigDecimal.ROUND_DOWN),
+
+        /**
+         * Rounding mode to round towards positive infinity.  If the
+         * result is positive, behaves as for {@code RoundingMode.UP};
+         * if negative, behaves as for {@code RoundingMode.DOWN}.  Note
+         * that this rounding mode never decreases the calculated value.
+         *
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode CEILING Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code CEILING} rounding
+         *<tr align=right><td>5.5</td>  <td>6</td>
+         *<tr align=right><td>2.5</td>  <td>3</td>
+         *<tr align=right><td>1.6</td>  <td>2</td>
+         *<tr align=right><td>1.1</td>  <td>2</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>-1</td>
+         *<tr align=right><td>-1.6</td> <td>-1</td>
+         *<tr align=right><td>-2.5</td> <td>-2</td>
+         *<tr align=right><td>-5.5</td> <td>-5</td>
+         *</table>
+         */
+    CEILING(BigDecimal.ROUND_CEILING),
+
+        /**
+         * Rounding mode to round towards negative infinity.  If the
+         * result is positive, behave as for {@code RoundingMode.DOWN};
+         * if negative, behave as for {@code RoundingMode.UP}.  Note that
+         * this rounding mode never increases the calculated value.
+         *
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode FLOOR Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code FLOOR} rounding
+         *<tr align=right><td>5.5</td>  <td>5</td>
+         *<tr align=right><td>2.5</td>  <td>2</td>
+         *<tr align=right><td>1.6</td>  <td>1</td>
+         *<tr align=right><td>1.1</td>  <td>1</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>-2</td>
+         *<tr align=right><td>-1.6</td> <td>-2</td>
+         *<tr align=right><td>-2.5</td> <td>-3</td>
+         *<tr align=right><td>-5.5</td> <td>-6</td>
+         *</table>
+         */
+    FLOOR(BigDecimal.ROUND_FLOOR),
+
+        /**
+         * Rounding mode to round towards {@literal "nearest neighbor"}
+         * unless both neighbors are equidistant, in which case round up.
+         * Behaves as for {@code RoundingMode.UP} if the discarded
+         * fraction is &ge; 0.5; otherwise, behaves as for
+         * {@code RoundingMode.DOWN}.  Note that this is the rounding
+         * mode commonly taught at school.
+         *
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode HALF_UP Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code HALF_UP} rounding
+         *<tr align=right><td>5.5</td>  <td>6</td>
+         *<tr align=right><td>2.5</td>  <td>3</td>
+         *<tr align=right><td>1.6</td>  <td>2</td>
+         *<tr align=right><td>1.1</td>  <td>1</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>-1</td>
+         *<tr align=right><td>-1.6</td> <td>-2</td>
+         *<tr align=right><td>-2.5</td> <td>-3</td>
+         *<tr align=right><td>-5.5</td> <td>-6</td>
+         *</table>
+         */
+    HALF_UP(BigDecimal.ROUND_HALF_UP),
+
+        /**
+         * Rounding mode to round towards {@literal "nearest neighbor"}
+         * unless both neighbors are equidistant, in which case round
+         * down.  Behaves as for {@code RoundingMode.UP} if the discarded
+         * fraction is &gt; 0.5; otherwise, behaves as for
+         * {@code RoundingMode.DOWN}.
+         *
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode HALF_DOWN Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code HALF_DOWN} rounding
+         *<tr align=right><td>5.5</td>  <td>5</td>
+         *<tr align=right><td>2.5</td>  <td>2</td>
+         *<tr align=right><td>1.6</td>  <td>2</td>
+         *<tr align=right><td>1.1</td>  <td>1</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>-1</td>
+         *<tr align=right><td>-1.6</td> <td>-2</td>
+         *<tr align=right><td>-2.5</td> <td>-2</td>
+         *<tr align=right><td>-5.5</td> <td>-5</td>
+         *</table>
+         */
+    HALF_DOWN(BigDecimal.ROUND_HALF_DOWN),
+
+        /**
+         * Rounding mode to round towards the {@literal "nearest neighbor"}
+         * unless both neighbors are equidistant, in which case, round
+         * towards the even neighbor.  Behaves as for
+         * {@code RoundingMode.HALF_UP} if the digit to the left of the
+         * discarded fraction is odd; behaves as for
+         * {@code RoundingMode.HALF_DOWN} if it's even.  Note that this
+         * is the rounding mode that statistically minimizes cumulative
+         * error when applied repeatedly over a sequence of calculations.
+         * It is sometimes known as {@literal "Banker's rounding,"} and is
+         * chiefly used in the USA.  This rounding mode is analogous to
+         * the rounding policy used for {@code float} and {@code double}
+         * arithmetic in Java.
+         *
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode HALF_EVEN Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code HALF_EVEN} rounding
+         *<tr align=right><td>5.5</td>  <td>6</td>
+         *<tr align=right><td>2.5</td>  <td>2</td>
+         *<tr align=right><td>1.6</td>  <td>2</td>
+         *<tr align=right><td>1.1</td>  <td>1</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>-1</td>
+         *<tr align=right><td>-1.6</td> <td>-2</td>
+         *<tr align=right><td>-2.5</td> <td>-2</td>
+         *<tr align=right><td>-5.5</td> <td>-6</td>
+         *</table>
+         */
+    HALF_EVEN(BigDecimal.ROUND_HALF_EVEN),
+
+        /**
+         * Rounding mode to assert that the requested operation has an exact
+         * result, hence no rounding is necessary.  If this rounding mode is
+         * specified on an operation that yields an inexact result, an
+         * {@code ArithmeticException} is thrown.
+         *<p>Example:
+         *<table border>
+         * <caption><b>Rounding mode UNNECESSARY Examples</b></caption>
+         *<tr valign=top><th>Input Number</th>
+         *    <th>Input rounded to one digit<br> with {@code UNNECESSARY} rounding
+         *<tr align=right><td>5.5</td>  <td>throw {@code ArithmeticException}</td>
+         *<tr align=right><td>2.5</td>  <td>throw {@code ArithmeticException}</td>
+         *<tr align=right><td>1.6</td>  <td>throw {@code ArithmeticException}</td>
+         *<tr align=right><td>1.1</td>  <td>throw {@code ArithmeticException}</td>
+         *<tr align=right><td>1.0</td>  <td>1</td>
+         *<tr align=right><td>-1.0</td> <td>-1</td>
+         *<tr align=right><td>-1.1</td> <td>throw {@code ArithmeticException}</td>
+         *<tr align=right><td>-1.6</td> <td>throw {@code ArithmeticException}</td>
+         *<tr align=right><td>-2.5</td> <td>throw {@code ArithmeticException}</td>
+         *<tr align=right><td>-5.5</td> <td>throw {@code ArithmeticException}</td>
+         *</table>
+         */
+    UNNECESSARY(BigDecimal.ROUND_UNNECESSARY);
+
+    // Corresponding BigDecimal rounding constant
+    final int oldMode;
+
+    /**
+     * Constructor
+     *
+     * @param oldMode The {@code BigDecimal} constant corresponding to
+     *        this mode
+     */
+    private RoundingMode(int oldMode) {
+        this.oldMode = oldMode;
+    }
+
+    /**
+     * Returns the {@code RoundingMode} object corresponding to a
+     * legacy integer rounding mode constant in {@link BigDecimal}.
+     *
+     * @param  rm legacy integer rounding mode to convert
+     * @return {@code RoundingMode} corresponding to the given integer.
+     * @throws IllegalArgumentException integer is out of range
+     */
+    public static RoundingMode valueOf(int rm) {
+        switch(rm) {
+
+        case BigDecimal.ROUND_UP:
+            return UP;
+
+        case BigDecimal.ROUND_DOWN:
+            return DOWN;
+
+        case BigDecimal.ROUND_CEILING:
+            return CEILING;
+
+        case BigDecimal.ROUND_FLOOR:
+            return FLOOR;
+
+        case BigDecimal.ROUND_HALF_UP:
+            return HALF_UP;
+
+        case BigDecimal.ROUND_HALF_DOWN:
+            return HALF_DOWN;
+
+        case BigDecimal.ROUND_HALF_EVEN:
+            return HALF_EVEN;
+
+        case BigDecimal.ROUND_UNNECESSARY:
+            return UNNECESSARY;
+
+        default:
+            throw new IllegalArgumentException("argument out of range");
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/math/SignedMutableBigInteger.java b/ojluni/src/main/java/java/math/SignedMutableBigInteger.java
new file mode 100644
index 0000000..a6e5fcd
--- /dev/null
+++ b/ojluni/src/main/java/java/math/SignedMutableBigInteger.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.math;
+
+/**
+ * A class used to represent multiprecision integers that makes efficient
+ * use of allocated space by allowing a number to occupy only part of
+ * an array so that the arrays do not have to be reallocated as often.
+ * When performing an operation with many iterations the array used to
+ * hold a number is only increased when necessary and does not have to
+ * be the same size as the number it represents. A mutable number allows
+ * calculations to occur on the same number without having to create
+ * a new number for every step of the calculation as occurs with
+ * BigIntegers.
+ *
+ * Note that SignedMutableBigIntegers only support signed addition and
+ * subtraction. All other operations occur as with MutableBigIntegers.
+ *
+ * @see     BigInteger
+ * @author  Michael McCloskey
+ * @since   1.3
+ */
+
+class SignedMutableBigInteger extends MutableBigInteger {
+
+   /**
+     * The sign of this MutableBigInteger.
+     */
+    int sign = 1;
+
+    // Constructors
+
+    /**
+     * The default constructor. An empty MutableBigInteger is created with
+     * a one word capacity.
+     */
+    SignedMutableBigInteger() {
+        super();
+    }
+
+    /**
+     * Construct a new MutableBigInteger with a magnitude specified by
+     * the int val.
+     */
+    SignedMutableBigInteger(int val) {
+        super(val);
+    }
+
+    /**
+     * Construct a new MutableBigInteger with a magnitude equal to the
+     * specified MutableBigInteger.
+     */
+    SignedMutableBigInteger(MutableBigInteger val) {
+        super(val);
+    }
+
+   // Arithmetic Operations
+
+   /**
+     * Signed addition built upon unsigned add and subtract.
+     */
+    void signedAdd(SignedMutableBigInteger addend) {
+        if (sign == addend.sign)
+            add(addend);
+        else
+            sign = sign * subtract(addend);
+
+    }
+
+   /**
+     * Signed addition built upon unsigned add and subtract.
+     */
+    void signedAdd(MutableBigInteger addend) {
+        if (sign == 1)
+            add(addend);
+        else
+            sign = sign * subtract(addend);
+
+    }
+
+   /**
+     * Signed subtraction built upon unsigned add and subtract.
+     */
+    void signedSubtract(SignedMutableBigInteger addend) {
+        if (sign == addend.sign)
+            sign = sign * subtract(addend);
+        else
+            add(addend);
+
+    }
+
+   /**
+     * Signed subtraction built upon unsigned add and subtract.
+     */
+    void signedSubtract(MutableBigInteger addend) {
+        if (sign == 1)
+            sign = sign * subtract(addend);
+        else
+            add(addend);
+        if (intLen == 0)
+             sign = 1;
+    }
+
+    /**
+     * Print out the first intLen ints of this MutableBigInteger's value
+     * array starting at offset.
+     */
+    public String toString() {
+        return this.toBigInteger(sign).toString();
+    }
+
+}
diff --git a/luni/src/main/java/java/math/TEST_MAPPING b/ojluni/src/main/java/java/math/TEST_MAPPING
similarity index 100%
rename from luni/src/main/java/java/math/TEST_MAPPING
rename to ojluni/src/main/java/java/math/TEST_MAPPING
diff --git a/ojluni/src/main/java/java/math/package-info.java b/ojluni/src/main/java/java/math/package-info.java
new file mode 100644
index 0000000..377cc25
--- /dev/null
+++ b/ojluni/src/main/java/java/math/package-info.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Provides classes for performing arbitrary-precision integer
+ * arithmetic ({@code BigInteger}) and arbitrary-precision decimal
+ * arithmetic ({@code BigDecimal}).  {@code BigInteger} is analogous
+ * to the primitive integer types except that it provides arbitrary
+ * precision, hence operations on {@code BigInteger}s do not overflow
+ * or lose precision.  In addition to standard arithmetic operations,
+ * {@code BigInteger} provides modular arithmetic, GCD calculation,
+ * primality testing, prime generation, bit manipulation, and a few
+ * other miscellaneous operations.
+ *
+ * {@code BigDecimal} provides arbitrary-precision signed decimal
+ * numbers suitable for currency calculations and the like.  {@code
+ * BigDecimal} gives the user complete control over rounding behavior,
+ * allowing the user to choose from a comprehensive set of eight
+ * rounding modes.
+ *
+ * @since JDK1.1
+ */
+package java.math;
diff --git a/ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java b/ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java
index a3c738a..df3b6ed 100644
--- a/ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java
+++ b/ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java
@@ -55,7 +55,7 @@
     protected InetAddress connectedAddress = null;
     private int connectedPort = -1;
 
-    // Android-added: CloseGuard
+    // Android-added: CloseGuard.
     private final CloseGuard guard = CloseGuard.get();
 
     private static final String os = AccessController.doPrivileged(
@@ -67,7 +67,7 @@
      */
     private final static boolean connectDisabled = os.contains("OS X");
 
-    // BEGIN Android-removed: Android doesn't need to load native net library
+    // BEGIN Android-removed: Android doesn't need to load native net library.
     /**
      * Load net library into runtime.
      *
@@ -81,7 +81,7 @@
             });
     }
     */
-    // END Android-removed: Android doesn't need to load native net library
+    // END Android-removed: Android doesn't need to load native net library.
 
     /**
      * Creates a datagram socket
@@ -97,7 +97,7 @@
             throw ioe;
         }
 
-        // Android-added: CloseGuard/fdsan
+        // Android-added: CloseGuard/fdsan.
         if (fd != null && fd.valid()) {
             guard.open("close");
             IoUtils.setFdOwner(fd, this);
@@ -130,7 +130,7 @@
      * @param port the remote port number
      */
     protected void connect(InetAddress address, int port) throws SocketException {
-        // Android-added: BlockGuard
+        // Android-added: BlockGuard.
         BlockGuard.getThreadPolicy().onNetwork();
         connect0(address, port);
         connectedAddress = address;
@@ -248,7 +248,7 @@
      * Close the socket.
      */
     protected void close() {
-        // Android-added: CloseGuard
+        // Android-added: CloseGuard.
         guard.close();
 
         if (fd != null) {
@@ -263,7 +263,7 @@
     }
 
     protected void finalize() {
-        // Android-added: CloseGuard
+        // Android-added: CloseGuard.
         if (guard != null) {
             guard.warnIfOpen();
         }
@@ -375,7 +375,7 @@
             case SO_REUSEADDR:
             case SO_BROADCAST:
                 result = socketGetOption(optID);
-                // Android-added: Added for app compat reason. See methodgetNIFirstAddress
+                // Android-added: Added for app compat reason. See methodgetNIFirstAddress.
                 if (optID == IP_MULTICAST_IF) {
                     return getNIFirstAddress((Integer)result);
                 }
@@ -422,7 +422,7 @@
         return connectDisabled;
     }
 
-    // Android-changed: rewritten on the top of IoBridge
+    // Android-changed: rewritten on the top of IoBridge.
     int dataAvailable() {
         try {
             return IoBridge.available(fd);
diff --git a/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java b/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java
index 0500811..963b2ac 100644
--- a/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java
+++ b/ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java
@@ -50,7 +50,7 @@
 {
     /* instance variable for SO_TIMEOUT */
     int timeout;   // timeout in millisec
-    // Android-removed: traffic class is set through socket
+    // Android-removed: traffic class is set through socket.
     // private int trafficClass;
 
     private boolean shut_rd = false;
@@ -63,7 +63,7 @@
     protected int fdUseCount = 0;
 
     /* lock when increment/decrementing fdUseCount */
-    // Android-added: @ReachabilitySensitive
+    // Android-added: @ReachabilitySensitive.
     // Marked mostly because it's used where fd is, and fd isn't declared here.
     // This adds reachabilityFences where we would if fd were annotated.
     @ReachabilitySensitive
@@ -83,7 +83,7 @@
     */
     protected boolean stream;
 
-    // BEGIN Android-removed: Android doesn't need to load native net library
+    // BEGIN Android-removed: Android doesn't need to load native net library.
     /*
     /**
      * Load net library into runtime.
@@ -98,9 +98,9 @@
             });
     }
     */
-    // END Android-removed: Android doesn't need to load native net library
+    // END Android-removed: Android doesn't need to load native net library.
 
-    // Android-added: logs a warning if socket is not closed
+    // Android-added: logs a warning if socket is not closed.
     @ReachabilitySensitive
     private final CloseGuard guard = CloseGuard.get();
 
@@ -112,18 +112,18 @@
         this.stream = stream;
         if (!stream) {
             ResourceManager.beforeUdpCreate();
-            // Android-removed: socketCreate should set fd if it succeeds
+            // Android-removed: socketCreate should set fd if it succeeds.
             // fd = new FileDescriptor();
             try {
                 socketCreate(false);
             } catch (IOException ioe) {
                 ResourceManager.afterUdpClose();
-                // Android-removed: b/26470377 Represent closed sockets with invalid fd, not null.
+                // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
                 // fd = null;
                 throw ioe;
             }
         } else {
-            // Android-removed: socketCreate should set fd if it succeeds
+            // Android-removed: socketCreate should set fd if it succeeds.
             // fd = new FileDescriptor();
             socketCreate(true);
         }
@@ -132,7 +132,7 @@
         if (serverSocket != null)
             serverSocket.setCreated();
 
-        // Android-added: CloseGuard
+        // Android-added: CloseGuard.
         if (fd != null && fd.valid()) {
             guard.open("close");
         }
@@ -301,7 +301,7 @@
         socketSetOption(opt, on, val);
         */
         // END Android-removed: Logic dealing with value type moved to socketSetOption.
-        // Android-added: Keep track of timeout value not handled by socketSetOption
+        // Android-added: Keep track of timeout value not handled by socketSetOption.
         if (opt == SO_TIMEOUT) {
             timeout = (Integer) val;
         }
@@ -385,7 +385,7 @@
         try {
             acquireFD();
             try {
-                // Android-added: BlockGuard
+                // Android-added: BlockGuard.
                 BlockGuard.getThreadPolicy().onNetwork();
                 socketConnect(address, port, timeout);
                 /* socket may have been closed during poll/select */
@@ -446,7 +446,7 @@
     protected void accept(SocketImpl s) throws IOException {
         acquireFD();
         try {
-            // Android-added: BlockGuard
+            // Android-added: BlockGuard.
             BlockGuard.getThreadPolicy().onNetwork();
             socketAccept(s);
         } finally {
@@ -558,10 +558,9 @@
                 if (!stream) {
                     ResourceManager.afterUdpClose();
                 }
-                // Android-changed:
-                // Socket should be untagged before the preclose. After preclose,
-                // socket will dup2-ed to marker_fd, therefore, it won't describe the same file.
-                // If closingPending is true, then the socket has been preclosed.
+                // Android-changed: Socket should be untagged before the preclose.
+                // After preclose, socket will dup2-ed to marker_fd, therefore, it won't describe
+                // the same file.  If closingPending is true, then the socket has been preclosed.
                 //
                 // Also, close the CloseGuard when the #close is called.
                 if (!closePending) {
@@ -582,8 +581,8 @@
                         } finally {
                             socketClose();
                         }
-                        // Android-changed(http://b/26470377): Some Android code doesn't expect file
-                        // descriptor to be null. socketClose invalidates the fd by closing the fd.
+                        // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
+                        // socketClose invalidates the fd by closing the fd.
                         // fd = null;
                         return;
                     } else {
@@ -607,7 +606,7 @@
             // Android-changed: Notified the CloseGuard object as the fd has been released.
             guard.close();
         }
-        // Android-removed: b/26470377 Represent closed sockets with invalid fd, not null.
+        // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
         // fd = null;
         super.reset();
     }
@@ -617,7 +616,7 @@
      * Shutdown read-half of the socket connection;
      */
     protected void shutdownInput() throws IOException {
-      // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+      // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
       if (fd != null && fd.valid()) {
           socketShutdown(SHUT_RD);
           if (socketInputStream != null) {
@@ -631,7 +630,7 @@
      * Shutdown write-half of the socket connection;
      */
     protected void shutdownOutput() throws IOException {
-      // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+      // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
       if (fd != null && fd.valid()) {
           socketShutdown(SHUT_WR);
           shut_wr = true;
@@ -643,7 +642,7 @@
     }
 
     protected void sendUrgentData (int data) throws IOException {
-        // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+        // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
         if (fd == null || !fd.valid()) {
             throw new IOException("Socket Closed");
         }
@@ -654,7 +653,7 @@
      * Cleans up if the user forgets to close it.
      */
     protected void finalize() throws IOException {
-        // Android-added: CloseGuard
+        // Android-added: CloseGuard.
         if (guard != null) {
             guard.warnIfOpen();
         }
@@ -688,8 +687,8 @@
                     try {
                         socketClose();
                     } catch (IOException e) {
-                        // Android-removed: b/26470377 Some Android code doesn't expect file
-                        // descriptor to be null. socketClose invalidates the fd by closing the fd.
+                        // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
+                        // socketClose invalidates the fd by closing the fd.
                         // } finally {
                         //     fd = null;
                     }
@@ -734,7 +733,7 @@
          * close is in progress.
          */
         synchronized (fdLock) {
-            // Android-changed: b/26470377 Represent closed sockets with invalid fd, not null.
+            // Android-changed: Closed sockets use an invalid fd, not null. b/26470377
             if (closePending || (fd == null) || !fd.valid()) {
                 return true;
             } else {
@@ -781,8 +780,8 @@
     abstract void socketShutdown(int howto)
         throws IOException;
 
-    // Android-changed: Method signature changed, socket{Get,Set}Option work directly with Object
-    // values.
+    // Android-changed: Method signature changes.
+    // socket{Get,Set}Option work directly with Object values.
     abstract void socketSetOption(int cmd, Object value) throws SocketException;
     abstract Object socketGetOption(int opt) throws SocketException;
 
diff --git a/ojluni/src/main/java/java/net/CookieManager.java b/ojluni/src/main/java/java/net/CookieManager.java
index dfd3b86..bff9734 100644
--- a/ojluni/src/main/java/java/net/CookieManager.java
+++ b/ojluni/src/main/java/java/net/CookieManager.java
@@ -250,7 +250,7 @@
                 }
             }
         }
-        // Android-added: b/25897688 A fix to return empty map if cookies list is empty
+        // Android-added: A fix to return empty map if cookies list is empty. b/25897688
         if (cookies.isEmpty()) {
             return Collections.emptyMap();
         }
@@ -315,7 +315,7 @@
                                 }
                             }
                             cookie.setPath(path);
-                        // Android-added: b/25763487 A fix to verify cookie URI before removal
+                        // Android-added: A fix to verify cookie URI before removal. b/25763487
                         } else {
                             // Validate existing path
                             if (!pathMatches(uri, cookie)) {
@@ -408,7 +408,7 @@
         return false;
     }
 
-    // Android-changed: b/25763487 Cookie path matching logic in OpenJDK was wrong
+    // Android-changed: Cookie path matching logic in OpenJDK was wrong. b/25763487
     /**
      * Return true iff. the path from {@code cookie} matches the path from {@code uri}.
      */
@@ -436,9 +436,9 @@
     private List<String> sortByPath(List<HttpCookie> cookies) {
         Collections.sort(cookies, new CookiePathComparator());
 
-        // BEGIN Android-changed: Netscape cookie spec and RFC 2965 have different format
-        // of Cookie header; RFC 2965 requires a leading $Version="1" string while Netscape does not
-        // The workaround here is to add a $Version="1" string in advance
+        // BEGIN Android-changed: Cookie header differs in Netscape cookie spec and RFC 2965.
+        // RFC 2965 requires a leading $Version="1" string while Netscape does not.
+        // The workaround here is to add a $Version="1" string in advance.
         final StringBuilder result = new StringBuilder();
         int minVersion = 1;
         for (HttpCookie cookie : cookies) {
@@ -461,7 +461,7 @@
 
         List<String> cookieHeader = new java.util.ArrayList<String>();
         cookieHeader.add(result.toString());
-        // END Android-changed: Netscape cookie spec and RFC 2965 have different format
+        // END Android-changed: Cookie header differs in Netscape cookie spec and RFC 2965.
         return cookieHeader;
     }
 
@@ -475,7 +475,7 @@
             // path rule only applies to the cookies with same name
             if (!c1.getName().equals(c2.getName())) return 0;
 
-            // Android-changed: normalize before comparison
+            // Android-changed: normalize before comparison.
             final String c1Path = normalizePath(c1.getPath());
             final String c2Path = normalizePath(c2.getPath());
 
diff --git a/ojluni/src/main/java/java/net/DatagramSocket.java b/ojluni/src/main/java/java/net/DatagramSocket.java
index 3e3faf7..e78f3f2 100755
--- a/ojluni/src/main/java/java/net/DatagramSocket.java
+++ b/ojluni/src/main/java/java/net/DatagramSocket.java
@@ -1349,7 +1349,11 @@
 
     // Android-added: for testing and internal use.
     /**
+     * Gets socket's underlying {@link FileDescriptor}.
+     *
      * @hide internal use only
+     *
+     * @return socket's underlying {@link FileDescriptor}.
      */
     public FileDescriptor getFileDescriptor$() {
         return impl.fd;
diff --git a/ojluni/src/main/java/java/net/HttpCookie.java b/ojluni/src/main/java/java/net/HttpCookie.java
index 3ff33c7..70b9b6b 100644
--- a/ojluni/src/main/java/java/net/HttpCookie.java
+++ b/ojluni/src/main/java/java/net/HttpCookie.java
@@ -57,7 +57,7 @@
  * @since 1.6
  */
 public final class HttpCookie implements Cloneable {
-    // BEGIN Android-added: Reserved name can't be HttpCookie name
+    // BEGIN Android-added: Reserved name can't be HttpCookie name.
     private static final Set<String> RESERVED_NAMES = new HashSet<String>();
 
     static {
@@ -73,7 +73,7 @@
         RESERVED_NAMES.add("secure");     // Netscape  RFC 2109  RFC 2965  RFC 6265
         RESERVED_NAMES.add("version");    //           RFC 2109  RFC 2965  RFC 6265
     }
-    // END Android-added: Reserved name can't be HttpCookie name
+    // END Android-added: Reserved name can't be HttpCookie name.
 
     // ---------------- Fields --------------
 
@@ -691,13 +691,13 @@
             String H = host.substring(0, lengthDiff);
             String D = host.substring(lengthDiff);
 
-            // BEGIN Android-changed: App compat reason
+            // BEGIN Android-changed: App compat reason.
             // 1) Disregard RFC 2965 sec. 3.3.2, the "The request-host is a HDN..."
             // 2) match "foo.local" for domain ".local".
             // return (H.indexOf('.') == -1 && D.equalsIgnoreCase(domain));
             return D.equalsIgnoreCase(domain) && ((domain.startsWith(".") && isFullyQualifiedDomainName(domain, 1))
                 || isLocalDomain);
-            // END Android-changed: App compat reason
+            // END Android-changed: App compat reason.
         }
         else if (lengthDiff == -1) {
             // if domain is actually .host
@@ -708,12 +708,12 @@
         return false;
     }
 
-    // BEGIN Android-added: App compat reason
+    // BEGIN Android-added: App compat reason.
     private static boolean isFullyQualifiedDomainName(String s, int firstCharacter) {
         int dotPosition = s.indexOf('.', firstCharacter + 1);
         return dotPosition != -1 && dotPosition < s.length() - 1;
     }
-    // END Android-added: App compat reason
+    // END Android-added: App compat reason.
 
     /**
      * Constructs a cookie header string representation of this cookie,
@@ -799,7 +799,7 @@
     // from RFC 2068, token special case characters
     //
     // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
-    // Android-changed: App compat reason. Disallow "=\t" as token
+    // Android-changed: App compat reason. Disallow "=\t" as token.
     // private static final String tspecials = ",; ";  // deliberately includes space
     private static final String tspecials = ",;= \t";
 
@@ -813,7 +813,7 @@
      *          {@code false} if it is not
      */
     private static boolean isToken(String value) {
-        // Android-added: Reserved name can't be a token
+        // Android-added: Reserved name can't be a token.
         if (RESERVED_NAMES.contains(value.toLowerCase(Locale.US))) {
             return false;
         }
@@ -998,7 +998,7 @@
                         // BEGIN Android-changed: Use HttpDate for date parsing.
                         // it accepts broader set of date formats.
                         // cookie.setMaxAge(cookie.expiryDate2DeltaSeconds(attrValue));
-                        // Android-changed: Altered max age calculation to avoid setting
+                        // Android-changed: Altered max age calculation to avoid setting.
                         // it to MAX_AGE_UNSPECIFIED (-1) if "expires" is one second in past.
                         Date date = HttpDate.parse(attrValue);
                         long maxAgeInSeconds = 0;
@@ -1030,7 +1030,7 @@
         }
     }
 
-    // BEGIN Android-removed: Android doesn't use JavaNetHttpCookieAccess
+    // BEGIN Android-removed: Android doesn't use JavaNetHttpCookieAccess.
     /*
     static {
         sun.misc.SharedSecrets.setJavaNetHttpCookieAccess(
@@ -1046,7 +1046,7 @@
         );
     }
     */
-    // END Android-removed: Android doesn't use JavaNetHttpCookieAccess
+    // END Android-removed: Android doesn't use JavaNetHttpCookieAccess.
 
     /*
      * Returns the original header this cookie was consructed from, if it was
diff --git a/ojluni/src/main/java/java/net/IDN.java b/ojluni/src/main/java/java/net/IDN.java
index a18c3a8..5bce94c 100644
--- a/ojluni/src/main/java/java/net/IDN.java
+++ b/ojluni/src/main/java/java/net/IDN.java
@@ -25,7 +25,7 @@
  */
 package java.net;
 
-import android.icu.text.IDNA;
+import com.android.icu.text.ExtendedIDNA;
 
 /**
  * Provides methods to convert internationalized domain names (IDNs) between
@@ -103,9 +103,9 @@
      * @throws IllegalArgumentException   if the input string doesn't conform to RFC 3490 specification
      */
     public static String toASCII(String input, int flag) {
-        // BEGIN Android-changed: Use ICU4J implementation
+        // BEGIN Android-changed: Use ICU4J implementation.
         try {
-            return IDNA.convertIDNToASCII(input, flag).toString();
+            return ExtendedIDNA.convertIDNToASCII(input, flag).toString();
         } catch (android.icu.text.StringPrepParseException e) {
             // b/113787610: "." is a valid IDN but is rejected by ICU.
             // Usage is relatively uncommon, so only check for it if ICU throws.
@@ -114,7 +114,7 @@
             }
             throw new IllegalArgumentException("Invalid input to toASCII: " + input, e);
         }
-        // END Android-changed: Use ICU4J implementation
+        // END Android-changed: Use ICU4J implementation.
     }
 
 
@@ -158,20 +158,20 @@
      * @return          the translated {@code String}
      */
     public static String toUnicode(String input, int flag) {
-        // BEGIN Android-changed: Use ICU4J implementation
+        // BEGIN Android-changed: Use ICU4J implementation.
         try {
             // ICU only translates separators to ASCII for toASCII.
             // Java expects the translation for toUnicode too.
-            return convertFullStop(IDNA.convertIDNToUnicode(input, flag)).toString();
+            return convertFullStop(ExtendedIDNA.convertIDNToUnicode(input, flag)).toString();
         } catch (android.icu.text.StringPrepParseException e) {
             // The RI documentation explicitly states that if the conversion was unsuccessful
             // the original string is returned.
             return input;
         }
-        // END Android-changed: Use ICU4J implementation
+        // END Android-changed: Use ICU4J implementation.
     }
 
-    // BEGIN Android-added: Use ICU4J implementation
+    // BEGIN Android-added: Use ICU4J implementation.
     private static boolean isLabelSeperator(char c) {
         return (c == '\u3002' || c == '\uff0e' || c == '\uff61');
     }
@@ -184,7 +184,7 @@
         }
         return input;
     }
-    // END Android-added: Use ICU4J implementation
+    // END Android-added: Use ICU4J implementation.
 
     /**
      * Translates a string from ASCII Compatible Encoding (ACE) to Unicode,
diff --git a/ojluni/src/main/java/java/net/InMemoryCookieStore.java b/ojluni/src/main/java/java/net/InMemoryCookieStore.java
index 5df66c0..85aab8b 100644
--- a/ojluni/src/main/java/java/net/InMemoryCookieStore.java
+++ b/ojluni/src/main/java/java/net/InMemoryCookieStore.java
@@ -36,7 +36,7 @@
 import java.util.Iterator;
 import java.util.concurrent.locks.ReentrantLock;
 
-// Android-changed: App compat changes and bug fixes
+// Android-changed: App compat changes and bug fixes.
 // b/26456024 Add targetSdkVersion based compatibility for domain matching
 // b/33034917 Support clearing cookies by adding it with "max-age=0"
 // b/25897688 InMemoryCookieStore ignores scheme (http/https) port and path of the cookie
@@ -52,7 +52,7 @@
  */
 public class InMemoryCookieStore implements CookieStore {
     // the in-memory representation of cookies
-    // BEGIN Android-removed: Remove cookieJar and domainIndex
+    // BEGIN Android-removed: Remove cookieJar and domainIndex.
     /*
     private List<HttpCookie> cookieJar = null;
 
@@ -62,13 +62,13 @@
     //          presence of cookie when retrieve one form index store.
     private Map<String, List<HttpCookie>> domainIndex = null;
     */
-    // END Android-removed: Remove cookieJar and domainIndex
+    // END Android-removed: Remove cookieJar and domainIndex.
     private Map<URI, List<HttpCookie>> uriIndex = null;
 
     // use ReentrantLock instead of syncronized for scalability
     private ReentrantLock lock = null;
 
-    // BEGIN Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex
+    // BEGIN Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex.
     private final boolean applyMCompatibility;
 
     /**
@@ -83,7 +83,7 @@
         lock = new ReentrantLock(false);
         applyMCompatibility = (targetSdkVersion <= 23);
     }
-    // END Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex
+    // END Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex.
 
     /**
      * Add one cookie into cookie store.
@@ -96,8 +96,8 @@
 
         lock.lock();
         try {
-            // Android-changed: http://b/33034917, android supports clearing cookies
-            // by adding the cookie with max-age: 0.
+            // Android-changed: Android supports clearing cookies. http://b/33034917
+            // They are cleared by adding the cookie with max-age: 0.
             //if (cookie.getMaxAge() != 0) {
             addIndex(uriIndex, getEffectiveURI(uri), cookie);
             //}
@@ -121,7 +121,7 @@
         }
 
         List<HttpCookie> cookies = new ArrayList<HttpCookie>();
-        // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+        // BEGIN Android-changed: InMemoryCookieStore ignores scheme (http/https). b/25897688
         lock.lock();
         try {
             // check domainIndex first
@@ -131,7 +131,7 @@
         } finally {
             lock.unlock();
         }
-        // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+        // END Android-changed: InMemoryCookieStore ignores scheme (http/https). b/25897688
         return cookies;
     }
 
@@ -139,7 +139,7 @@
      * Get all cookies in cookie store, except those have expired
      */
     public List<HttpCookie> getCookies() {
-        // BEGIN Android-changed: Remove cookieJar and domainIndex
+        // BEGIN Android-changed: Remove cookieJar and domainIndex.
         List<HttpCookie> rt = new ArrayList<HttpCookie>();
 
         lock.lock();
@@ -159,7 +159,7 @@
             rt = Collections.unmodifiableList(rt);
             lock.unlock();
         }
-        // END Android-changed: Remove cookieJar and domainIndex
+        // END Android-changed: Remove cookieJar and domainIndex.
 
         return rt;
     }
@@ -213,7 +213,7 @@
             throw new NullPointerException("cookie is null");
         }
 
-        // BEGIN Android-changed: Fix uri not being removed from uriIndex
+        // BEGIN Android-changed: Fix uri not being removed from uriIndex.
         lock.lock();
         try {
             uri = getEffectiveURI(uri);
@@ -230,7 +230,7 @@
         } finally {
             lock.unlock();
         }
-        // END Android-changed: Fix uri not being removed from uriIndex
+        // END Android-changed: Fix uri not being removed from uriIndex.
     }
 
 
@@ -299,7 +299,7 @@
             // need to check H & D component
             String D = host.substring(lengthDiff);
 
-            // Android-changed: b/26456024 targetSdkVersion based compatibility for domain matching
+            // Android-changed: b/26456024 targetSdkVersion based compatibility for domain matching.
             // Android M and earlier: Cookies with domain "foo.com" would not match "bar.foo.com".
             // The RFC dictates that the user agent must treat those domains as if they had a
             // leading period and must therefore match "bar.foo.com".
@@ -319,7 +319,7 @@
 
     private void getInternal1(List<HttpCookie> cookies, Map<URI, List<HttpCookie>> cookieIndex,
             String host) {
-        // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+        // BEGIN Android-changed: InMemoryCookieStore ignores scheme (http/https). b/25897688
         // Use a separate list to handle cookies that need to be removed so
         // that there is no conflict with iterators.
         ArrayList<HttpCookie> toRemove = new ArrayList<HttpCookie>();
@@ -348,7 +348,7 @@
             }
             toRemove.clear();
         }
-        // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+        // END Android-changed: InMemoryCookieStore ignores scheme (http/https). b/25897688
     }
 
     // @param cookies           [OUT] contains the found cookies
@@ -359,7 +359,7 @@
         void getInternal2(List<HttpCookie> cookies, Map<T, List<HttpCookie>> cookieIndex,
                           T comparator)
     {
-        // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+        // BEGIN Android-changed: InMemoryCookieStore ignores scheme (http/https). b/25897688
         // Removed cookieJar
         for (T index : cookieIndex.keySet()) {
             if ((index == comparator) || (index != null && comparator.compareTo(index) == 0)) {
@@ -381,7 +381,7 @@
                 } // end of indexedCookies != null
             } // end of comparator.compareTo(index) == 0
         } // end of cookieIndex iteration
-        // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
+        // END Android-changed: InMemoryCookieStore ignores scheme (http/https). b/25897688
     }
 
     // add 'cookie' indexed by 'index' into 'indexStore'
@@ -389,9 +389,9 @@
                               T index,
                               HttpCookie cookie)
     {
-        // Android-changed: "index" can be null. We only use the URI based
-        // index on Android and we want to support null URIs. The underlying
-        // store is a HashMap which will support null keys anyway.
+        // Android-changed: "index" can be null.
+        // We only use the URI based index on Android and we want to support null URIs. The
+        // underlying store is a HashMap which will support null keys anyway.
         // if (index != null) {
         List<HttpCookie> cookies = indexStore.get(index);
         if (cookies != null) {
@@ -413,7 +413,7 @@
     //
     private URI getEffectiveURI(URI uri) {
         URI effectiveURI = null;
-        // Android-added: Fix NullPointerException
+        // Android-added: Fix NullPointerException.
         if (uri == null) {
             return null;
         }
diff --git a/ojluni/src/main/java/java/net/Inet4Address.java b/ojluni/src/main/java/java/net/Inet4Address.java
index 1fb7b92..08952ee 100644
--- a/ojluni/src/main/java/java/net/Inet4Address.java
+++ b/ojluni/src/main/java/java/net/Inet4Address.java
@@ -93,21 +93,34 @@
      *  serialized */
     private static final long serialVersionUID = 3286316764910316507L;
 
-    // BEGIN Android-added: Define special-purpose IPv4 address
-    /** @hide */
+    // BEGIN Android-added: Define special-purpose IPv4 address.
+    /**
+     * Reserved address for {@code INADDR_ANY}, to specify any IPv4 address at all.
+     *
+     * @hide
+     */
     public static final InetAddress ANY = new Inet4Address(null, new byte[] { 0, 0, 0, 0 });
 
-    /** @hide */
+    /**
+     * Broadcast address to transmit to all devices on network.
+     *
+     * @hide
+     */
     public static final InetAddress ALL =
             new Inet4Address(null, new byte[] { (byte) 255, (byte) 255,
                   (byte) 255, (byte) 255 });
-    /** @hide */
+
+    /**
+     * Loopback address to the local host.
+     *
+     * @hide
+     */
     public static final InetAddress LOOPBACK =
             new Inet4Address("localhost", new byte[] { 127, 0, 0, 1 });
-    // END Android-added: Define special-purpose IPv4 address
+    // END Android-added: Define special-purpose IPv4 address.
 
 
-    // BEGIN Android-removed: Android doesn't need to call native init
+    // BEGIN Android-removed: Android doesn't need to call native init.
     /*
      * Perform initializations.
      *
@@ -115,7 +128,7 @@
         init();
     }
     */
-    // END Android-removed: Android doesn't need to call native init
+    // END Android-removed: Android doesn't need to call native init.
     Inet4Address() {
         super();
         holder().hostName = null;
@@ -391,11 +404,11 @@
         return (src[0] & 0xff) + "." + (src[1] & 0xff) + "." + (src[2] & 0xff) + "." + (src[3] & 0xff);
     }
 
-    // BEGIN Android-removed: Android doesn't need to call native init
+    // BEGIN Android-removed: Android doesn't need to call native init.
     /*
      * Perform class load-time initializations.
      *
     private static native void init();
     */
-    // END Android-removed: Android doesn't need to call native init
+    // END Android-removed: Android doesn't need to call native init.
 }
diff --git a/ojluni/src/main/java/java/net/Inet6Address.java b/ojluni/src/main/java/java/net/Inet6Address.java
index c0aadb3..a8c7510 100644
--- a/ojluni/src/main/java/java/net/Inet6Address.java
+++ b/ojluni/src/main/java/java/net/Inet6Address.java
@@ -178,23 +178,31 @@
 class Inet6Address extends InetAddress {
     final static int INADDRSZ = 16;
 
-    // BEGIN Android-removed: Remove special handling for link-local addresses
+    // BEGIN Android-removed: Remove special handling for link-local addresses.
     /*
     * cached scope_id - for link-local address use only.
     *
     private transient int cached_scope_id;  // 0
     */
-    // END Android-removed: Remove special handling for link-local addresses
+    // END Android-removed: Remove special handling for link-local addresses.
 
-    // BEGIN Android-added: Define special-purpose IPv6 address
-    /** @hide */
+    // BEGIN Android-added: Define special-purpose IPv6 address.
+    /**
+     * Reserved address for {@code INADDR_ANY}, to specify any IPv6 address at all.
+     *
+     * @hide
+     */
     public static final InetAddress ANY =
             new Inet6Address("::", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0);
 
-    /** @hide */
+    /**
+     * Loopback address to the local host.
+     *
+     * @hide
+     */
     public static final InetAddress LOOPBACK = new Inet6Address("ip6-localhost",
             new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 0);
-    // END Android-added: Define special-purpose IPv6 address
+    // END Android-added: Define special-purpose IPv6 address.
 
     private class Inet6AddressHolder {
 
@@ -252,7 +260,7 @@
         void init(byte addr[], int scope_id) {
             setAddr(addr);
 
-            // Android-changed: was >= 0
+            // Android-changed: was >= 0.
             if (scope_id > 0) {
                 this.scope_id = scope_id;
                 this.scope_id_set = true;
@@ -272,7 +280,7 @@
             }
         }
 
-        // Android-removed: getnameinfo returns smarter representations than getHostAddress()
+        // Android-removed: getnameinfo returns smarter representations than getHostAddress().
         /*
         String getHostAddress() {
             String s = numericToTextFormat(ipaddress);
@@ -389,11 +397,11 @@
 
     private static final long serialVersionUID = 6880410070516793377L;
 
-    // BEGIN Android-removed: Android doesn't need to call native init
+    // BEGIN Android-removed: Android doesn't need to call native init.
     /*
     // Perform native initialization
     static { init(); }
-    // END Android-removed: Android doesn't need to call native init
+    // END Android-removed: Android doesn't need to call native init.
     */
 
     Inet6Address() {
@@ -622,7 +630,7 @@
         throws IOException, ClassNotFoundException {
         NetworkInterface scope_ifname = null;
 
-        // Android-changed: was getClass().getClassLoader() != null
+        // Android-changed: was getClass().getClassLoader() != null.
         if (getClass().getClassLoader() != Class.class.getClassLoader()) {
             throw new SecurityException ("invalid address type");
         }
@@ -894,7 +902,7 @@
      */
     @Override
     public String getHostAddress() {
-        // Android-changed: getnameinfo returns smarter representations than getHostAddress()
+        // Android-changed: getnameinfo returns smarter representations than getHostAddress().
         // return holder6.getHostAddress();
         return Libcore.os.getnameinfo(this, NI_NUMERICHOST); // Can't throw.
     }
@@ -971,11 +979,11 @@
         return sb.toString();
     }
 
-    // BEGIN Android-removed: Android doesn't need to call native init
+    // BEGIN Android-removed: Android doesn't need to call native init.
     /*
      * Perform class load-time initializations.
      *
     private static native void init();
     */
-    // END Android-removed: Android doesn't need to call native init
+    // END Android-removed: Android doesn't need to call native init.
 }
diff --git a/ojluni/src/main/java/java/net/InetAddress.java b/ojluni/src/main/java/java/net/InetAddress.java
index e4f8660..0cefc10 100644
--- a/ojluni/src/main/java/java/net/InetAddress.java
+++ b/ojluni/src/main/java/java/net/InetAddress.java
@@ -1664,7 +1664,7 @@
      * @param netId the network to use for host resolution.
      * @return the {@code InetAddress} instance representing the host.
      * @throws UnknownHostException if the address lookup fails.
-     * @hide internal use only
+     * @hide
      */
     public static InetAddress getByNameOnNet(String host, int netId) throws UnknownHostException {
         return impl.lookupAllHostAddr(host, netId)[0];
@@ -1678,7 +1678,7 @@
      * @param netId the network to use for host resolution.
      * @return the array of addresses associated with the specified host.
      * @throws UnknownHostException if the address lookup fails.
-     * @hide internal use only
+     * @hide
      */
     public static InetAddress[] getAllByNameOnNet(String host, int netId) throws UnknownHostException {
         return impl.lookupAllHostAddr(host, netId).clone();
diff --git a/ojluni/src/main/java/java/net/PlainDatagramSocketImpl.java b/ojluni/src/main/java/java/net/PlainDatagramSocketImpl.java
index d4b1f2c..a086d9f 100644
--- a/ojluni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/ojluni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -47,6 +47,7 @@
 import static libcore.io.IoBridge.JAVA_MCAST_LEAVE_GROUP;
 import static sun.net.ExtendedOptionsImpl.*;
 
+// Android-changed: Rewritten to use android.system POSIX calls and assume AF_INET6.
 /*
  * On Unix systems we simply delegate to native methods.
  *
diff --git a/ojluni/src/main/java/java/net/Socket.java b/ojluni/src/main/java/java/net/Socket.java
index bae1d1c..e8c253b 100644
--- a/ojluni/src/main/java/java/net/Socket.java
+++ b/ojluni/src/main/java/java/net/Socket.java
@@ -1775,7 +1775,11 @@
 
     // Android-added: getFileDescriptor$() method for testing and internal use.
     /**
+     * Gets socket's underlying {@link FileDescriptor}.
+     *
      * @hide internal use only
+     *
+     * @return socket's underlying {@link FileDescriptor}.
      */
     public FileDescriptor getFileDescriptor$() {
         return impl.getFileDescriptor();
diff --git a/ojluni/src/main/java/java/net/SocketImpl.java b/ojluni/src/main/java/java/net/SocketImpl.java
index ade2630..51000db 100644
--- a/ojluni/src/main/java/java/net/SocketImpl.java
+++ b/ojluni/src/main/java/java/net/SocketImpl.java
@@ -229,7 +229,11 @@
 
     // Android-added: getFD$() for testing.
     /**
+     * Gets socket's underlying {@link FileDescriptor}.
+     *
      * @hide used by java.nio tests
+     *
+     * @return socket's underlying {@link FileDescriptor}.
      */
     public FileDescriptor getFD$() {
         return fd;
diff --git a/ojluni/src/main/java/java/net/URLStreamHandler.java b/ojluni/src/main/java/java/net/URLStreamHandler.java
index dffc6d5..c0876e8 100644
--- a/ojluni/src/main/java/java/net/URLStreamHandler.java
+++ b/ojluni/src/main/java/java/net/URLStreamHandler.java
@@ -134,9 +134,9 @@
 
         boolean isRelPath = false;
         boolean queryOnly = false;
-        // BEGIN Android-changed: App compat
+        // BEGIN Android-changed: App compat.
         boolean querySet = false;
-        // END Android-changed: App compat
+        // END Android-changed: App compat.
 
 // FIX: should not assume query if opaque
         // Strip off the query part
@@ -148,22 +148,22 @@
                 if (limit > queryStart)
                     limit = queryStart;
                 spec = spec.substring(0, queryStart);
-                // BEGIN Android-changed: App compat
+                // BEGIN Android-changed: App compat.
                 querySet = true;
-                // END Android-changed: App compat
+                // END Android-changed: App compat.
             }
         }
 
         int i = 0;
         // Parse the authority part if any
-        // BEGIN Android-changed: App compat
+        // BEGIN Android-changed: App compat.
         // boolean isUNCName = (start <= limit - 4) &&
         //                 (spec.charAt(start) == '/') &&
         //                 (spec.charAt(start + 1) == '/') &&
         //                 (spec.charAt(start + 2) == '/') &&
         //                 (spec.charAt(start + 3) == '/');
         boolean isUNCName = false;
-        // END Android-changed: App compat
+        // END Android-changed: App compat.
         if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') &&
             (spec.charAt(start + 1) == '/')) {
             start += 2;
@@ -239,7 +239,7 @@
                     if (ind >= 0) {
                         // port can be null according to RFC2396
                         if (host.length() > (ind + 1)) {
-                            // BEGIN Android-changed: App compat
+                            // BEGIN Android-changed: App compat.
                             // port = Integer.parseInt(host.substring(ind + 1));
                             char firstPortChar = host.charAt(ind+1);
                             if (firstPortChar >= '0' && firstPortChar <= '9') {
@@ -248,7 +248,7 @@
                                 throw new IllegalArgumentException("invalid port: " +
                                                                    host.substring(ind + 1));
                             }
-                            // END Android-changed: App compat
+                            // END Android-changed: App compat.
                         }
                         host = host.substring(0, ind);
                     }
@@ -263,14 +263,14 @@
 
             // If the authority is defined then the path is defined by the
             // spec only; See RFC 2396 Section 5.2.4.
-            // BEGIN Android-changed: App compat
+            // BEGIN Android-changed: App compat.
             // if (authority != null && authority.length() > 0)
             //   path = "";
             path = null;
             if (!querySet) {
                 query = null;
             }
-            // END Android-changed: App compat
+            // END Android-changed: App compat.
         }
 
         if (host == null) {
@@ -297,21 +297,21 @@
                 path = seperator + spec.substring(start, limit);
             }
         }
-        // BEGIN Android-changed: App compat
+        // BEGIN Android-changed: App compat.
         //else if (queryOnly && path != null) {
         //    int ind = path.lastIndexOf('/');
         //    if (ind < 0)
         //        ind = 0;
         //    path = path.substring(0, ind) + "/";
         //}
-        // END Android-changed: App compat
+        // END Android-changed: App compat.
         if (path == null)
             path = "";
 
-        // BEGIN Android-changed
+        // BEGIN Android-changed: always assume isRelPath is true.
         //if (isRelPath) {
         if (true) {
-        // END Android-changed
+        // END Android-changed: always assume isRelPath is true.
             // Remove embedded /./
             while ((i = path.indexOf("/./")) >= 0) {
                 path = path.substring(0, i) + path.substring(i + 2);
@@ -319,21 +319,21 @@
             // Remove embedded /../ if possible
             i = 0;
             while ((i = path.indexOf("/../", i)) >= 0) {
-                // BEGIN Android-changed: App compat
+                // BEGIN Android-changed: App compat.
                 /*
                  * Trailing /../
                  */
                 if (i == 0) {
                     path = path.substring(i + 3);
                     i = 0;
-                // END Android-changed: App compat
+                // END Android-changed: App compat.
                 /*
                  * A "/../" will cancel the previous segment and itself,
                  * unless that segment is a "/../" itself
                  * i.e. "/a/b/../c" becomes "/a/c"
                  * but "/../../a" should stay unchanged
                  */
-                // Android-changed: App compat
+                // Android-changed: App compat.
                 // if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
                 } else if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
                     (path.indexOf("/../", limit) != 0)) {
@@ -360,7 +360,7 @@
             if (path.endsWith("/."))
                 path = path.substring(0, path.length() -1);
 
-            // Android-changed: App compat: Remove trailing ?
+            // Android-changed: App compat: Remove trailing '?'.
             if (path.endsWith("?"))
                 path = path.substring(0, path.length() -1);
         }
@@ -391,7 +391,7 @@
      * @since 1.3
      */
     protected boolean equals(URL u1, URL u2) {
-        // Android-changed: Avoid network I/O
+        // Android-changed: Avoid network I/O.
         return Objects.equals(u1.getRef(), u2.getRef()) &&
                Objects.equals(u1.getQuery(), u2.getQuery()) &&
                // sameFile compares the protocol, file, port & host components of
@@ -408,7 +408,7 @@
      * @since 1.3
      */
     protected int hashCode(URL u) {
-        // Android-changed: Avoid network I/O
+        // Android-changed: Avoid network I/O.
         // Hash on the same set of fields that we compare in equals().
         return Objects.hash(
                 u.getRef(),
@@ -522,7 +522,7 @@
         if (u.getRef() != null)
             len += 1 + u.getRef().length();
 
-        // BEGIN Android-changed: Add a toExternalForm variant that optionally escapes illegal chars
+        // BEGIN Android-changed: New toExternalForm variant that optionally escapes illegal chars.
         // TODO: The variant has been removed. We can potentially revert the change
         StringBuilder result = new StringBuilder(len);
         result.append(u.getProtocol());
@@ -535,7 +535,7 @@
         if (fileAndQuery != null) {
             result.append(fileAndQuery);
         }
-        // END Android-changed: Add a toExternalForm variant that optionally escapes illegal chars
+        // END Android-changed: New toExternalForm variant that optionally escapes illegal chars.
         if (u.getRef() != null) {
             result.append("#");
             result.append(u.getRef());
@@ -543,7 +543,7 @@
         return result.toString();
     }
 
-    // Android-changed: Removed @see tag (target is package-private):
+    // Android-changed: Removed @see tag (target is package-private).
     // @see     java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String)
     /**
      * Sets the fields of the {@code URL} argument to the indicated values.
diff --git a/ojluni/src/main/java/java/nio/charset/Charset.java b/ojluni/src/main/java/java/nio/charset/Charset.java
index 5a3b35a..2571569 100755
--- a/ojluni/src/main/java/java/nio/charset/Charset.java
+++ b/ojluni/src/main/java/java/nio/charset/Charset.java
@@ -26,7 +26,7 @@
 
 package java.nio.charset;
 
-import com.android.icu.charset.CharsetICU;
+import com.android.icu.charset.CharsetFactory;
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
@@ -508,7 +508,7 @@
 
         // Android-changed: Drop support for "standard" and "extended"
         // providers.
-        if ((cs = CharsetICU.charsetForName(charsetName))  != null ||
+        if ((cs = CharsetFactory.create(charsetName))  != null ||
             (cs = lookupViaProviders(charsetName))              != null)
         {
             cache(charsetName, cs);
@@ -628,8 +628,8 @@
                     TreeMap<String,Charset> m =
                         new TreeMap<String,Charset>(
                             ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER);
-                    for (String charsetName : CharsetICU.getAvailableCharsetNames()) {
-                        Charset charset = CharsetICU.charsetForName(charsetName);
+                    for (String charsetName : CharsetFactory.getAvailableCharsetNames()) {
+                        Charset charset = CharsetFactory.create(charsetName);
                         m.put(charset.name(), charset);
                     }
                     // Android-changed: No more "standard" provider.
@@ -685,7 +685,6 @@
      * @throws IllegalCharsetNameException
      *         If the canonical name or any of the aliases are illegal
      */
-    @libcore.api.IntraCoreApi
     protected Charset(String canonicalName, String[] aliases) {
         checkName(canonicalName);
         String[] as = (aliases == null) ? new String[0] : aliases;
diff --git a/ojluni/src/main/java/java/security/AlgorithmParameterGenerator.java b/ojluni/src/main/java/java/security/AlgorithmParameterGenerator.java
index 7222b44..4cab0c3 100644
--- a/ojluni/src/main/java/java/security/AlgorithmParameterGenerator.java
+++ b/ojluni/src/main/java/java/security/AlgorithmParameterGenerator.java
@@ -99,7 +99,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
  * AlgorithmParameterGenerator section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -164,7 +164,7 @@
      * @param algorithm the name of the algorithm this
      * parameter generator is associated with.
      * See the AlgorithmParameterGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -206,7 +206,7 @@
      * @param algorithm the name of the algorithm this
      * parameter generator is associated with.
      * See the AlgorithmParameterGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -252,7 +252,7 @@
      * @param algorithm the string name of the algorithm this
      * parameter generator is associated with.
      * See the AlgorithmParameterGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameterGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/AlgorithmParameters.java b/ojluni/src/main/java/java/security/AlgorithmParameters.java
index fd1966f1..989159e 100644
--- a/ojluni/src/main/java/java/security/AlgorithmParameters.java
+++ b/ojluni/src/main/java/java/security/AlgorithmParameters.java
@@ -153,7 +153,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameters">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameters">
  * AlgorithmParameters section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -223,7 +223,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the AlgorithmParameters section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameters">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameters">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -265,7 +265,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the AlgorithmParameters section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameters">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameters">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -314,7 +314,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the AlgorithmParameters section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#AlgorithmParameters">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#AlgorithmParameters">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/KeyFactory.java b/ojluni/src/main/java/java/security/KeyFactory.java
index 716f62d..f687d61 100644
--- a/ojluni/src/main/java/java/security/KeyFactory.java
+++ b/ojluni/src/main/java/java/security/KeyFactory.java
@@ -100,7 +100,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyFactory">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory">
  * KeyFactory section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -178,7 +178,7 @@
      *
      * @param algorithm the name of the requested key algorithm.
      * See the KeyFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -209,7 +209,7 @@
      *
      * @param algorithm the name of the requested key algorithm.
      * See the KeyFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -250,7 +250,7 @@
      *
      * @param algorithm the name of the requested key algorithm.
      * See the KeyFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/KeyPairGenerator.java b/ojluni/src/main/java/java/security/KeyPairGenerator.java
index 7c6cdb5..1c4c2cb 100644
--- a/ojluni/src/main/java/java/security/KeyPairGenerator.java
+++ b/ojluni/src/main/java/java/security/KeyPairGenerator.java
@@ -133,7 +133,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyPairGenerator">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator">
  * KeyPairGenerator section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -162,7 +162,7 @@
      *
      * @param algorithm the standard string name of the algorithm.
      * See the KeyPairGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyPairGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      */
@@ -173,7 +173,7 @@
     /**
      * Returns the standard name of the algorithm for this key pair generator.
      * See the KeyPairGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyPairGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -220,7 +220,7 @@
      *
      * @param algorithm the standard string name of the algorithm.
      * See the KeyPairGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyPairGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -276,7 +276,7 @@
      *
      * @param algorithm the standard string name of the algorithm.
      * See the KeyPairGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyPairGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -317,7 +317,7 @@
      *
      * @param algorithm the standard string name of the algorithm.
      * See the KeyPairGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyPairGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/KeyStore.java b/ojluni/src/main/java/java/security/KeyStore.java
index 7781649..924f14f 100644
--- a/ojluni/src/main/java/java/security/KeyStore.java
+++ b/ojluni/src/main/java/java/security/KeyStore.java
@@ -195,7 +195,7 @@
  * </table>
  *
  * These types are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyStore">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">
  * KeyStore section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -311,7 +311,7 @@
          * @param protectionAlgorithm the encryption algorithm name, for
          *     example, {@code PBEWithHmacSHA256AndAES_256}.
          *     See the Cipher section in the <a href=
-         * "{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher">
+         * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
          * Java Cryptography Architecture Standard Algorithm Name
          * Documentation</a>
          *     for information about standard encryption algorithm names.
@@ -868,7 +868,7 @@
      *
      * @param type the type of keystore.
      * See the KeyStore section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyStore">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard keystore types.
      *
@@ -906,7 +906,7 @@
      *
      * @param type the type of keystore.
      * See the KeyStore section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyStore">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard keystore types.
      *
@@ -949,7 +949,7 @@
      *
      * @param type the type of keystore.
      * See the KeyStore section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyStore">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyStore">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard keystore types.
      *
diff --git a/ojluni/src/main/java/java/security/MessageDigest.java b/ojluni/src/main/java/java/security/MessageDigest.java
index 5b9359f..996339e 100644
--- a/ojluni/src/main/java/java/security/MessageDigest.java
+++ b/ojluni/src/main/java/java/security/MessageDigest.java
@@ -120,7 +120,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest">
  * MessageDigest section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -155,7 +155,7 @@
      *
      * @param algorithm the standard name of the digest algorithm.
      * See the MessageDigest section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      */
@@ -178,7 +178,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the MessageDigest section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -232,7 +232,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the MessageDigest section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -283,7 +283,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the MessageDigest section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -511,7 +511,7 @@
      * implementation details. The name should be a standard
      * Java Security name (such as "SHA", "MD5", and so on).
      * See the MessageDigest section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java
index 9936859..6deb954 100644
--- a/ojluni/src/main/java/java/security/Provider.java
+++ b/ojluni/src/main/java/java/security/Provider.java
@@ -95,7 +95,7 @@
     // Declare serialVersionUID to be compatible with JDK1.1
     static final long serialVersionUID = -4298000515446427739L;
 
-    // Android-added: Provider registration
+    // Android-added: Provider registration.
     // Marking a provider as "registered" makes it change the security version when
     // changes to it are made.  As of 2017-05-22 this is only used in ProviderTest.
     // TODO: Change ProviderTest to no longer require this mechanism
@@ -703,7 +703,7 @@
 
     private void readObject(ObjectInputStream in)
                 throws IOException, ClassNotFoundException {
-        // Android-added: Provider registration
+        // Android-added: Provider registration.
         registered = false;
         Map<Object,Object> copy = new HashMap<>();
         for (Map.Entry<Object,Object> entry : super.entrySet()) {
@@ -717,7 +717,7 @@
     }
 
     private boolean checkLegacy(Object key) {
-        // Android-added: Provider registration
+        // Android-added: Provider registration.
         if (registered) {
             Security.increaseVersion();
         }
@@ -742,7 +742,7 @@
         for (Map.Entry<?,?> e : t.entrySet()) {
             implPut(e.getKey(), e.getValue());
         }
-        // Android-added: Provider registration
+        // Android-added: Provider registration.
         if (registered) {
             Security.increaseVersion();
         }
@@ -817,7 +817,7 @@
             if (!checkLegacy(key)) {
                 return null;
             }
-            // BEGIN Android-changed: use compute() instead of computeIfAbsent() to avoid cast fails
+            // BEGIN Android-changed: use compute(), not computeIfAbsent(), to avoid cast fails.
             // The upstream code cannot ever succeed as the cast from BiFunction to Function
             // always fails.
             // legacyStrings.computeIfAbsent((String) key,
@@ -825,7 +825,7 @@
             legacyStrings.compute((String) key,
                     (BiFunction<? super String, ? super String, ? extends String>)
                             remappingFunction);
-            // END Android-changed: use compute() instead of computeIfAbsent() to avoid cast fails
+            // END Android-changed: use compute(), not computeIfAbsent(), to avoid cast fails.
         }
         return super.compute(key, remappingFunction);
     }
@@ -887,7 +887,7 @@
         serviceSet = null;
         super.clear();
         putId();
-        // Android-added: Provider registration
+        // Android-added: Provider registration.
         if (registered) {
           Security.increaseVersion();
         }
@@ -1192,7 +1192,7 @@
             String key = type + "." + algorithm + " " + entry.getKey();
             super.put(key, entry.getValue());
         }
-        // Android-added: Provider registration
+        // Android-added: Provider registration.
         if (registered) {
             Security.increaseVersion();
         }
@@ -1214,7 +1214,7 @@
             String key = type + "." + algorithm + " " + entry.getKey();
             super.remove(key);
         }
-        // Android-added: Provider registration
+        // Android-added: Provider registration.
         if (registered) {
           Security.increaseVersion();
         }
@@ -1883,7 +1883,7 @@
 
     }
 
-    // BEGIN Android-added: Provider registration
+    // BEGIN Android-added: Provider registration.
     /**
      * @hide
      */
@@ -1922,5 +1922,5 @@
         // stored field, if the services didn't change in the meantime.
         getServices();
     }
-    // END Android-added: Provider registration
+    // END Android-added: Provider registration.
 }
diff --git a/ojluni/src/main/java/java/security/SecureRandom.java b/ojluni/src/main/java/java/security/SecureRandom.java
index 79e3840..4da178d 100644
--- a/ojluni/src/main/java/java/security/SecureRandom.java
+++ b/ojluni/src/main/java/java/security/SecureRandom.java
@@ -151,7 +151,7 @@
      * the {@link Security#getProviders() Security.getProviders()} method.
      *
      * <p> See the SecureRandom section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecureRandom">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard RNG algorithm names.
      *
@@ -189,7 +189,7 @@
      * the {@link Security#getProviders() Security.getProviders()} method.
      *
      * <p> See the SecureRandom section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecureRandom">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard RNG algorithm names.
      *
@@ -278,7 +278,7 @@
      *
      * @param algorithm the name of the RNG algorithm.
      * See the SecureRandom section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecureRandom">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard RNG algorithm names.
      *
@@ -321,7 +321,7 @@
      *
      * @param algorithm the name of the RNG algorithm.
      * See the SecureRandom section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecureRandom">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard RNG algorithm names.
      *
@@ -369,7 +369,7 @@
      *
      * @param algorithm the name of the RNG algorithm.
      * See the SecureRandom section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecureRandom">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard RNG algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/Security.java b/ojluni/src/main/java/java/security/Security.java
index bb853a6..ab1cc1d 100644
--- a/ojluni/src/main/java/java/security/Security.java
+++ b/ojluni/src/main/java/java/security/Security.java
@@ -207,7 +207,7 @@
     /**
      * Gets a specified property for an algorithm. The algorithm name
      * should be a standard name. See the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -443,7 +443,7 @@
      * </ul>
      *
      * <p> See the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard cryptographic service names, standard
      * algorithm names and standard attribute names.
diff --git a/ojluni/src/main/java/java/security/Signature.java b/ojluni/src/main/java/java/security/Signature.java
index bbe9640..a09409f 100644
--- a/ojluni/src/main/java/java/security/Signature.java
+++ b/ojluni/src/main/java/java/security/Signature.java
@@ -238,7 +238,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature">
  * Signature section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -298,7 +298,7 @@
      *
      * @param algorithm the standard string name of the algorithm.
      * See the Signature section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      */
@@ -338,7 +338,7 @@
      *
      * @param algorithm the standard name of the algorithm requested.
      * See the Signature section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -464,7 +464,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the Signature section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -516,7 +516,7 @@
      *
      * @param algorithm the name of the algorithm requested.
      * See the Signature section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/cert/CertPath.java b/ojluni/src/main/java/java/security/cert/CertPath.java
index 8717f94..fa88c39 100644
--- a/ojluni/src/main/java/java/security/cert/CertPath.java
+++ b/ojluni/src/main/java/java/security/cert/CertPath.java
@@ -91,7 +91,7 @@
  * <li>{@code PkiPath}</li>
  * </ul>
  * These encodings are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathEncodings">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathEncodings">
  * CertPath Encodings section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  * Consult the release documentation for your implementation to see if any
diff --git a/ojluni/src/main/java/java/security/cert/CertPathBuilder.java b/ojluni/src/main/java/java/security/cert/CertPathBuilder.java
index 891d86c..2d54292 100644
--- a/ojluni/src/main/java/java/security/cert/CertPathBuilder.java
+++ b/ojluni/src/main/java/java/security/cert/CertPathBuilder.java
@@ -83,7 +83,7 @@
  * </table>
  *
  * This algorithm is described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathBuilder">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathBuilder">
  * CertPathBuilder section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  * Consult the release documentation for your implementation to see if any
@@ -155,7 +155,7 @@
      *
      * @param algorithm the name of the requested {@code CertPathBuilder}
      *  algorithm.  See the CertPathBuilder section in the <a href=
-     *  "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathBuilder">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathBuilder">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -190,7 +190,7 @@
      *
      * @param algorithm the name of the requested {@code CertPathBuilder}
      *  algorithm.  See the CertPathBuilder section in the <a href=
-     *  "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathBuilder">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathBuilder">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -230,7 +230,7 @@
      *
      * @param algorithm the name of the requested {@code CertPathBuilder}
      *  algorithm.  See the CertPathBuilder section in the <a href=
-     *  "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathBuilder">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathBuilder">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/cert/CertPathValidator.java b/ojluni/src/main/java/java/security/cert/CertPathValidator.java
index 942c5fa..bcb3488 100644
--- a/ojluni/src/main/java/java/security/cert/CertPathValidator.java
+++ b/ojluni/src/main/java/java/security/cert/CertPathValidator.java
@@ -85,7 +85,7 @@
  * </table>
  *
  * This algorithm is described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathValidator">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathValidator">
  * CertPathValidator section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -154,7 +154,7 @@
      *
      * @param algorithm the name of the requested {@code CertPathValidator}
      *  algorithm. See the CertPathValidator section in the <a href=
-     *  "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathValidator">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathValidator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -189,7 +189,7 @@
      *
      * @param algorithm the name of the requested {@code CertPathValidator}
      *  algorithm. See the CertPathValidator section in the <a href=
-     *  "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathValidator">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathValidator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -230,7 +230,7 @@
      *
      * @param algorithm the name of the requested {@code CertPathValidator}
      * algorithm. See the CertPathValidator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathValidator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathValidator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/java/security/cert/CertStore.java b/ojluni/src/main/java/java/security/cert/CertStore.java
index 6eaee5c..7cfd0d2 100644
--- a/ojluni/src/main/java/java/security/cert/CertStore.java
+++ b/ojluni/src/main/java/java/security/cert/CertStore.java
@@ -77,7 +77,7 @@
  * </table>
  *
  * This type is described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertStore">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertStore">
  * CertStore section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -212,7 +212,7 @@
      *
      * @param type the name of the requested {@code CertStore} type.
      * See the CertStore section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertStore">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertStore">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard types.
      *
@@ -272,7 +272,7 @@
      *
      * @param type the requested {@code CertStore} type.
      * See the CertStore section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertStore">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertStore">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard types.
      *
@@ -330,7 +330,7 @@
      *
      * @param type the requested {@code CertStore} type.
      * See the CertStore section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertStore">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertStore">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard types.
      *
diff --git a/ojluni/src/main/java/java/security/cert/Certificate.java b/ojluni/src/main/java/java/security/cert/Certificate.java
index 1054498..4056416 100644
--- a/ojluni/src/main/java/java/security/cert/Certificate.java
+++ b/ojluni/src/main/java/java/security/cert/Certificate.java
@@ -74,7 +74,7 @@
      *
      * @param type the standard name of the certificate type.
      * See the CertificateFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertificateFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertificateFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard certificate types.
      */
diff --git a/ojluni/src/main/java/java/security/cert/CertificateFactory.java b/ojluni/src/main/java/java/security/cert/CertificateFactory.java
index a47b788..e34fddd 100644
--- a/ojluni/src/main/java/java/security/cert/CertificateFactory.java
+++ b/ojluni/src/main/java/java/security/cert/CertificateFactory.java
@@ -127,9 +127,9 @@
  * </table>
  *
  * The type and encodings are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertificateFactory">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertificateFactory">
  * CertificateFactory section</a> and the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathEncodings">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathEncodings">
  * CertPath Encodings section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -188,7 +188,7 @@
      *
      * @param type the name of the requested certificate type.
      * See the CertificateFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertificateFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertificateFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard certificate types.
      *
@@ -226,7 +226,7 @@
      *
      * @param type the certificate type.
      * See the CertificateFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertificateFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertificateFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard certificate types.
      *
@@ -272,7 +272,7 @@
      *
      * @param type the certificate type.
      * See the CertificateFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertificateFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertificateFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard certificate types.
      * @param provider the provider.
@@ -370,7 +370,7 @@
      * Returns an iteration of the {@code CertPath} encodings supported
      * by this certificate factory, with the default encoding first. See
      * the CertPath Encodings section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathEncodings">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathEncodings">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard encoding names and their formats.
      * <p>
@@ -410,7 +410,7 @@
      * the data read from the {@code InputStream} inStream. The data
      * is assumed to be in the specified encoding. See
      * the CertPath Encodings section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathEncodings">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathEncodings">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard encoding names and their formats.
      *
diff --git a/ojluni/src/main/java/java/security/cert/CertificateFactorySpi.java b/ojluni/src/main/java/java/security/cert/CertificateFactorySpi.java
index 35aee84..691777d 100644
--- a/ojluni/src/main/java/java/security/cert/CertificateFactorySpi.java
+++ b/ojluni/src/main/java/java/security/cert/CertificateFactorySpi.java
@@ -183,7 +183,7 @@
      * Returns an iteration of the {@code CertPath} encodings supported
      * by this certificate factory, with the default encoding first. See
      * the CertPath Encodings section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#CertPathEncodings">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#CertPathEncodings">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard encoding names.
      * <p>
diff --git a/ojluni/src/main/java/java/security/cert/package-info.java b/ojluni/src/main/java/java/security/cert/package-info.java
index 58f5fb7..4830455 100644
--- a/ojluni/src/main/java/java/security/cert/package-info.java
+++ b/ojluni/src/main/java/java/security/cert/package-info.java
@@ -32,14 +32,14 @@
  * <h2>Package Specification</h2>
  *
  * <ul>
- *   <li><a href="{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html">
+ *   <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">
  *     <b>Java&trade;
  *     Cryptography Architecture (JCA) Reference Guide</b></a>
  *   <li>RFC 5280: Internet X.509 Public Key Infrastructure Certificate and
  *     Certificate Revocation List (CRL) Profile
  *   <li>RFC 2560: X.509 Internet Public Key Infrastructure Online Certificate
  *     Status Protocol - OCSP
- *   <li><a href="{@docRoot}/../technotes/guides/security/StandardNames.html">
+ *   <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">
  *     <b>Java&trade;
  *     Cryptography Architecture Standard Algorithm Name
  *     Documentation</b></a></li>
@@ -52,10 +52,10 @@
  *   <li><a href="http://www.ietf.org/rfc/rfc5280.txt">
  *     http://www.ietf.org/rfc/rfc5280.txt</a>
  *   <li><a href=
- *     "{@docRoot}/../technotes/guides/security/certpath/CertPathProgGuide.html">
+ *     "https://docs.oracle.com/javase/8/docs/technotes/guides/security/certpath/CertPathProgGuide.html">
  *     <b>Java&trade;
  *     PKI Programmer's Guide</b></a>
- *   <li><a href="{@docRoot}/../technotes/guides/security/cert3.html">
+ *   <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/cert3.html">
  *     <b>X.509 Certificates and Certificate Revocation Lists (CRLs)</b></a>
  * </ul>
  *
diff --git a/ojluni/src/main/java/java/security/interfaces/package-info.java b/ojluni/src/main/java/java/security/interfaces/package-info.java
index 54c9397..8942340 100644
--- a/ojluni/src/main/java/java/security/interfaces/package-info.java
+++ b/ojluni/src/main/java/java/security/interfaces/package-info.java
@@ -43,7 +43,7 @@
  * to these cryptographic provider developer guides:
  * <ul>
  *   <li><a href=
- *     "{@docRoot}/../technotes/guides/security/crypto/HowToImplAProvider.html">
+ *     "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
  *     <b>How to Implement a Provider for the
  *     Java&trade; Cryptography Architecture
  *     </b></a></li>
@@ -63,7 +63,7 @@
  * <ul>
  *   <li>
  *     <a href=
- *       "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html">
+ *       "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">
  *       <b>Java&trade;
  *       Cryptography Architecture API Specification and Reference
  *       </b></a></li>
diff --git a/ojluni/src/main/java/java/security/package-info.java b/ojluni/src/main/java/java/security/package-info.java
index 2c8205b..60e9b4b 100644
--- a/ojluni/src/main/java/java/security/package-info.java
+++ b/ojluni/src/main/java/java/security/package-info.java
@@ -46,14 +46,14 @@
  * <h2>Package Specification</h2>
  *
  * <ul>
- *   <li><a href="{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html">
+ *   <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">
  *     <b>Java&trade;
  *     Cryptography Architecture (JCA) Reference Guide</b></a></li>
  *
  *   <li>PKCS #8: Private-Key Information Syntax Standard, Version 1.2,
  *     November 1993</li>
  *
- *   <li><a href="{@docRoot}/../technotes/guides/security/StandardNames.html">
+ *   <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">
  *     <b>Java&trade;
  *     Cryptography Architecture Standard Algorithm Name
  *     Documentation</b></a></li>
@@ -64,44 +64,44 @@
  * For further documentation, please see:
  * <ul>
  *   <li><a href=
- *     "{@docRoot}/../technotes/guides/security/spec/security-spec.doc.html">
+ *     "https://docs.oracle.com/javase/8/docs/technotes/guides/security/spec/security-spec.doc.html">
  *     <b>Java&trade;
  *     SE Platform Security Architecture</b></a></li>
  *
  *   <li><a href=
- *     "{@docRoot}/../technotes/guides/security/crypto/HowToImplAProvider.html">
+ *     "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
  *     <b>How to Implement a Provider in the
  *     Java&trade; Cryptography Architecture
  *     </b></a></li>
  *
  *   <li><a href=
- *     "{@docRoot}/../technotes/guides/security/PolicyFiles.html"><b>
+ *     "https://docs.oracle.com/javase/8/docs/technotes/guides/security/PolicyFiles.html"><b>
  *     Default Policy Implementation and Policy File Syntax
  *     </b></a></li>
  *
  *   <li><a href=
- *     "{@docRoot}/../technotes/guides/security/permissions.html"><b>
+ *     "https://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html"><b>
  *     Permissions in the
  *     Java&trade; SE Development Kit (JDK)
  *     </b></a></li>
  *
  *   <li><a href=
- *     "{@docRoot}/../technotes/guides/security/SecurityToolsSummary.html"><b>
+ *     "https://docs.oracle.com/javase/8/docs/technotes/guides/security/SecurityToolsSummary.html"><b>
  *     Summary of Tools for
  *     Java&trade; Platform Security
  *     </b></a></li>
  *
  *   <li><b>keytool</b>
- *     (<a href="{@docRoot}/../technotes/tools/unix/keytool.html">
+ *     (<a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html">
  *       for Solaris/Linux</a>)
- *     (<a href="{@docRoot}/../technotes/tools/windows/keytool.html">
+ *     (<a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html">
  *       for Windows</a>)
  *     </li>
  *
  *   <li><b>jarsigner</b>
- *     (<a href="{@docRoot}/../technotes/tools/unix/jarsigner.html">
+ *     (<a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jarsigner.html">
  *       for Solaris/Linux</a>)
- *     (<a href="{@docRoot}/../technotes/tools/windows/jarsigner.html">
+ *     (<a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jarsigner.html">
  *       for Windows</a>)
  *     </li>
  *
diff --git a/ojluni/src/main/java/java/security/spec/package-info.java b/ojluni/src/main/java/java/security/spec/package-info.java
index cb39308..68f0b8e 100644
--- a/ojluni/src/main/java/java/security/spec/package-info.java
+++ b/ojluni/src/main/java/java/security/spec/package-info.java
@@ -56,13 +56,13 @@
  * <ul>
  *   <li>
  *     <a href=
- *       "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html">
+ *       "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">
  *       <b>Java&trade;
  *       Cryptography Architecture API Specification and Reference
  *       </b></a></li>
  *   <li>
  *     <a href=
- *       "{@docRoot}/../technotes/guides/security/crypto/HowToImplAProvider.html">
+ *       "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
  *       <b>How to Implement a Provider for the
  *       Java&trade; Cryptography Architecture
  *       </b></a></li>
diff --git a/ojluni/src/main/java/java/text/CollationElementIterator.java b/ojluni/src/main/java/java/text/CollationElementIterator.java
index fede747..17ce8c7 100644
--- a/ojluni/src/main/java/java/text/CollationElementIterator.java
+++ b/ojluni/src/main/java/java/text/CollationElementIterator.java
@@ -107,8 +107,7 @@
      * Null order which indicates the end of string is reached by the
      * cursor.
      */
-    // Android-changed: use ICU CollationElementIterator constant.
-    public final static int NULLORDER = android.icu.text.CollationElementIterator.NULLORDER;
+    public final static int NULLORDER = 0xffffffff;
 
     // Android-removed: internal constructors.
 
diff --git a/ojluni/src/main/java/java/text/DateFormat.java b/ojluni/src/main/java/java/text/DateFormat.java
index d7d5081..8622c2f 100644
--- a/ojluni/src/main/java/java/text/DateFormat.java
+++ b/ojluni/src/main/java/java/text/DateFormat.java
@@ -600,13 +600,21 @@
      */
     public static Boolean is24Hour;
 
+    // BEGIN Android-changed: Improve javadoc for stable SystemApi.
     /**
-     * Override the time formatting behavior for SHORT and MEDIUM time formats.
-     * {@code null}: use Locale default. {@code true}: force 24-hour format.
-     * {@code false} force 12-hour format.
+     * Override the time formatting behavior for {@link #SHORT} and {@link #MEDIUM} time formats.
+     * Accepts one of the following:
+     * <ul>
+     *   <li>{@code null}: use Locale default/li>
+     *   <li>{@code true}: force 24-hour format</li>
+     *   <li>{@code false} force 12-hour format</li>
+     * </ul>
+     *
+     * @param is24Hour whether to use 24-hour format or not. {@code null} uses locale default.
      *
      * @hide for internal use only.
      */
+    // END Android-changed: Improve javadoc for stable SystemApi.
     public static final void set24HourTimePref(Boolean is24Hour) {
         DateFormat.is24Hour = is24Hour;
     }
diff --git a/ojluni/src/main/java/java/text/DateFormatSymbols.java b/ojluni/src/main/java/java/text/DateFormatSymbols.java
index 5216928..de10644 100644
--- a/ojluni/src/main/java/java/text/DateFormatSymbols.java
+++ b/ojluni/src/main/java/java/text/DateFormatSymbols.java
@@ -101,7 +101,7 @@
  */
 public class DateFormatSymbols implements Serializable, Cloneable {
 
-    // Android-changed: Removed reference to DateFormatSymbolsProvider but suggested getInstance()
+    // Android-changed: Removed reference to DateFormatSymbolsProvider but suggested getInstance().
     // be used instead in case Android supports it in future.
     /**
      * Construct a DateFormatSymbols object by loading format data from
@@ -123,7 +123,7 @@
         initializeData(Locale.getDefault(Locale.Category.FORMAT));
     }
 
-    // Android-changed: Removed reference to DateFormatSymbolsProvider but suggested getInstance()
+    // Android-changed: Removed reference to DateFormatSymbolsProvider but suggested getInstance().
     // be used instead in case Android supports it in future.
     /**
      * Construct a DateFormatSymbols object by loading format data from
@@ -229,7 +229,7 @@
      * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
      * All locales use the same these unlocalized pattern characters.
      */
-    // Android-changed: Add 'c' (standalone day of week), 'b' (day period),
+    // Android-changed: Add 'c' (standalone day of week), 'b' (day period),.
     //   'B' (flexible day period)
     static final String  patternChars = "GyMdkHmsSEDFwWahKzZYuXLcbB";
 
@@ -258,7 +258,7 @@
     static final int PATTERN_MONTH_STANDALONE     = 22; // L
     // Android-added: Constant for standalone day of week.
     static final int PATTERN_STANDALONE_DAY_OF_WEEK = 23; // c
-    // Android-added: Constant for pattern letter 'b', 'B'
+    // Android-added: Constant for pattern letter 'b', 'B'.
     static final int PATTERN_DAY_PERIOD = 24; // b
     static final int PATTERN_FLEXIBLE_DAY_PERIOD = 25; // B
 
@@ -848,7 +848,7 @@
         // END Android-changed: Use ICU data and move cache handling to getCachedInstance().
     }
 
-    // Android-removed: toOneBasedArray(String[])
+    // Android-removed: toOneBasedArray(String[]).
 
     // BEGIN Android-added: initializeSupplementaryData(LocaleData) for tiny and standalone fields.
     private void initializeSupplementaryData(LocaleData localeData) {
@@ -921,10 +921,11 @@
         }
         return zoneStrings;
     }
+    // END Android-changed: extract initialization of zoneStrings to separate method.
 
     private String[][] getZoneStringsImpl(boolean needsCopy) {
+        // Android-changed: use helper method to initialize zoneStrings.
         String[][] zoneStrings = internalZoneStrings();
-        // END Android-changed: extract initialization of zoneStrings to separate method.
 
         if (!needsCopy) {
             return zoneStrings;
diff --git a/ojluni/src/main/java/java/text/DecimalFormat.java b/ojluni/src/main/java/java/text/DecimalFormat.java
index a2a37b1..1dc430c 100644
--- a/ojluni/src/main/java/java/text/DecimalFormat.java
+++ b/ojluni/src/main/java/java/text/DecimalFormat.java
@@ -39,7 +39,6 @@
 
 package java.text;
 
-import android.icu.impl.number.DecimalFormatProperties.ParseMode;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -55,6 +54,8 @@
 import libcore.icu.LocaleData;
 import android.icu.math.MathContext;
 
+import com.android.icu.text.CompatibleDecimalFormatFactory;
+
 /**
  * <code>DecimalFormat</code> is a concrete subclass of
  * <code>NumberFormat</code> that formats decimal numbers. It has a variety of
@@ -487,10 +488,9 @@
      * {@link #icuDecimalFormat} in the process. This should only be called from constructors.
      */
     private void initPattern(String pattern) {
-        this.icuDecimalFormat =  new android.icu.text.DecimalFormat(pattern,
-                symbols.getIcuDecimalFormatSymbols());
         // Android-changed: Compatibility mode for j.t.DecimalFormat. http://b/112355520
-        icuDecimalFormat.setParseStrictMode(ParseMode.JAVA_COMPATIBILITY);
+        this.icuDecimalFormat = CompatibleDecimalFormatFactory.create(pattern,
+                symbols.getIcuDecimalFormatSymbols());
         updateFieldsFromIcu();
     }
 
@@ -897,7 +897,7 @@
         return format(number, result, fieldPosition.getFieldDelegate());
         */
         FieldPosition icuFieldPosition = getIcuFieldPosition(fieldPosition);
-        icuDecimalFormat.format(number, result, fieldPosition);
+        icuDecimalFormat.format(number, result, icuFieldPosition);
         fieldPosition.setBeginIndex(icuFieldPosition.getBeginIndex());
         fieldPosition.setEndIndex(icuFieldPosition.getEndIndex());
         return result;
@@ -966,7 +966,7 @@
         return format(number, result, fieldPosition.getFieldDelegate(), false);
         */
         FieldPosition icuFieldPosition = getIcuFieldPosition(fieldPosition);
-        icuDecimalFormat.format(number, result, fieldPosition);
+        icuDecimalFormat.format(number, result, icuFieldPosition);
         fieldPosition.setBeginIndex(icuFieldPosition.getBeginIndex());
         fieldPosition.setEndIndex(icuFieldPosition.getEndIndex());
         return result;
diff --git a/ojluni/src/main/java/java/text/Normalizer.java b/ojluni/src/main/java/java/text/Normalizer.java
index 8c22547..f1e77ff 100644
--- a/ojluni/src/main/java/java/text/Normalizer.java
+++ b/ojluni/src/main/java/java/text/Normalizer.java
@@ -38,6 +38,10 @@
 
 package java.text;
 
+import android.icu.text.Normalizer2;
+
+import java.util.function.Supplier;
+
 /**
  * This class provides the method <code>normalize</code> which transforms Unicode
  * text into an equivalent composed or decomposed form, allowing for easier
@@ -123,27 +127,27 @@
         /**
          * Canonical decomposition.
          */
-        NFD(android.icu.text.Normalizer.NFD),
+        NFD(Normalizer2::getNFDInstance),
 
         /**
          * Canonical decomposition, followed by canonical composition.
          */
-        NFC(android.icu.text.Normalizer.NFC),
+        NFC(Normalizer2::getNFCInstance),
 
         /**
          * Compatibility decomposition.
          */
-        NFKD(android.icu.text.Normalizer.NFKD),
+        NFKD(Normalizer2::getNFKDInstance),
 
         /**
          * Compatibility decomposition, followed by canonical composition.
          */
-        NFKC(android.icu.text.Normalizer.NFKC);
+        NFKC(Normalizer2::getNFKCInstance);
 
-        private final android.icu.text.Normalizer.Mode icuMode;
+        private final Supplier<Normalizer2> icuNormalizer;
 
-        Form(android.icu.text.Normalizer.Mode icuMode) {
-            this.icuMode = icuMode;
+        Form(Supplier<Normalizer2> icuNormalizer) {
+            this.icuNormalizer = icuNormalizer;
         }
     }
     // END Android-changed: remove static modifier and add mapping to equivalent ICU values.
@@ -164,7 +168,7 @@
      */
     public static String normalize(CharSequence src, Form form) {
         // Android-changed: Switched to ICU.
-        return android.icu.text.Normalizer.normalize(src.toString(), form.icuMode);
+        return form.icuNormalizer.get().normalize(src);
     }
 
     /**
@@ -182,6 +186,6 @@
      */
     public static boolean isNormalized(CharSequence src, Form form) {
         // Android-changed: Switched to ICU.
-        return android.icu.text.Normalizer.isNormalized(src.toString(), form.icuMode, 0);
+        return form.icuNormalizer.get().isNormalized(src);
     }
 }
diff --git a/ojluni/src/main/java/java/text/SimpleDateFormat.java b/ojluni/src/main/java/java/text/SimpleDateFormat.java
index ca5a504..36a4601 100644
--- a/ojluni/src/main/java/java/text/SimpleDateFormat.java
+++ b/ojluni/src/main/java/java/text/SimpleDateFormat.java
@@ -39,27 +39,22 @@
 
 package java.text;
 
-import android.icu.text.TimeZoneFormat;
 import android.icu.text.TimeZoneNames;
 import android.icu.util.ULocale;
 
+import com.android.icu.text.ExtendedTimeZoneNames;
+import com.android.icu.text.ExtendedTimeZoneNames.Match;
+
 import java.io.IOException;
 import java.io.InvalidObjectException;
 import java.io.ObjectInputStream;
-import java.util.Arrays;
 import java.util.Calendar;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
-import java.util.EnumSet;
 import java.util.GregorianCalendar;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.NavigableMap;
-import java.util.Set;
 import java.util.SimpleTimeZone;
-import java.util.SortedMap;
 import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -71,6 +66,8 @@
 
 // Android-changed: Added supported API level, removed unnecessary <br>
 // Android-changed: Clarified info about X symbol time zone parsing
+// Android-changed: Changed MMMMM to MMMM in month format example (ICU behavior).
+// http://b/147860740
 /**
  * <code>SimpleDateFormat</code> is a concrete class for formatting and
  * parsing dates in a locale-sensitive manner. It allows for formatting
@@ -422,7 +419,7 @@
  *         <td><code>"K:mm a, z"</code>
  *         <td><code>0:08 PM, PDT</code>
  *     <tr style="background-color: rgb(238, 238, 255);">
- *         <td><code>"yyyyy.MMMMM.dd GGG hh:mm aaa"</code>
+ *         <td><code>"yyyyy.MMMM.dd GGG hh:mm aaa"</code>
  *         <td><code>02001.July.04 AD 12:08 PM</code>
  *     <tr>
  *         <td><code>"EEE, d MMM yyyy HH:mm:ss Z"</code>
@@ -594,7 +591,7 @@
     /**
      * ICU TimeZoneNames used to format and parse time zone names.
      */
-    private transient TimeZoneNames timeZoneNames;
+    private transient ExtendedTimeZoneNames timeZoneNames;
 
     /**
      * Constructs a <code>SimpleDateFormat</code> using the default pattern and
@@ -1997,27 +1994,16 @@
         }
     }
 
-    private TimeZoneNames getTimeZoneNames() {
+    private ExtendedTimeZoneNames getExtendedTimeZoneNames() {
         if (timeZoneNames == null) {
-            timeZoneNames = TimeZoneNames.getInstance(locale);
+            timeZoneNames = ExtendedTimeZoneNames.getInstance(ULocale.forLocale(locale));
         }
         return timeZoneNames;
     }
 
-    /**
-     * The set of name types accepted when parsing time zone names.
-     */
-    private static final EnumSet<TimeZoneNames.NameType> NAME_TYPES =
-            EnumSet.of(TimeZoneNames.NameType.LONG_GENERIC, TimeZoneNames.NameType.LONG_STANDARD,
-                    TimeZoneNames.NameType.LONG_DAYLIGHT, TimeZoneNames.NameType.SHORT_GENERIC,
-                    TimeZoneNames.NameType.SHORT_STANDARD, TimeZoneNames.NameType.SHORT_DAYLIGHT);
-
-    /**
-     * Time zone name types that indicate daylight saving time.
-     */
-    private static final Set<TimeZoneNames.NameType> DST_NAME_TYPES =
-            Collections.unmodifiableSet(EnumSet.of(
-                    TimeZoneNames.NameType.LONG_DAYLIGHT, TimeZoneNames.NameType.SHORT_DAYLIGHT));
+    private TimeZoneNames getTimeZoneNames() {
+        return getExtendedTimeZoneNames().getTimeZoneNames();
+    }
 
     /**
      * Parses the time zone string using the ICU4J class {@link TimeZoneNames}.
@@ -2025,71 +2011,26 @@
     private int subParseZoneStringFromICU(String text, int start, CalendarBuilder calb) {
         String currentTimeZoneID = android.icu.util.TimeZone.getCanonicalID(getTimeZone().getID());
 
-        TimeZoneNames tzNames = getTimeZoneNames();
-        TimeZoneNames.MatchInfo bestMatch = null;
-        // The MetaZones associated with the current time zone are needed in two places, both of
-        // which are avoided in some cases, so they are computed lazily.
-        Set<String> currentTzMetaZoneIds = null;
-
-        Collection<TimeZoneNames.MatchInfo> matches = tzNames.find(text, start, NAME_TYPES);
-        for (TimeZoneNames.MatchInfo match : matches) {
-            if (bestMatch == null || bestMatch.matchLength() < match.matchLength()) {
-                bestMatch = match;
-            } else if (bestMatch.matchLength() == match.matchLength()) {
-                if (currentTimeZoneID.equals(match.tzID())) {
-                    // Prefer the currently set timezone over other matches, even if they are
-                    // the same length.
-                    bestMatch = match;
-                    break;
-                } else if (match.mzID() != null) {
-                    if (currentTzMetaZoneIds == null) {
-                        currentTzMetaZoneIds =
-                                tzNames.getAvailableMetaZoneIDs(currentTimeZoneID);
-                    }
-                    if (currentTzMetaZoneIds.contains(match.mzID())) {
-                        bestMatch = match;
-                        break;
-                    }
-                }
-            }
-        }
-        if (bestMatch == null) {
+        Match matchedName = getExtendedTimeZoneNames().matchName(text, start, currentTimeZoneID);
+        if (matchedName == null) {
             // No match found, return error.
             return -start;
         }
 
-        String tzId = bestMatch.tzID();
-        if (tzId == null) {
-            if (currentTzMetaZoneIds == null) {
-                currentTzMetaZoneIds = tzNames.getAvailableMetaZoneIDs(currentTimeZoneID);
-            }
-            if (currentTzMetaZoneIds.contains(bestMatch.mzID())) {
-                tzId = currentTimeZoneID;
-            } else {
-                // Match was for a meta-zone, find the matching reference zone.
-                ULocale uLocale = ULocale.forLocale(locale);
-                String region = uLocale.getCountry();
-                if (region.length() == 0) {
-                    uLocale = ULocale.addLikelySubtags(uLocale);
-                    region = uLocale.getCountry();
-                }
-                tzId = tzNames.getReferenceZoneID(bestMatch.mzID(), region);
-            }
-        }
-
+        String tzId = matchedName.getTzId();
         TimeZone newTimeZone = TimeZone.getTimeZone(tzId);
         if (!currentTimeZoneID.equals(tzId)) {
             setTimeZone(newTimeZone);
         }
 
         // Same logic as in subParseZoneStringFromSymbols, see below for details.
-        boolean isDst = DST_NAME_TYPES.contains(bestMatch.nameType());
+        boolean isDst = matchedName.isDst();
         int dstAmount = isDst ? newTimeZone.getDSTSavings() : 0;
         if (!isDst || dstAmount != 0) {
             calb.clear(Calendar.ZONE_OFFSET).set(Calendar.DST_OFFSET, dstAmount);
         }
 
-        return bestMatch.matchLength() + start;
+        return matchedName.getMatchLength() + start;
     }
 
     /**
diff --git a/ojluni/src/main/java/java/time/Duration.java b/ojluni/src/main/java/java/time/Duration.java
index 8637c40..ef1e802 100644
--- a/ojluni/src/main/java/java/time/Duration.java
+++ b/ojluni/src/main/java/java/time/Duration.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -61,6 +61,8 @@
  */
 package java.time;
 
+import static java.time.LocalTime.MINUTES_PER_HOUR;
+import static java.time.LocalTime.NANOS_PER_MILLI;
 import static java.time.LocalTime.NANOS_PER_SECOND;
 import static java.time.LocalTime.SECONDS_PER_DAY;
 import static java.time.LocalTime.SECONDS_PER_HOUR;
@@ -223,7 +225,7 @@
      * This method allows an arbitrary number of nanoseconds to be passed in.
      * The factory will alter the values of the second and nanosecond in order
      * to ensure that the stored nanosecond is in the range 0 to 999,999,999.
-     * For example, the following will result in the exactly the same duration:
+     * For example, the following will result in exactly the same duration:
      * <pre>
      *  Duration.ofSeconds(3, 1);
      *  Duration.ofSeconds(4, -999_999_999);
@@ -954,7 +956,7 @@
         if (multiplicand == 1) {
             return this;
         }
-        return create(toSeconds().multiply(BigDecimal.valueOf(multiplicand)));
+        return create(toBigDecimalSeconds().multiply(BigDecimal.valueOf(multiplicand)));
      }
 
     /**
@@ -973,16 +975,34 @@
         if (divisor == 1) {
             return this;
         }
-        return create(toSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN));
+        return create(toBigDecimalSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN));
      }
 
     /**
+     * Returns number of whole times a specified Duration occurs within this Duration.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @param divisor the value to divide the duration by, positive or negative, not null
+     * @return number of whole times, rounded toward zero, a specified
+     *         {@code Duration} occurs within this Duration, may be negative
+     * @throws ArithmeticException if the divisor is zero, or if numeric overflow occurs
+     * @since 9
+     */
+    public long dividedBy(Duration divisor) {
+        Objects.requireNonNull(divisor, "divisor");
+        BigDecimal dividendBigD = toBigDecimalSeconds();
+        BigDecimal divisorBigD = divisor.toBigDecimalSeconds();
+        return dividendBigD.divideToIntegralValue(divisorBigD).longValueExact();
+    }
+
+    /**
      * Converts this duration to the total length in seconds and
      * fractional nanoseconds expressed as a {@code BigDecimal}.
      *
      * @return the total length of the duration in seconds, with a scale of 9, not null
      */
-    private BigDecimal toSeconds() {
+    private BigDecimal toBigDecimalSeconds() {
         return BigDecimal.valueOf(seconds).add(BigDecimal.valueOf(nanos, 9));
     }
 
@@ -1149,6 +1169,20 @@
     }
 
     /**
+     * Gets the number of seconds in this duration.
+     * <p>
+     * This returns the total number of whole seconds in the duration.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @return the whole seconds part of the length of the duration, positive or negative
+     * @since 9
+     */
+    public long toSeconds() {
+        return seconds;
+    }
+
+    /**
      * Converts this duration to the total length in milliseconds.
      * <p>
      * If this duration is too large to fit in a {@code long} milliseconds, then an
@@ -1182,6 +1216,150 @@
         return totalNanos;
     }
 
+    /**
+     * Extracts the number of days in the duration.
+     * <p>
+     * This returns the total number of days in the duration by dividing the
+     * number of seconds by 86400.
+     * This is based on the standard definition of a day as 24 hours.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @return the number of days in the duration, may be negative
+     * @since 9
+     */
+    public long toDaysPart(){
+        return seconds / SECONDS_PER_DAY;
+    }
+
+    /**
+     * Extracts the number of hours part in the duration.
+     * <p>
+     * This returns the number of remaining hours when dividing {@link #toHours}
+     * by hours in a day.
+     * This is based on the standard definition of a day as 24 hours.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @return the number of hours part in the duration, may be negative
+     * @since 9
+     */
+    public int toHoursPart(){
+        return (int) (toHours() % 24);
+    }
+
+    /**
+     * Extracts the number of minutes part in the duration.
+     * <p>
+     * This returns the number of remaining minutes when dividing {@link #toMinutes}
+     * by minutes in an hour.
+     * This is based on the standard definition of an hour as 60 minutes.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @return the number of minutes parts in the duration, may be negative
+     * @since 9
+     */
+    public int toMinutesPart(){
+        return (int) (toMinutes() % MINUTES_PER_HOUR);
+    }
+
+    /**
+     * Extracts the number of seconds part in the duration.
+     * <p>
+     * This returns the remaining seconds when dividing {@link #toSeconds}
+     * by seconds in a minute.
+     * This is based on the standard definition of a minute as 60 seconds.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @return the number of seconds parts in the duration, may be negative
+     * @since 9
+     */
+    public int toSecondsPart(){
+        return (int) (seconds % SECONDS_PER_MINUTE);
+    }
+
+    /**
+     * Extracts the number of milliseconds part of the duration.
+     * <p>
+     * This returns the milliseconds part by dividing the number of nanoseconds by 1,000,000.
+     * The length of the duration is stored using two fields - seconds and nanoseconds.
+     * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to
+     * the length in seconds.
+     * The total duration is defined by calling {@link #getNano()} and {@link #getSeconds()}.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @return the number of milliseconds part of the duration.
+     * @since 9
+     */
+    public int toMillisPart(){
+        return nanos / 1000_000;
+    }
+
+    /**
+     * Get the nanoseconds part within seconds of the duration.
+     * <p>
+     * The length of the duration is stored using two fields - seconds and nanoseconds.
+     * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to
+     * the length in seconds.
+     * The total duration is defined by calling {@link #getNano()} and {@link #getSeconds()}.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @return the nanoseconds within the second part of the length of the duration, from 0 to 999,999,999
+     * @since 9
+     */
+    public int toNanosPart(){
+        return nanos;
+    }
+
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns a copy of this {@code Duration} truncated to the specified unit.
+     * <p>
+     * Truncating the duration returns a copy of the original with conceptual fields
+     * smaller than the specified unit set to zero.
+     * For example, truncating with the {@link ChronoUnit#MINUTES MINUTES} unit will
+     * round down towards zero to the nearest minute, setting the seconds and
+     * nanoseconds to zero.
+     * <p>
+     * The unit must have a {@linkplain TemporalUnit#getDuration() duration}
+     * that divides into the length of a standard day without remainder.
+     * This includes all
+     * {@linkplain ChronoUnit#isTimeBased() time-based units on {@code ChronoUnit}}
+     * and {@link ChronoUnit#DAYS DAYS}. Other ChronoUnits throw an exception.
+     * <p>
+     * This instance is immutable and unaffected by this method call.
+     *
+     * @param unit the unit to truncate to, not null
+     * @return a {@code Duration} based on this duration with the time truncated, not null
+     * @throws DateTimeException if the unit is invalid for truncation
+     * @throws UnsupportedTemporalTypeException if the unit is not supported
+     * @since 9
+     */
+    public Duration truncatedTo(TemporalUnit unit) {
+        Objects.requireNonNull(unit, "unit");
+        if (unit == ChronoUnit.SECONDS && (seconds >= 0 || nanos == 0)) {
+            return new Duration(seconds, 0);
+        } else if (unit == ChronoUnit.NANOS) {
+            return this;
+        }
+        Duration unitDur = unit.getDuration();
+        if (unitDur.getSeconds() > LocalTime.SECONDS_PER_DAY) {
+            throw new UnsupportedTemporalTypeException("Unit is too large to be used for truncation");
+        }
+        long dur = unitDur.toNanos();
+        if ((LocalTime.NANOS_PER_DAY % dur) != 0) {
+            throw new UnsupportedTemporalTypeException("Unit must divide into a standard day without remainder");
+        }
+        long nod = (seconds % LocalTime.SECONDS_PER_DAY) * LocalTime.NANOS_PER_SECOND + nanos;
+        long result = (nod / dur) * dur;
+        return plusNanos(result - nod);
+    }
+
     //-----------------------------------------------------------------------
     /**
      * Compares this duration to the specified {@code Duration}.
@@ -1189,7 +1367,7 @@
      * The comparison is based on the total length of the durations.
      * It is "consistent with equals", as defined by {@link Comparable}.
      *
-     * @param otherDuration  the other duration to compare to, not null
+     * @param otherDuration the other duration to compare to, not null
      * @return the comparator value, negative if less, positive if greater
      */
     @Override
@@ -1207,7 +1385,7 @@
      * <p>
      * The comparison is based on the total length of the durations.
      *
-     * @param otherDuration  the other duration, null returns false
+     * @param otherDuration the other duration, null returns false
      * @return true if the other duration is equal to this one
      */
     @Override
@@ -1240,7 +1418,7 @@
      * <p>
      * The format of the returned string will be {@code PTnHnMnS}, where n is
      * the relevant hours, minutes or seconds part of the duration.
-     * Any fractional seconds are placed after a decimal point i the seconds section.
+     * Any fractional seconds are placed after a decimal point in the seconds section.
      * If a section has a zero value, it is omitted.
      * The hours, minutes and seconds will all have the same sign.
      * <p>
diff --git a/ojluni/src/main/java/java/time/LocalTime.java b/ojluni/src/main/java/java/time/LocalTime.java
index b0b1b4d..22343c1 100644
--- a/ojluni/src/main/java/java/time/LocalTime.java
+++ b/ojluni/src/main/java/java/time/LocalTime.java
@@ -184,6 +184,10 @@
      */
     static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
     /**
+     * Nanos per millisecond.
+     */
+    static final long NANOS_PER_MILLI = 1000_000L;
+    /**
      * Nanos per second.
      */
     static final long NANOS_PER_SECOND = 1000_000_000L;
@@ -268,10 +272,7 @@
         Objects.requireNonNull(clock, "clock");
         // inline OffsetTime factory to avoid creating object and InstantProvider checks
         final Instant now = clock.instant();  // called once
-        ZoneOffset offset = clock.getZone().getRules().getOffset(now);
-        long localSecond = now.getEpochSecond() + offset.getTotalSeconds();  // overflow caught later
-        int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
-        return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + now.getNano());
+        return ofInstant(now, clock.getZone());
     }
 
     //-----------------------------------------------------------------------
@@ -337,6 +338,28 @@
         return create(hour, minute, second, nanoOfSecond);
     }
 
+    /**
+     * Obtains an instance of {@code LocalTime} from an {@code Instant} and zone ID.
+     * <p>
+     * This creates a local time based on the specified instant.
+     * First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
+     * which is simple as there is only one valid offset for each instant.
+     * Then, the instant and offset are used to calculate the local time.
+     *
+     * @param instant  the instant to create the time from, not null
+     * @param zone  the time-zone, which may be an offset, not null
+     * @return the local time, not null
+     * @since 9
+     */
+    public static LocalTime ofInstant(Instant instant, ZoneId zone) {
+        Objects.requireNonNull(instant, "instant");
+        Objects.requireNonNull(zone, "zone");
+        ZoneOffset offset = zone.getRules().getOffset(instant);
+        long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
+        int secsOfDay = Math.floorMod(localSecond, SECONDS_PER_DAY);
+        return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + instant.getNano());
+    }
+
     //-----------------------------------------------------------------------
     /**
      * Obtains an instance of {@code LocalTime} from a second-of-day value.
@@ -591,7 +614,7 @@
      * If the field is a {@link ChronoField} then the query is implemented here.
      * The {@link #isSupported(TemporalField) supported fields} will return valid
      * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
-     * which are too large to fit in an {@code int} and throw a {@code DateTimeException}.
+     * which are too large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
      * <p>
      * If the field is not a {@code ChronoField}, then the result of this method
@@ -1462,6 +1485,30 @@
         return total;
     }
 
+    /**
+     * Converts this {@code LocalTime} to the number of seconds since the epoch
+     * of 1970-01-01T00:00:00Z.
+     * <p>
+     * This combines this local time with the specified date and
+     * offset to calculate the epoch-second value, which is the
+     * number of elapsed seconds from 1970-01-01T00:00:00Z.
+     * Instants on the time-line after the epoch are positive, earlier
+     * are negative.
+     *
+     * @param date the local date, not null
+     * @param offset the zone offset, not null
+     * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative
+     * @since 9
+     */
+    public long toEpochSecond(LocalDate date, ZoneOffset offset) {
+        Objects.requireNonNull(date, "date");
+        Objects.requireNonNull(offset, "offset");
+        long epochDay = date.toEpochDay();
+        long secs = epochDay * 86400 + toSecondOfDay();
+        secs -= offset.getTotalSeconds();
+        return secs;
+    }
+
     //-----------------------------------------------------------------------
     /**
      * Compares this time to another time.
diff --git a/ojluni/src/main/java/java/time/TEST_MAPPING b/ojluni/src/main/java/java/time/TEST_MAPPING
index 5b24483..54d2ea6 100644
--- a/ojluni/src/main/java/java/time/TEST_MAPPING
+++ b/ojluni/src/main/java/java/time/TEST_MAPPING
@@ -7,7 +7,9 @@
           "include-filter": "libcore.java.time"
         }
       ]
-    },
+    }
+  ],
+  "presubmit-large": [
     {
       "name": "CtsLibcoreOjTestCases",
       "options": [
@@ -23,4 +25,4 @@
       ]
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java b/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java
index 736a8fc..fff8d9a 100644
--- a/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java
+++ b/ojluni/src/main/java/java/time/format/DateTimeFormatterBuilder.java
@@ -61,7 +61,6 @@
  */
 package java.time.format;
 
-import android.icu.impl.ZoneMeta;
 import android.icu.text.LocaleDisplayNames;
 import android.icu.text.TimeZoneFormat;
 import android.icu.text.TimeZoneNames;
@@ -78,6 +77,10 @@
 import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
 import static java.time.temporal.ChronoField.YEAR;
 
+import com.android.icu.util.ExtendedCalendar;
+
+import libcore.icu.ICU;
+
 import java.lang.ref.SoftReference;
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -210,14 +213,20 @@
             throw new IllegalArgumentException("Either dateStyle or timeStyle must be non-null");
         }
 
-        // Android-changed: get format string from ICU.
+        // BEGIN Android-changed: get format string from ICU.
         // LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased()
         //         .getLocaleResources(locale);
         // String pattern = lr.getJavaTimeDateTimePattern(
         //         convertStyle(timeStyle), convertStyle(dateStyle), chrono.getCalendarType());
-        String pattern = Calendar.getDateTimeFormatString(
-                ULocale.forLocale(locale), chrono.getCalendarType(),
-                convertStyle(dateStyle), convertStyle(timeStyle));
+        ExtendedCalendar extendedCalendar = ICU.getExtendedCalendar(locale,
+                chrono.getCalendarType());
+        String pattern = extendedCalendar.getDateTimePattern(convertStyle(dateStyle),
+                convertStyle(timeStyle));
+        // Transform the pattern coming from ICU because DateTimeFormatter does not handle some date
+        // symbols, e.g. 'B' / 'b', and thus we use a heuristic algorithm to remove the symbol.
+        // See http://b/174804526.
+        pattern = ICU.transformIcuDateTimePattern_forJavaTime(pattern);
+        // END Android-changed: get format string from ICU.
         return pattern;
     }
 
@@ -3698,9 +3707,9 @@
                 names = new String[TYPES.length + 1];
                 // Zeroth index used for id, other indexes based on NameType constant + 1.
                 names[0] = id;
-                String canonicalId = ZoneMeta.getCanonicalCLDRID(id);
-                timeZoneNames.getDisplayNames(canonicalId, TYPES, System.currentTimeMillis(),
-                        /* dest */ names, /* destoffset */ 1);
+                String canonicalId = ZoneName.getSystemCanonicalID(id);
+                libcore.icu.TimeZoneNames.getDisplayNames(timeZoneNames, canonicalId, TYPES,
+                        System.currentTimeMillis(), /* dest */ names, /* destoffset */ 1);
                 if (names == null) {
                     return null;
                 }
@@ -3826,7 +3835,8 @@
                 for (String zid : regionIds) {
                     tree.add(zid, zid);    // don't convert zid -> metazone
                     zid = ZoneName.toZid(zid, locale);
-                    timeZoneNames.getDisplayNames(zid, types, now, names, 0);
+                    libcore.icu.TimeZoneNames.getDisplayNames(timeZoneNames, zid, types, now,
+                            names, 0);
                     for (int i = 0; i < names.length; i++) {
                         if (names[i] != null) {
                             tree.add(names[i], zid);
@@ -3841,7 +3851,8 @@
                             continue;
                         }
                         String canonicalId = ZoneName.toZid(zid, locale);
-                        timeZoneNames.getDisplayNames(canonicalId, types, now, names, 0);
+                        libcore.icu.TimeZoneNames.getDisplayNames(timeZoneNames, canonicalId, types,
+                                now, names, 0);
                         for (int i = 0; i < names.length; i++) {
                             if (names[i] != null) {
                                 tree.add(names[i], zid);
diff --git a/ojluni/src/main/java/java/time/format/DateTimeTextProvider.java b/ojluni/src/main/java/java/time/format/DateTimeTextProvider.java
index 260196a..490d91f 100644
--- a/ojluni/src/main/java/java/time/format/DateTimeTextProvider.java
+++ b/ojluni/src/main/java/java/time/format/DateTimeTextProvider.java
@@ -61,15 +61,16 @@
  */
 package java.time.format;
 
-import android.icu.impl.ICUData;
-import android.icu.impl.ICUResourceBundle;
-import android.icu.util.UResourceBundle;
+import android.icu.text.DateFormatSymbols;
+import android.icu.util.ULocale;
 
 import static java.time.temporal.ChronoField.AMPM_OF_DAY;
 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
 import static java.time.temporal.ChronoField.ERA;
 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
 
+import com.android.icu.text.ExtendedDateFormatSymbols;
+
 import java.time.chrono.Chronology;
 import java.time.chrono.IsoChronology;
 import java.time.chrono.JapaneseChronology;
@@ -452,17 +453,25 @@
                 }
             }
             */
-            ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle
-                    .getBundleInstance(ICUData.ICU_BASE_NAME, locale);
-            ICUResourceBundle quartersRb = rb.getWithFallback("calendar/gregorian/quarters");
-            ICUResourceBundle formatRb = quartersRb.getWithFallback("format");
-            ICUResourceBundle standaloneRb = quartersRb.getWithFallback("stand-alone");
-            styleMap.put(TextStyle.FULL, extractQuarters(formatRb, "wide"));
-            styleMap.put(TextStyle.FULL_STANDALONE, extractQuarters(standaloneRb, "wide"));
-            styleMap.put(TextStyle.SHORT, extractQuarters(formatRb, "abbreviated"));
-            styleMap.put(TextStyle.SHORT_STANDALONE, extractQuarters(standaloneRb, "abbreviated"));
-            styleMap.put(TextStyle.NARROW, extractQuarters(formatRb, "narrow"));
-            styleMap.put(TextStyle.NARROW_STANDALONE, extractQuarters(standaloneRb, "narrow"));
+            ULocale uLocale = ULocale.forLocale(locale);
+            // TODO: Figure why we forced Gregorian calendar in the first patch in
+            // https://r.android.com/311224
+            uLocale.setKeywordValue("calendar", "gregorian");
+            ExtendedDateFormatSymbols extendedDfs = ExtendedDateFormatSymbols.getInstance(uLocale);
+            DateFormatSymbols dfs = extendedDfs.getDateFormatSymbols();
+            styleMap.put(TextStyle.FULL, extractQuarters(
+                    dfs.getQuarters(DateFormatSymbols.FORMAT, DateFormatSymbols.WIDE)));
+            styleMap.put(TextStyle.FULL_STANDALONE, extractQuarters(
+                    dfs.getQuarters(DateFormatSymbols.STANDALONE, DateFormatSymbols.WIDE)));
+            styleMap.put(TextStyle.SHORT, extractQuarters(
+                    dfs.getQuarters(DateFormatSymbols.FORMAT, DateFormatSymbols.ABBREVIATED)));
+            styleMap.put(TextStyle.SHORT_STANDALONE, extractQuarters(
+                    dfs.getQuarters(DateFormatSymbols.STANDALONE, DateFormatSymbols.ABBREVIATED)));
+            styleMap.put(TextStyle.NARROW, extractQuarters(
+                    extendedDfs.getNarrowQuarters(DateFormatSymbols.FORMAT)));
+            styleMap.put(TextStyle.NARROW_STANDALONE, extractQuarters(
+                    extendedDfs.getNarrowQuarters(DateFormatSymbols.STANDALONE)));
+
             // END Android-changed: Use ICU resources.
             return new LocaleStore(styleMap);
         }
@@ -470,16 +479,15 @@
         return "";  // null marker for map
     }
 
-    // BEGIN Android-added: Extracts a Map of quarter names from ICU resource bundle.
-    private static Map<Long, String> extractQuarters(ICUResourceBundle rb, String key) {
-        String[] names = rb.getWithFallback(key).getStringArray();
+    // BEGIN Android-added: Extracts a Map of quarter names.
+    private static Map<Long, String> extractQuarters(String[] quarters) {
         Map<Long, String> map = new HashMap<>();
-        for (int q = 0; q < names.length; q++) {
-            map.put((long) (q + 1), names[q]);
+        for (int q = 0; q < quarters.length; q++) {
+            map.put((long) (q + 1), quarters[q]);
         }
         return map;
     }
-    // END Android-added: Extracts a Map of quarter names from ICU resource bundle.
+    // END Android-added: Extracts a Map of quarter names.
 
     /**
      * Helper method to create an immutable entry.
diff --git a/ojluni/src/main/java/java/time/format/ZoneName.java b/ojluni/src/main/java/java/time/format/ZoneName.java
index fe4a95a..daecb6e 100644
--- a/ojluni/src/main/java/java/time/format/ZoneName.java
+++ b/ojluni/src/main/java/java/time/format/ZoneName.java
@@ -24,7 +24,6 @@
  */
 package java.time.format;
 
-import android.icu.impl.ZoneMeta;
 import android.icu.text.TimeZoneNames;
 import android.icu.util.TimeZone;
 import android.icu.util.ULocale;
@@ -57,14 +56,28 @@
     }
 
     public static String toZid(String zid) {
-        // Android-changed: Use ICU ZoneMeta.
-        String canonicalCldrId = ZoneMeta.getCanonicalCLDRID(zid);
+        // Android-changed: Use ICU TimeZone.getCanonicalID().
+        String canonicalCldrId = getSystemCanonicalID(zid);
         if (canonicalCldrId != null) {
             return canonicalCldrId;
         }
         return zid;
     }
 
+    // BEGIN Android-added: Get non-custom system canonical time zone Id from ICU.
+    public static String getSystemCanonicalID(String zid) {
+        if (TimeZone.UNKNOWN_ZONE_ID.equals(zid)) {
+            return zid;
+        }
+        boolean[] isSystemID = { false };
+        String canonicalID = TimeZone.getCanonicalID(zid, isSystemID);
+        if (canonicalID == null || !isSystemID[0]) {
+            return null;
+        }
+        return canonicalID;
+    }
+    // END Android-added: Get non-custom system canonical time zone Id from ICU.
+
     // Android-removed: zidMap and aliasMap containing zone id data.
     // Android-removed: zidToMzone, mzoneToZid, mzoneToZidL, aliases and their initialization code.
 }
diff --git a/ojluni/src/main/java/java/time/temporal/ChronoField.java b/ojluni/src/main/java/java/time/temporal/ChronoField.java
index b8f7cee..e1e8250 100644
--- a/ojluni/src/main/java/java/time/temporal/ChronoField.java
+++ b/ojluni/src/main/java/java/time/temporal/ChronoField.java
@@ -621,7 +621,7 @@
 
         // Android-changed: use ICU names.
         DateTimePatternGenerator generator = DateTimePatternGenerator
-                .getFrozenInstance(ULocale.forLocale(locale));
+                .getInstance(ULocale.forLocale(locale));
         String icuName = generator.getAppendItemName(getIcuFieldNumber(this));
         return icuName != null && !icuName.isEmpty() ? icuName : name;
     }
diff --git a/ojluni/src/main/java/java/time/temporal/IsoFields.java b/ojluni/src/main/java/java/time/temporal/IsoFields.java
index 8b6f237..20b2cfd 100644
--- a/ojluni/src/main/java/java/time/temporal/IsoFields.java
+++ b/ojluni/src/main/java/java/time/temporal/IsoFields.java
@@ -420,7 +420,7 @@
                 Objects.requireNonNull(locale, "locale");
                 // Android-changed: Use ICU name values.
                 DateTimePatternGenerator dateTimePatternGenerator = DateTimePatternGenerator
-                        .getFrozenInstance(ULocale.forLocale(locale));
+                        .getInstance(ULocale.forLocale(locale));
                 String icuName = dateTimePatternGenerator
                         .getAppendItemName(DateTimePatternGenerator.WEEK_OF_YEAR);
                 return icuName != null && !icuName.isEmpty() ? icuName : toString();
diff --git a/ojluni/src/main/java/java/time/temporal/WeekFields.java b/ojluni/src/main/java/java/time/temporal/WeekFields.java
index 57f481c..b2f85ae 100644
--- a/ojluni/src/main/java/java/time/temporal/WeekFields.java
+++ b/ojluni/src/main/java/java/time/temporal/WeekFields.java
@@ -285,9 +285,8 @@
     public static WeekFields of(Locale locale) {
         Objects.requireNonNull(locale, "locale");
         // Android-changed: get Week data from ICU4J
-        ULocale ulocale = ULocale.forLocale(locale);
-        String region = ULocale.getRegionForSupplementalData(ulocale, /* inferRegion */ true);
-        Calendar.WeekData weekData = Calendar.getWeekDataForRegion(region);
+        Calendar calendar = Calendar.getInstance(locale);
+        Calendar.WeekData weekData = calendar.getWeekData();
         DayOfWeek dow = DayOfWeek.SUNDAY.plus(weekData.firstDayOfWeek - 1);
         return WeekFields.of(dow, weekData.minimalDaysInFirstWeek);
     }
@@ -1034,7 +1033,7 @@
             if (rangeUnit == YEARS) {  // only have values for week-of-year
                 // Android-changed: Use ICU name values.
                 DateTimePatternGenerator dateTimePatternGenerator = DateTimePatternGenerator
-                        .getFrozenInstance(ULocale.forLocale(locale));
+                        .getInstance(ULocale.forLocale(locale));
                 String icuName = dateTimePatternGenerator
                         .getAppendItemName(DateTimePatternGenerator.WEEK_OF_YEAR);
                 return icuName != null && !icuName.isEmpty() ? icuName : name;
diff --git a/ojluni/src/main/java/java/time/zone/IcuZoneRulesProvider.java b/ojluni/src/main/java/java/time/zone/IcuZoneRulesProvider.java
index 5a4e37d..a48aa62 100644
--- a/ojluni/src/main/java/java/time/zone/IcuZoneRulesProvider.java
+++ b/ojluni/src/main/java/java/time/zone/IcuZoneRulesProvider.java
@@ -21,25 +21,16 @@
 
 package java.time.zone;
 
-import android.icu.util.AnnualTimeZoneRule;
-import android.icu.util.BasicTimeZone;
-import android.icu.util.DateTimeRule;
-import android.icu.util.InitialTimeZoneRule;
+
 import android.icu.util.TimeZone;
-import android.icu.util.TimeZoneRule;
-import android.icu.util.TimeZoneTransition;
-import java.time.DayOfWeek;
-import java.time.LocalTime;
-import java.time.Month;
-import java.time.ZoneOffset;
-import java.util.ArrayList;
+
+import com.android.icu.util.ExtendedTimeZone;
+
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.List;
 import java.util.NavigableMap;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.concurrent.TimeUnit;
 import libcore.util.BasicLruCache;
 
 /**
@@ -49,11 +40,6 @@
  */
 public class IcuZoneRulesProvider extends ZoneRulesProvider {
 
-    // Arbitrary upper limit to number of transitions including the final rules.
-    private static final int MAX_TRANSITIONS = 10000;
-
-    private static final int SECONDS_IN_DAY = 24 * 60 * 60;
-
     private final BasicLruCache<String, ZoneRules> cache = new ZoneRulesCache(8);
 
     @Override
@@ -80,216 +66,8 @@
                         provideRules(zoneId, /* forCaching */ false)));
     }
 
-    /*
-     * This implementation is only tested with BasicTimeZone objects and depends on
-     * implementation details of that class:
-     *
-     * 0. TimeZone.getFrozenTimeZone() always returns a BasicTimeZone object.
-     * 1. The first rule is always an InitialTimeZoneRule (guaranteed by spec).
-     * 2. AnnualTimeZoneRules are only used as "final rules".
-     * 3. The final rules are either 0 or 2 AnnualTimeZoneRules
-     * 4. The final rules have endYear set to MAX_YEAR.
-     * 5. Each transition generated by the rules changes either the raw offset, the total offset
-     *    or both.
-     * 6. There is a non-immense number of transitions for any rule before the final rules apply
-     *    (enforced via the arbitrary limit defined in MAX_TRANSITIONS).
-     *
-     * Assumptions #5 and #6 are not strictly required for this code to work, but hold for the
-     * the data and code at the time of implementation. If they were broken they would indicate
-     * an incomplete understanding of how ICU TimeZoneRules are used which would probably mean that
-     * this code needs to be updated.
-     *
-     * These assumptions are verified using the verify() method where appropriate.
-     */
     static ZoneRules generateZoneRules(String zoneId) {
-        TimeZone timeZone = TimeZone.getFrozenTimeZone(zoneId);
-        // Assumption #0
-        verify(timeZone instanceof BasicTimeZone, zoneId,
-                "Unexpected time zone class " + timeZone.getClass());
-        BasicTimeZone tz = (BasicTimeZone) timeZone;
-        TimeZoneRule[] rules = tz.getTimeZoneRules();
-        // Assumption #1
-        InitialTimeZoneRule initial = (InitialTimeZoneRule) rules[0];
-
-        ZoneOffset baseStandardOffset = millisToOffset(initial.getRawOffset());
-        ZoneOffset baseWallOffset =
-                millisToOffset((initial.getRawOffset() + initial.getDSTSavings()));
-
-        List<ZoneOffsetTransition> standardOffsetTransitionList = new ArrayList<>();
-        List<ZoneOffsetTransition> transitionList = new ArrayList<>();
-        List<ZoneOffsetTransitionRule> lastRules = new ArrayList<>();
-
-        int preLastDstSavings = 0;
-        AnnualTimeZoneRule last1 = null;
-        AnnualTimeZoneRule last2 = null;
-
-        TimeZoneTransition transition = tz.getNextTransition(Long.MIN_VALUE, false);
-        int transitionCount = 1;
-        // This loop has two possible exit conditions (in normal operation):
-        // 1. for zones that end with a static value and have no ongoing DST changes, it will exit
-        //    via the normal condition (transition != null)
-        // 2. for zones with ongoing DST changes (represented by a "final zone" in ICU4J, and by
-        //    "last rules" in java.time) the "break transitionLoop" will be used to exit the loop.
-        transitionLoop:
-        while (transition != null) {
-            TimeZoneRule from = transition.getFrom();
-            TimeZoneRule to = transition.getTo();
-            boolean hadEffect = false;
-            if (from.getRawOffset() != to.getRawOffset()) {
-                standardOffsetTransitionList.add(new ZoneOffsetTransition(
-                        TimeUnit.MILLISECONDS.toSeconds(transition.getTime()),
-                        millisToOffset(from.getRawOffset()),
-                        millisToOffset(to.getRawOffset())));
-                hadEffect = true;
-            }
-            int fromTotalOffset = from.getRawOffset() + from.getDSTSavings();
-            int toTotalOffset = to.getRawOffset() + to.getDSTSavings();
-            if (fromTotalOffset != toTotalOffset) {
-                transitionList.add(new ZoneOffsetTransition(
-                        TimeUnit.MILLISECONDS.toSeconds(transition.getTime()),
-                        millisToOffset(fromTotalOffset),
-                        millisToOffset(toTotalOffset)));
-                hadEffect = true;
-            }
-            // Assumption #5
-            verify(hadEffect, zoneId, "Transition changed neither total nor raw offset.");
-            if (to instanceof AnnualTimeZoneRule) {
-                // The presence of an AnnualTimeZoneRule is taken as an indication of a final rule.
-                if (last1 == null) {
-                    preLastDstSavings = from.getDSTSavings();
-                    last1 = (AnnualTimeZoneRule) to;
-                    // Assumption #4
-                    verify(last1.getEndYear() == AnnualTimeZoneRule.MAX_YEAR, zoneId,
-                            "AnnualTimeZoneRule is not permanent.");
-                } else {
-                    last2 = (AnnualTimeZoneRule) to;
-                    // Assumption #4
-                    verify(last2.getEndYear() == AnnualTimeZoneRule.MAX_YEAR, zoneId,
-                            "AnnualTimeZoneRule is not permanent.");
-
-                    // Assumption #3
-                    transition = tz.getNextTransition(transition.getTime(), false);
-                    verify(transition.getTo() == last1, zoneId,
-                            "Unexpected rule after 2 AnnualTimeZoneRules.");
-                    break transitionLoop;
-                }
-            } else {
-                // Assumption #2
-                verify(last1 == null, zoneId, "Unexpected rule after AnnualTimeZoneRule.");
-            }
-            verify(transitionCount <= MAX_TRANSITIONS, zoneId,
-                    "More than " + MAX_TRANSITIONS + " transitions.");
-            transition = tz.getNextTransition(transition.getTime(), false);
-            transitionCount++;
-        }
-        if (last1 != null) {
-            // Assumption #3
-            verify(last2 != null, zoneId, "Only one AnnualTimeZoneRule.");
-            lastRules.add(toZoneOffsetTransitionRule(last1, preLastDstSavings));
-            lastRules.add(toZoneOffsetTransitionRule(last2, last1.getDSTSavings()));
-        }
-
-        return ZoneRules.of(baseStandardOffset, baseWallOffset, standardOffsetTransitionList,
-                transitionList, lastRules);
-    }
-
-    /**
-     * Verify an assumption about the zone rules.
-     *
-     * @param check
-     *         {@code true} if the assumption holds, {@code false} otherwise.
-     * @param zoneId
-     *         Zone ID for which to check.
-     * @param message
-     *         Error description of a failed check.
-     * @throws ZoneRulesException
-     *         If and only if {@code check} is {@code false}.
-     */
-    private static void verify(boolean check, String zoneId, String message) {
-        if (!check) {
-            throw new ZoneRulesException(
-                    String.format("Failed verification of zone %s: %s", zoneId, message));
-        }
-    }
-
-    /**
-     * Transform an {@link AnnualTimeZoneRule} into an equivalent {@link ZoneOffsetTransitionRule}.
-     * This is only used for the "final rules".
-     *
-     * @param rule
-     *         The rule to transform.
-     * @param dstSavingMillisBefore
-     *         The DST offset before the first transition in milliseconds.
-     */
-    private static ZoneOffsetTransitionRule toZoneOffsetTransitionRule(
-            AnnualTimeZoneRule rule, int dstSavingMillisBefore) {
-        DateTimeRule dateTimeRule = rule.getRule();
-        // Calendar.JANUARY is 0, transform it into a proper Month.
-        Month month = Month.JANUARY.plus(dateTimeRule.getRuleMonth());
-        int dayOfMonthIndicator;
-        // Calendar.SUNDAY is 1, transform it into a proper DayOfWeek.
-        DayOfWeek dayOfWeek = DayOfWeek.SATURDAY.plus(dateTimeRule.getRuleDayOfWeek());
-        switch (dateTimeRule.getDateRuleType()) {
-            case DateTimeRule.DOM:
-                // Transition always on a specific day of the month.
-                dayOfMonthIndicator = dateTimeRule.getRuleDayOfMonth();
-                dayOfWeek = null;
-                break;
-            case DateTimeRule.DOW_GEQ_DOM:
-                // ICU representation matches java.time representation.
-                dayOfMonthIndicator = dateTimeRule.getRuleDayOfMonth();
-                break;
-            case DateTimeRule.DOW_LEQ_DOM:
-                // java.time uses a negative dayOfMonthIndicator to represent "Sun<=X" or "lastSun"
-                // rules. ICU uses this constant and the normal day. So "lastSun" in January would
-                // ruleDayOfMonth = 31 in ICU and dayOfMonthIndicator = -1 in java.time.
-                dayOfMonthIndicator = -month.maxLength() + dateTimeRule.getRuleDayOfMonth() - 1;
-                break;
-            case DateTimeRule.DOW:
-                // DOW is unspecified in the documentation and seems to never be used.
-                throw new ZoneRulesException("Date rule type DOW is unsupported");
-            default:
-                throw new ZoneRulesException(
-                        "Unexpected date rule type: " + dateTimeRule.getDateRuleType());
-        }
-        // Cast to int is save, as input is int.
-        int secondOfDay = (int) TimeUnit.MILLISECONDS.toSeconds(dateTimeRule.getRuleMillisInDay());
-        LocalTime time;
-        boolean timeEndOfDay;
-        if (secondOfDay == SECONDS_IN_DAY) {
-            time = LocalTime.MIDNIGHT;
-            timeEndOfDay = true;
-        } else {
-            time = LocalTime.ofSecondOfDay(secondOfDay);
-            timeEndOfDay = false;
-        }
-        ZoneOffsetTransitionRule.TimeDefinition timeDefinition;
-        switch (dateTimeRule.getTimeRuleType()) {
-            case DateTimeRule.WALL_TIME:
-                timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.WALL;
-                break;
-            case DateTimeRule.STANDARD_TIME:
-                timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.STANDARD;
-                break;
-            case DateTimeRule.UTC_TIME:
-                timeDefinition = ZoneOffsetTransitionRule.TimeDefinition.UTC;
-                break;
-            default:
-                throw new ZoneRulesException(
-                        "Unexpected time rule type " + dateTimeRule.getTimeRuleType());
-        }
-        ZoneOffset standardOffset = millisToOffset(rule.getRawOffset());
-        ZoneOffset offsetBefore = millisToOffset(rule.getRawOffset() + dstSavingMillisBefore);
-        ZoneOffset offsetAfter = millisToOffset(
-                rule.getRawOffset() + rule.getDSTSavings());
-        return ZoneOffsetTransitionRule.of(
-                month, dayOfMonthIndicator, dayOfWeek, time, timeEndOfDay, timeDefinition,
-                standardOffset, offsetBefore, offsetAfter);
-    }
-
-    private static ZoneOffset millisToOffset(int offset) {
-        // Cast to int is save, as input is int.
-        return ZoneOffset.ofTotalSeconds((int) TimeUnit.MILLISECONDS.toSeconds(offset));
+        return ExtendedTimeZone.getInstance(zoneId).createZoneRules();
     }
 
     private static class ZoneRulesCache extends BasicLruCache<String, ZoneRules> {
diff --git a/ojluni/src/main/java/java/util/AbstractCollection.java b/ojluni/src/main/java/java/util/AbstractCollection.java
index 1f7cdd9..19c15f8 100644
--- a/ojluni/src/main/java/java/util/AbstractCollection.java
+++ b/ojluni/src/main/java/java/util/AbstractCollection.java
@@ -49,7 +49,7 @@
  * the collection being implemented admits a more efficient implementation.<p>
  *
  * This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/util/AbstractList.java b/ojluni/src/main/java/java/util/AbstractList.java
index c8c160d..30778ad 100644
--- a/ojluni/src/main/java/java/util/AbstractList.java
+++ b/ojluni/src/main/java/java/util/AbstractList.java
@@ -62,7 +62,7 @@
  * collection being implemented admits a more efficient implementation.
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/util/AbstractMap.java b/ojluni/src/main/java/java/util/AbstractMap.java
index 948bf51..09c2f94 100644
--- a/ojluni/src/main/java/java/util/AbstractMap.java
+++ b/ojluni/src/main/java/java/util/AbstractMap.java
@@ -52,7 +52,7 @@
  * map being implemented admits a more efficient implementation.
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <K> the type of keys maintained by this map
diff --git a/ojluni/src/main/java/java/util/AbstractSequentialList.java b/ojluni/src/main/java/java/util/AbstractSequentialList.java
index 36a91da..37292df 100644
--- a/ojluni/src/main/java/java/util/AbstractSequentialList.java
+++ b/ojluni/src/main/java/java/util/AbstractSequentialList.java
@@ -54,7 +54,7 @@
  * specification.<p>
  *
  * This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/util/AbstractSet.java b/ojluni/src/main/java/java/util/AbstractSet.java
index b3fe0dc..620f672 100644
--- a/ojluni/src/main/java/java/util/AbstractSet.java
+++ b/ojluni/src/main/java/java/util/AbstractSet.java
@@ -42,7 +42,7 @@
  * for <tt>equals</tt> and <tt>hashCode</tt>.<p>
  *
  * This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <E> the type of elements maintained by this set
diff --git a/ojluni/src/main/java/java/util/ArrayList.java b/ojluni/src/main/java/java/util/ArrayList.java
index 20c623a..02f9479 100644
--- a/ojluni/src/main/java/java/util/ArrayList.java
+++ b/ojluni/src/main/java/java/util/ArrayList.java
@@ -92,7 +92,7 @@
  * should be used only to detect bugs.</i>
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/util/Arrays.java b/ojluni/src/main/java/java/util/Arrays.java
index 6da3e5d..d0a218a 100644
--- a/ojluni/src/main/java/java/util/Arrays.java
+++ b/ojluni/src/main/java/java/util/Arrays.java
@@ -61,7 +61,7 @@
  * a MergeSort, but it does have to be <i>stable</i>.)
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author Josh Bloch
@@ -78,7 +78,7 @@
      * tasks that makes parallel speedups unlikely.
      * @hide
      */
-    // Android-changed: Make MIN_ARRAY_SORT_GRAN public and @hide (used by harmony ArraysTest)
+    // Android-changed: Make MIN_ARRAY_SORT_GRAN public and @hide (used by harmony ArraysTest).
     public static final int MIN_ARRAY_SORT_GRAN = 1 << 13;
 
     // Suppresses default constructor, ensuring non-instantiability.
@@ -1232,14 +1232,14 @@
      *         {@link Comparable} contract
      */
     public static void sort(Object[] a) {
-        // Android-removed: LegacyMergeSort support
+        // Android-removed: LegacyMergeSort support.
         // if (LegacyMergeSort.userRequested)
         //     legacyMergeSort(a);
         // else
             ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
     }
 
-    // Android-removed: legacyMergeSort() (unused on Android)
+    // Android-removed: legacyMergeSort() (unused on Android).
 
     /**
      * Sorts the specified range of the specified array of objects into
@@ -1295,14 +1295,14 @@
      */
     public static void sort(Object[] a, int fromIndex, int toIndex) {
         rangeCheck(a.length, fromIndex, toIndex);
-        // Android-removed: LegacyMergeSort support
+        // Android-removed: LegacyMergeSort support.
         // if (LegacyMergeSort.userRequested)
         //     legacyMergeSort(a, fromIndex, toIndex);
         // else
             ComparableTimSort.sort(a, fromIndex, toIndex, null, 0, 0);
     }
 
-    // Android-removed: legacyMergeSort() (unused on Android)
+    // Android-removed: legacyMergeSort() (unused on Android).
 
     /**
      * Tuning parameter: list size at or below which insertion sort will be
@@ -1417,7 +1417,7 @@
         if (c == null) {
             sort(a);
         } else {
-        // Android-removed: LegacyMergeSort support
+        // Android-removed: LegacyMergeSort support.
             // if (LegacyMergeSort.userRequested)
             //     legacyMergeSort(a, c);
             // else
@@ -1425,7 +1425,7 @@
         }
     }
 
-    // Android-removed: legacyMergeSort() (unused on Android)
+    // Android-removed: legacyMergeSort() (unused on Android).
 
     /**
      * Sorts the specified range of the specified array of objects according
@@ -1485,7 +1485,7 @@
             sort(a, fromIndex, toIndex);
         } else {
             rangeCheck(a.length, fromIndex, toIndex);
-            // Android-removed: LegacyMergeSort support
+            // Android-removed: LegacyMergeSort support.
             // if (LegacyMergeSort.userRequested)
             //     legacyMergeSort(a, fromIndex, toIndex, c);
             // else
@@ -1493,8 +1493,8 @@
         }
     }
 
-    // Android-removed: legacyMergeSort() (unused on Android)
-    // Android-removed: mergeSort() (unused on Android)
+    // Android-removed: legacyMergeSort() (unused on Android).
+    // Android-removed: mergeSort() (unused on Android).
 
     // Parallel prefix
 
@@ -4110,7 +4110,7 @@
 
         for (Object element : a) {
             int elementHash = 0;
-            // BEGIN Android-changed: getComponentType() is faster than instanceof()
+            // BEGIN Android-changed: getComponentType() is faster than instanceof().
             if (element != null) {
                 Class<?> cl = element.getClass().getComponentType();
                 if (cl == null)
@@ -4136,7 +4136,7 @@
                 else
                     elementHash = element.hashCode();
             }
-            // END Android-changed: getComponentType() is faster than instanceof()
+            // END Android-changed: getComponentType() is faster than instanceof().
             result = 31 * result + elementHash;
         }
 
diff --git a/ojluni/src/main/java/java/util/Calendar.java b/ojluni/src/main/java/java/util/Calendar.java
index 7093533..53994c1 100644
--- a/ojluni/src/main/java/java/util/Calendar.java
+++ b/ojluni/src/main/java/java/util/Calendar.java
@@ -1595,7 +1595,7 @@
      */
     protected Calendar(TimeZone zone, Locale aLocale)
     {
-        // BEGIN Android-added: Allow aLocale == null
+        // BEGIN Android-added: Allow aLocale == null.
         // http://b/16938922.
         //
         // TODO: This is for backwards compatibility only. Seems like a better idea to throw
@@ -1603,7 +1603,7 @@
         if (aLocale == null) {
             aLocale = Locale.getDefault();
         }
-        // END Android-added: Allow aLocale == null
+        // END Android-added: Allow aLocale == null.
         fields = new int[FIELD_COUNT];
         isSet = new boolean[FIELD_COUNT];
         stamp = new int[FIELD_COUNT];
@@ -1667,7 +1667,7 @@
         return createCalendar(zone, aLocale);
     }
 
-    // BEGIN Android-added: add getJapaneseImperialInstance()
+    // BEGIN Android-added: add getJapaneseImperialInstance().
     /**
      * Create a Japanese Imperial Calendar.
      * @hide
@@ -1675,14 +1675,14 @@
     public static Calendar getJapaneseImperialInstance(TimeZone zone, Locale aLocale) {
         return new JapaneseImperialCalendar(zone, aLocale);
     }
-    // END Android-added: add getJapaneseImperialInstance()
+    // END Android-added: add getJapaneseImperialInstance().
 
     private static Calendar createCalendar(TimeZone zone,
                                            Locale aLocale)
     {
-        // BEGIN Android-changed: only support GregorianCalendar here
+        // BEGIN Android-changed: only support GregorianCalendar here.
         return new GregorianCalendar(zone, aLocale);
-        // END Android-changed: only support GregorianCalendar here
+        // END Android-changed: only support GregorianCalendar here.
     }
 
     /**
@@ -2061,13 +2061,13 @@
      * @since 1.6
      */
     public String getDisplayName(int field, int style, Locale locale) {
-        // BEGIN Android-changed: Treat ALL_STYLES as SHORT
+        // BEGIN Android-changed: Treat ALL_STYLES as SHORT.
         // Android has traditionally treated ALL_STYLES as SHORT, even though
         // it's not documented to be a valid value for style.
         if (style == ALL_STYLES) {
             style = SHORT;
         }
-        // END Android-changed: Treat ALL_STYLES as SHORT
+        // END Android-changed: Treat ALL_STYLES as SHORT.
         if (!checkDisplayNameParams(field, style, SHORT, NARROW_FORMAT, locale,
                             ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
             return null;
@@ -2161,7 +2161,7 @@
                                     ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
             return null;
         }
-        // Android-added: Add complete() here to fix leniency, see http://b/35382060
+        // Android-added: Add complete() here to fix leniency. http://b/35382060
         complete();
 
         String calendarType = getCalendarType();
@@ -2209,12 +2209,12 @@
             baseStyle < minStyle || baseStyle > maxStyle) {
             throw new IllegalArgumentException();
         }
-        // BEGIN Android-added: Check for invalid baseStyle == 3
+        // BEGIN Android-added: Check for invalid baseStyle == 3.
         // 3 is not a valid base style (unlike 1, 2 and 4). Throw if used.
         if (baseStyle == 3) {
             throw new IllegalArgumentException();
         }
-        // END Android-added: Check for invalid baseStyle == 3
+        // END Android-added: Check for invalid baseStyle == 3.
         if (locale == null) {
             throw new NullPointerException();
         }
@@ -2574,12 +2574,12 @@
         return style & ~STANDALONE_MASK;
     }
 
-    // BEGIN Android-changed: Make toStandaloneStyle() public to use in java.text.SimpleDateFormat
+    // BEGIN Android-changed: Make toStandaloneStyle() public to use in java.text.SimpleDateFormat.
     /**
      * @hide
      */
     public static int toStandaloneStyle(int style) {
-    // END Android-changed: Make toStandaloneStyle() public to use in java.text.SimpleDateFormat
+    // END Android-changed: Make toStandaloneStyle() public to use in java.text.SimpleDateFormat.
         return style | STANDALONE_MASK;
     }
 
diff --git a/ojluni/src/main/java/java/util/Collection.java b/ojluni/src/main/java/java/util/Collection.java
index 2ae8872..279ec9e 100644
--- a/ojluni/src/main/java/java/util/Collection.java
+++ b/ojluni/src/main/java/java/util/Collection.java
@@ -112,7 +112,7 @@
  * however most current implementations do not do so.
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @implSpec
diff --git a/ojluni/src/main/java/java/util/Collections.java b/ojluni/src/main/java/java/util/Collections.java
index 58ad86f..e7e8e72 100644
--- a/ojluni/src/main/java/java/util/Collections.java
+++ b/ojluni/src/main/java/java/util/Collections.java
@@ -70,7 +70,7 @@
  * already sorted may or may not throw <tt>UnsupportedOperationException</tt>.
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/util/Comparator.java b/ojluni/src/main/java/java/util/Comparator.java
index ecf8d64..27cfdd5 100644
--- a/ojluni/src/main/java/java/util/Comparator.java
+++ b/ojluni/src/main/java/java/util/Comparator.java
@@ -94,7 +94,7 @@
  * an equivalence relation.
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <T> the type of objects that may be compared by this comparator
diff --git a/ojluni/src/main/java/java/util/Currency.java b/ojluni/src/main/java/java/util/Currency.java
index 94629c7..c7b8b16 100644
--- a/ojluni/src/main/java/java/util/Currency.java
+++ b/ojluni/src/main/java/java/util/Currency.java
@@ -361,16 +361,21 @@
         */
         android.icu.util.Currency icuInstance =
                 android.icu.util.Currency.getInstance(locale);
+        // Unknown historical reason to append variant to country code. The API documentation
+        // does not mention the effect of locale variant. The actual effect here is throwing
+        // IllegalArgumentException because the code like FR_EURO is not a valid country code.
         String variant = locale.getVariant();
         if (!variant.isEmpty() && (variant.equals("EURO") || variant.equals("HK") ||
                 variant.equals("PREEURO"))) {
             country = country + "_" + variant;
         }
-        String currencyCode = ICU.getCurrencyCode(country);
-        if (currencyCode == null) {
+        if (!ICU.isIsoCountry(country)) {
+            // Throws IllegalArgumentException as required by the API documentation.
             throw new IllegalArgumentException("Unsupported ISO 3166 country: " + locale);
         }
-        if (icuInstance == null || icuInstance.getCurrencyCode().equals("XXX")) {
+        String currencyCode = ICU.getCurrencyCode(country);
+        if (currencyCode == null || icuInstance == null ||
+                icuInstance.getCurrencyCode().equals("XXX")) { // XXX is not a real currency.
             return null;
         }
         return getInstance(currencyCode);
diff --git a/ojluni/src/main/java/java/util/EnumMap.java b/ojluni/src/main/java/java/util/EnumMap.java
index cd1c5fe..30a8dce 100644
--- a/ojluni/src/main/java/java/util/EnumMap.java
+++ b/ojluni/src/main/java/java/util/EnumMap.java
@@ -69,7 +69,7 @@
  * {@link HashMap} counterparts.
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author Josh Bloch
diff --git a/ojluni/src/main/java/java/util/EnumSet.java b/ojluni/src/main/java/java/util/EnumSet.java
index 95e0124..f1b0329 100644
--- a/ojluni/src/main/java/java/util/EnumSet.java
+++ b/ojluni/src/main/java/java/util/EnumSet.java
@@ -68,7 +68,7 @@
  * constant time if their argument is also an enum set.
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author Josh Bloch
diff --git a/ojluni/src/main/java/java/util/GregorianCalendar.java b/ojluni/src/main/java/java/util/GregorianCalendar.java
index 1d8c87b..46733e9 100644
--- a/ojluni/src/main/java/java/util/GregorianCalendar.java
+++ b/ojluni/src/main/java/java/util/GregorianCalendar.java
@@ -739,12 +739,12 @@
         gdate = (BaseCalendar.Date) gcal.newCalendarDate(getZone());
     }
 
-    // BEGIN Android-added
+    // BEGIN Android-added: Constructor.
     GregorianCalendar(long milliseconds) {
         this();
         setTimeInMillis(milliseconds);
     }
-    // END Android-added
+    // END Android-added: Constructor.
 
 /////////////////
 // Public methods
@@ -1080,7 +1080,7 @@
             }
 
             fd += delta; // fd is the expected fixed date after the calculation
-            // BEGIN Android-changed: time zone related calculation via helper methods
+            // BEGIN Android-changed: time zone related calculation via helper methods.
             // Calculate the time in the UTC time zone.
             long utcTime = (fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
 
@@ -1093,7 +1093,7 @@
 
             // Update the time and recompute the fields.
             setTimeInMillis(millis);
-            // END Android-changed: time zone related calculation via helper methods
+            // END Android-changed: time zone related calculation via helper methods.
         }
     }
 
@@ -2346,12 +2346,12 @@
         }
         if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
             if (tz instanceof ZoneInfo) {
-                // BEGIN Android-changed: use libcore.util.ZoneInfo
+                // BEGIN Android-changed: use libcore.util.ZoneInfo.
                 // The method name to get offsets differs from sun.util.calendar.ZoneInfo
                 // zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
                 ZoneInfo zoneInfo = (ZoneInfo) tz;
                 zoneOffset = zoneInfo.getOffsetsByUtcTime(time, zoneOffsets);
-                // END Android-changed: use libcore.util.ZoneInfo
+                // END Android-changed: use libcore.util.ZoneInfo.
             } else {
                 zoneOffset = tz.getOffset(time);
                 zoneOffsets[0] = tz.getRawOffset();
@@ -2801,11 +2801,11 @@
         // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
         // or DST_OFFSET fields; then we use those fields.
         TimeZone zone = getZone();
-        // BEGIN Android-changed: time zone related calculation via helper methods
+        // BEGIN Android-changed: time zone related calculation via helper methods.
         int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
 
         millis = adjustForZoneAndDaylightSavingsTime(tzMask, millis, zone);
-        // END Android-changed: time zone related calculation via helper methods
+        // END Android-changed: time zone related calculation via helper methods.
 
         // Set this calendar's time in milliseconds
         time = millis;
@@ -2828,7 +2828,7 @@
         setFieldsNormalized(mask);
     }
 
-    // BEGIN Android-added: helper methods for time zone related calculation
+    // BEGIN Android-added: helper methods for time zone related calculation.
     /**
      * Calculates the time in milliseconds that this calendar represents using the UTC time,
      * timezone information (specifically Daylight Savings Time (DST) rules, if any) and knowledge
@@ -2998,7 +2998,7 @@
         }
         return dstOffset;
     }
-    // END Android-added: helper methods for time zone related calculation
+    // END Android-added: helper methods for time zone related calculation.
 
     /**
      * Computes the fixed date under either the Gregorian or the
diff --git a/ojluni/src/main/java/java/util/HashMap.java b/ojluni/src/main/java/java/util/HashMap.java
index 2c01f6f..360f736 100644
--- a/ojluni/src/main/java/java/util/HashMap.java
+++ b/ojluni/src/main/java/java/util/HashMap.java
@@ -117,7 +117,7 @@
  * should be used only to detect bugs.</i>
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <K> the type of keys maintained by this map
diff --git a/ojluni/src/main/java/java/util/HashSet.java b/ojluni/src/main/java/java/util/HashSet.java
index f9b09ee..5805331 100644
--- a/ojluni/src/main/java/java/util/HashSet.java
+++ b/ojluni/src/main/java/java/util/HashSet.java
@@ -72,7 +72,7 @@
  * should be used only to detect bugs.</i>
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <E> the type of elements maintained by this set
diff --git a/ojluni/src/main/java/java/util/Hashtable.java b/ojluni/src/main/java/java/util/Hashtable.java
index 4c76a74..fc163a7 100644
--- a/ojluni/src/main/java/java/util/Hashtable.java
+++ b/ojluni/src/main/java/java/util/Hashtable.java
@@ -105,7 +105,7 @@
  *
  * <p>As of the Java 2 platform v1.2, this class was retrofitted to
  * implement the {@link Map} interface, making it a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  *
  * Java Collections Framework</a>.  Unlike the new collection
  * implementations, {@code Hashtable} is synchronized.  If a
diff --git a/ojluni/src/main/java/java/util/IdentityHashMap.java b/ojluni/src/main/java/java/util/IdentityHashMap.java
index 9dc0c26..1ed9d76 100644
--- a/ojluni/src/main/java/java/util/IdentityHashMap.java
+++ b/ojluni/src/main/java/java/util/IdentityHashMap.java
@@ -122,7 +122,7 @@
  * {@link HashMap} (which uses <i>chaining</i> rather than linear-probing).
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @see     System#identityHashCode(Object)
diff --git a/ojluni/src/main/java/java/util/ImmutableCollections.java b/ojluni/src/main/java/java/util/ImmutableCollections.java
index 2bc0cc1..e42538c 100644
--- a/ojluni/src/main/java/java/util/ImmutableCollections.java
+++ b/ojluni/src/main/java/java/util/ImmutableCollections.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -69,125 +69,316 @@
 
     static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
 
-    // ---------- List Implementations ----------
-
-    abstract static class AbstractImmutableList<E> extends AbstractList<E>
-                                                implements RandomAccess, Serializable {
+    static abstract class AbstractImmutableCollection<E> extends AbstractCollection<E> {
+        // all mutating methods throw UnsupportedOperationException
         @Override public boolean add(E e) { throw uoe(); }
         @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
-        @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
         @Override public void    clear() { throw uoe(); }
         @Override public boolean remove(Object o) { throw uoe(); }
         @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
         @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
-        @Override public void    replaceAll(UnaryOperator<E> operator) { throw uoe(); }
         @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
-        @Override public void    sort(Comparator<? super E> c) { throw uoe(); }
     }
 
-    static final class List0<E> extends AbstractImmutableList<E> {
-        private static final List0<?> INSTANCE = new List0<>();
+    // ---------- List Implementations ----------
 
-        @SuppressWarnings("unchecked")
-        static <T> List0<T> instance() {
-            return (List0<T>) INSTANCE;
+    // make a copy, short-circuiting based on implementation class
+    @SuppressWarnings("unchecked")
+    static <E> List<E> listCopy(Collection<? extends E> coll) {
+        if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {
+            return (List<E>)coll;
+        } else {
+            return (List<E>)List.of(coll.toArray());
         }
+    }
 
-        private List0() { }
+    @SuppressWarnings("unchecked")
+    static <E> List<E> emptyList() {
+        return (List<E>) ListN.EMPTY_LIST;
+    }
+
+    static abstract class AbstractImmutableList<E> extends AbstractImmutableCollection<E>
+            implements List<E>, RandomAccess {
+
+        // all mutating methods throw UnsupportedOperationException
+        @Override public void    add(int index, E element) { throw uoe(); }
+        @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
+        @Override public E       remove(int index) { throw uoe(); }
+        @Override public void    replaceAll(UnaryOperator<E> operator) { throw uoe(); }
+        @Override public E       set(int index, E element) { throw uoe(); }
+        @Override public void    sort(Comparator<? super E> c) { throw uoe(); }
 
         @Override
-        public int size() {
-            return 0;
+        public List<E> subList(int fromIndex, int toIndex) {
+            int size = size();
+            subListRangeCheck(fromIndex, toIndex, size);
+            return SubList.fromList(this, fromIndex, toIndex);
         }
 
-        @Override
-        public E get(int index) {
-            Objects.checkIndex(index, 0); // always throws IndexOutOfBoundsException
-            return null;                  // but the compiler doesn't know this
+        static void subListRangeCheck(int fromIndex, int toIndex, int size) {
+            if (fromIndex < 0)
+                throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+            if (toIndex > size)
+                throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+            if (fromIndex > toIndex)
+                throw new IllegalArgumentException("fromIndex(" + fromIndex +
+                        ") > toIndex(" + toIndex + ")");
         }
 
         @Override
         public Iterator<E> iterator() {
-            return Collections.emptyIterator();
-        }
-
-        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
-            throw new InvalidObjectException("not serial proxy");
-        }
-
-        private Object writeReplace() {
-            return new CollSer(CollSer.IMM_LIST);
+            return new ListItr<E>(this, size());
         }
 
         @Override
-        public boolean contains(Object o) {
+        public ListIterator<E> listIterator() {
+            return listIterator(0);
+        }
+
+        @Override
+        public ListIterator<E> listIterator(final int index) {
+            int size = size();
+            if (index < 0 || index > size) {
+                throw outOfBounds(index);
+            }
+            return new ListItr<E>(this, size, index);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof List)) {
+                return false;
+            }
+
+            Iterator<?> oit = ((List<?>) o).iterator();
+            for (int i = 0, s = size(); i < s; i++) {
+                if (!oit.hasNext() || !get(i).equals(oit.next())) {
+                    return false;
+                }
+            }
+            return !oit.hasNext();
+        }
+
+        @Override
+        public int indexOf(Object o) {
             Objects.requireNonNull(o);
-            return false;
+            for (int i = 0, s = size(); i < s; i++) {
+                if (o.equals(get(i))) {
+                    return i;
+                }
+            }
+            return -1;
         }
 
         @Override
-        public boolean containsAll(Collection<?> o) {
-            return o.isEmpty(); // implicit nullcheck of o
+        public int lastIndexOf(Object o) {
+            Objects.requireNonNull(o);
+            for (int i = size() - 1; i >= 0; i--) {
+                if (o.equals(get(i))) {
+                    return i;
+                }
+            }
+            return -1;
         }
 
         @Override
         public int hashCode() {
-            return 1;
-        }
-    }
-
-    static final class List1<E> extends AbstractImmutableList<E> {
-        @Stable
-        private final E e0;
-
-        List1(E e0) {
-            this.e0 = Objects.requireNonNull(e0);
-        }
-
-        @Override
-        public int size() {
-            return 1;
-        }
-
-        @Override
-        public E get(int index) {
-            Objects.checkIndex(index, 1);
-            return e0;
-        }
-
-        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
-            throw new InvalidObjectException("not serial proxy");
-        }
-
-        private Object writeReplace() {
-            return new CollSer(CollSer.IMM_LIST, e0);
+            int hash = 1;
+            for (int i = 0, s = size(); i < s; i++) {
+                hash = 31 * hash + get(i).hashCode();
+            }
+            return hash;
         }
 
         @Override
         public boolean contains(Object o) {
-            return o.equals(e0); // implicit nullcheck of o
+            return indexOf(o) >= 0;
         }
 
-        @Override
-        public int hashCode() {
-            return 31 + e0.hashCode();
+        IndexOutOfBoundsException outOfBounds(int index) {
+            return new IndexOutOfBoundsException("Index: " + index + " Size: " + size());
         }
     }
 
-    static final class List2<E> extends AbstractImmutableList<E> {
+    static final class ListItr<E> implements ListIterator<E> {
+
+        @Stable
+        private final List<E> list;
+
+        @Stable
+        private final int size;
+
+        @Stable
+        private final boolean isListIterator;
+
+        private int cursor;
+
+        ListItr(List<E> list, int size) {
+            this.list = list;
+            this.size = size;
+            this.cursor = 0;
+            isListIterator = false;
+        }
+
+        ListItr(List<E> list, int size, int index) {
+            this.list = list;
+            this.size = size;
+            this.cursor = index;
+            isListIterator = true;
+        }
+
+        public boolean hasNext() {
+            return cursor != size;
+        }
+
+        public E next() {
+            try {
+                int i = cursor;
+                E next = list.get(i);
+                cursor = i + 1;
+                return next;
+            } catch (IndexOutOfBoundsException e) {
+                throw new NoSuchElementException();
+            }
+        }
+
+        public void remove() {
+            throw uoe();
+        }
+
+        public boolean hasPrevious() {
+            if (!isListIterator) {
+                throw uoe();
+            }
+            return cursor != 0;
+        }
+
+        public E previous() {
+            if (!isListIterator) {
+                throw uoe();
+            }
+            try {
+                int i = cursor - 1;
+                E previous = list.get(i);
+                cursor = i;
+                return previous;
+            } catch (IndexOutOfBoundsException e) {
+                throw new NoSuchElementException();
+            }
+        }
+
+        public int nextIndex() {
+            if (!isListIterator) {
+                throw uoe();
+            }
+            return cursor;
+        }
+
+        public int previousIndex() {
+            if (!isListIterator) {
+                throw uoe();
+            }
+            return cursor - 1;
+        }
+
+        public void set(E e) {
+            throw uoe();
+        }
+
+        public void add(E e) {
+            throw uoe();
+        }
+    }
+
+    static final class SubList<E> extends AbstractImmutableList<E>
+            implements RandomAccess {
+
+        @Stable
+        private final List<E> root;
+
+        @Stable
+        private final int offset;
+
+        @Stable
+        private final int size;
+
+        private SubList(List<E> root, int offset, int size) {
+            this.root = root;
+            this.offset = offset;
+            this.size = size;
+        }
+
+        /**
+         * Constructs a sublist of another SubList.
+         */
+        static <E> SubList<E> fromSubList(SubList<E> parent, int fromIndex, int toIndex) {
+            return new SubList<>(parent.root, parent.offset + fromIndex, toIndex - fromIndex);
+        }
+
+        /**
+         * Constructs a sublist of an arbitrary AbstractImmutableList, which is
+         * not a SubList itself.
+         */
+        static <E> SubList<E> fromList(List<E> list, int fromIndex, int toIndex) {
+            return new SubList<>(list, fromIndex, toIndex - fromIndex);
+        }
+
+        public E get(int index) {
+            Objects.checkIndex(index, size);
+            return root.get(offset + index);
+        }
+
+        public int size() {
+            return size;
+        }
+
+        public Iterator<E> iterator() {
+            return new ListItr<>(this, size());
+        }
+
+        public ListIterator<E> listIterator(int index) {
+            rangeCheck(index);
+            return new ListItr<>(this, size(), index);
+        }
+
+        public List<E> subList(int fromIndex, int toIndex) {
+            subListRangeCheck(fromIndex, toIndex, size);
+            return SubList.fromSubList(this, fromIndex, toIndex);
+        }
+
+        private void rangeCheck(int index) {
+            if (index < 0 || index > size) {
+                throw outOfBounds(index);
+            }
+        }
+    }
+
+    static final class List12<E> extends AbstractImmutableList<E>
+            implements Serializable {
+
         @Stable
         private final E e0;
+
         @Stable
         private final E e1;
 
-        List2(E e0, E e1) {
+        List12(E e0) {
+            this.e0 = Objects.requireNonNull(e0);
+            this.e1 = null;
+        }
+
+        List12(E e0, E e1) {
             this.e0 = Objects.requireNonNull(e0);
             this.e1 = Objects.requireNonNull(e1);
         }
 
         @Override
         public int size() {
-            return 2;
+            return e1 != null ? 2 : 1;
         }
 
         @Override
@@ -195,20 +386,10 @@
             Objects.checkIndex(index, 2);
             if (index == 0) {
                 return e0;
-            } else { // index == 1
+            } else if (index == 1 && e1 != null) {
                 return e1;
             }
-        }
-
-        @Override
-        public boolean contains(Object o) {
-            return o.equals(e0) || o.equals(e1); // implicit nullcheck of o
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 31 + e0.hashCode();
-            return 31 * hash + e1.hashCode();
+            throw outOfBounds(index);
         }
 
         private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
@@ -216,11 +397,20 @@
         }
 
         private Object writeReplace() {
-            return new CollSer(CollSer.IMM_LIST, e0, e1);
+            if (e1 == null) {
+                return new CollSer(CollSer.IMM_LIST, e0);
+            } else {
+                return new CollSer(CollSer.IMM_LIST, e0, e1);
+            }
         }
+
     }
 
-    static final class ListN<E> extends AbstractImmutableList<E> {
+    static final class ListN<E> extends AbstractImmutableList<E>
+            implements Serializable {
+
+        static final List<?> EMPTY_LIST = new ListN<>();
+
         @Stable
         private final E[] elements;
 
@@ -232,7 +422,12 @@
             for (int i = 0; i < input.length; i++) {
                 tmp[i] = Objects.requireNonNull(input[i]);
             }
-            this.elements = tmp;
+            elements = tmp;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return size() == 0;
         }
 
         @Override
@@ -242,29 +437,9 @@
 
         @Override
         public E get(int index) {
-            Objects.checkIndex(index, elements.length);
             return elements[index];
         }
 
-        @Override
-        public boolean contains(Object o) {
-            for (E e : elements) {
-                if (o.equals(e)) { // implicit nullcheck of o
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            int hash = 1;
-            for (E e : elements) {
-                hash = 31 * hash + e.hashCode();
-            }
-            return hash;
-        }
-
         private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
             throw new InvalidObjectException("not serial proxy");
         }
diff --git a/ojluni/src/main/java/java/util/Iterator.java b/ojluni/src/main/java/java/util/Iterator.java
index 7d2daf8..02777b6 100644
--- a/ojluni/src/main/java/java/util/Iterator.java
+++ b/ojluni/src/main/java/java/util/Iterator.java
@@ -40,7 +40,7 @@
  * </ul>
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <E> the type of elements returned by this iterator
diff --git a/ojluni/src/main/java/java/util/LinkedHashMap.java b/ojluni/src/main/java/java/util/LinkedHashMap.java
index 9d3815f..cd501a5 100644
--- a/ojluni/src/main/java/java/util/LinkedHashMap.java
+++ b/ojluni/src/main/java/java/util/LinkedHashMap.java
@@ -158,7 +158,7 @@
  * {@code LinkedHashMap}.
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @implNote
@@ -491,6 +491,9 @@
     // Android-added: eldest(), for internal use in LRU caches
     /**
      * Returns the eldest entry in the map, or {@code null} if the map is empty.
+     *
+     * @return eldest entry in the map, or {@code null} if the map is empty
+     *
      * @hide
      */
     public Map.Entry<K, V> eldest() {
diff --git a/ojluni/src/main/java/java/util/LinkedHashSet.java b/ojluni/src/main/java/java/util/LinkedHashSet.java
index 08606cf..c573d7c 100644
--- a/ojluni/src/main/java/java/util/LinkedHashSet.java
+++ b/ojluni/src/main/java/java/util/LinkedHashSet.java
@@ -100,7 +100,7 @@
  * should be used only to detect bugs.</i>
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <E> the type of elements maintained by this set
diff --git a/ojluni/src/main/java/java/util/LinkedList.java b/ojluni/src/main/java/java/util/LinkedList.java
index 60f4c41..2683a90 100644
--- a/ojluni/src/main/java/java/util/LinkedList.java
+++ b/ojluni/src/main/java/java/util/LinkedList.java
@@ -70,7 +70,7 @@
  * should be used only to detect bugs.</i>
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/util/List.java b/ojluni/src/main/java/java/util/List.java
index 1b9deb3..fca40ac 100644
--- a/ojluni/src/main/java/java/util/List.java
+++ b/ojluni/src/main/java/java/util/List.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -88,15 +88,16 @@
  * Such exceptions are marked as "optional" in the specification for this
  * interface.
  *
- * <h2><a id="immutable">Immutable List Static Factory Methods</a></h2>
- * <p>The {@link List#of(Object...) List.of()} static factory methods
- * provide a convenient way to create immutable lists. The {@code List}
+ * <h2><a id="unmodifiable">Unmodifiable Lists</a></h2>
+ * <p>The {@link List#of(Object...) List.of} and
+ * {@link List#copyOf List.copyOf} static factory methods
+ * provide a convenient way to create unmodifiable lists. The {@code List}
  * instances created by these methods have the following characteristics:
  *
  * <ul>
- * <li>They are <em>structurally immutable</em>. Elements cannot be added, removed,
- * or replaced. Calling any mutator method will always cause
- * {@code UnsupportedOperationException} to be thrown.
+ * <li>They are <a href="Collection.html#unmodifiable"><i>unmodifiable</i></a>. Elements cannot
+ * be added, removed, or replaced. Calling any mutator method on the List
+ * will always cause {@code UnsupportedOperationException} to be thrown.
  * However, if the contained elements are themselves mutable,
  * this may cause the List's contents to appear to change.
  * <li>They disallow {@code null} elements. Attempts to create them with
@@ -796,9 +797,9 @@
     }
 
     /**
-     * Returns an immutable list containing zero elements.
+     * Returns an unmodifiable list containing zero elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @return an empty {@code List}
@@ -806,13 +807,13 @@
      * @since 9
      */
     static <E> List<E> of() {
-        return ImmutableCollections.List0.instance();
+        return ImmutableCollections.emptyList();
     }
 
     /**
-     * Returns an immutable list containing one element.
+     * Returns an unmodifiable list containing one element.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the single element
@@ -822,13 +823,13 @@
      * @since 9
      */
     static <E> List<E> of(E e1) {
-        return new ImmutableCollections.List1<>(e1);
+        return new ImmutableCollections.List12<>(e1);
     }
 
     /**
-     * Returns an immutable list containing two elements.
+     * Returns an unmodifiable list containing two elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -839,13 +840,13 @@
      * @since 9
      */
     static <E> List<E> of(E e1, E e2) {
-        return new ImmutableCollections.List2<>(e1, e2);
+        return new ImmutableCollections.List12<>(e1, e2);
     }
 
     /**
-     * Returns an immutable list containing three elements.
+     * Returns an unmodifiable list containing three elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -861,9 +862,9 @@
     }
 
     /**
-     * Returns an immutable list containing four elements.
+     * Returns an unmodifiable list containing four elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -880,9 +881,9 @@
     }
 
     /**
-     * Returns an immutable list containing five elements.
+     * Returns an unmodifiable list containing five elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -900,9 +901,9 @@
     }
 
     /**
-     * Returns an immutable list containing six elements.
+     * Returns an unmodifiable list containing six elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -922,9 +923,9 @@
     }
 
     /**
-     * Returns an immutable list containing seven elements.
+     * Returns an unmodifiable list containing seven elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -945,9 +946,9 @@
     }
 
     /**
-     * Returns an immutable list containing eight elements.
+     * Returns an unmodifiable list containing eight elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -969,9 +970,9 @@
     }
 
     /**
-     * Returns an immutable list containing nine elements.
+     * Returns an unmodifiable list containing nine elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -994,9 +995,9 @@
     }
 
     /**
-     * Returns an immutable list containing ten elements.
+     * Returns an unmodifiable list containing ten elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -1020,8 +1021,8 @@
     }
 
     /**
-     * Returns an immutable list containing an arbitrary number of elements.
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * Returns an unmodifiable list containing an arbitrary number of elements.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @apiNote
      * This method also accepts a single array as an argument. The element type of
@@ -1049,13 +1050,33 @@
     static <E> List<E> of(E... elements) {
         switch (elements.length) { // implicit null check of elements
             case 0:
-                return ImmutableCollections.List0.instance();
+                return ImmutableCollections.emptyList();
             case 1:
-                return new ImmutableCollections.List1<>(elements[0]);
+                return new ImmutableCollections.List12<>(elements[0]);
             case 2:
-                return new ImmutableCollections.List2<>(elements[0], elements[1]);
+                return new ImmutableCollections.List12<>(elements[0], elements[1]);
             default:
                 return new ImmutableCollections.ListN<>(elements);
         }
     }
+
+    /**
+     * Returns an <a href="#unmodifiable">unmodifiable List</a> containing the elements of
+     * the given Collection, in its iteration order. The given Collection must not be null,
+     * and it must not contain any null elements. If the given Collection is subsequently
+     * modified, the returned List will not reflect such modifications.
+     *
+     * @implNote
+     * If the given Collection is an <a href="#unmodifiable">unmodifiable List</a>,
+     * calling copyOf will generally not create a copy.
+     *
+     * @param <E> the {@code List}'s element type
+     * @param coll a {@code Collection} from which elements are drawn, must be non-null
+     * @return a {@code List} containing the elements of the given {@code Collection}
+     * @throws NullPointerException if coll is null, or if it contains any nulls
+     * @since 10
+     */
+    static <E> List<E> copyOf(Collection<? extends E> coll) {
+        return ImmutableCollections.listCopy(coll);
+    }
 }
diff --git a/ojluni/src/main/java/java/util/ListIterator.java b/ojluni/src/main/java/java/util/ListIterator.java
index 6ad98fe..85a2f25 100644
--- a/ojluni/src/main/java/java/util/ListIterator.java
+++ b/ojluni/src/main/java/java/util/ListIterator.java
@@ -46,7 +46,7 @@
  * {@link #previous()}.
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @author  Josh Bloch
diff --git a/ojluni/src/main/java/java/util/Locale.java b/ojluni/src/main/java/java/util/Locale.java
index 1ce4a202..80f9ed7 100644
--- a/ojluni/src/main/java/java/util/Locale.java
+++ b/ojluni/src/main/java/java/util/Locale.java
@@ -2750,24 +2750,6 @@
     }
     */
 
-    // BEGIN Android-added: adjustLanguageCode(), for internal use only.
-    /** @hide for internal use only. */
-    public static String adjustLanguageCode(String languageCode) {
-        String adjusted = languageCode.toLowerCase(Locale.US);
-        // Map new language codes to the obsolete language
-        // codes so the correct resource bundles will be used.
-        if (languageCode.equals("he")) {
-            adjusted = "iw";
-        } else if (languageCode.equals("id")) {
-            adjusted = "in";
-        } else if (languageCode.equals("yi")) {
-            adjusted = "ji";
-        }
-
-        return adjusted;
-    }
-    // END Android-added: adjustLanguageCode(), for internal use only.
-
     /**
      * Enum for locale categories.  These locale categories are used to get/set
      * the default locale for the specific functionality represented by the
diff --git a/ojluni/src/main/java/java/util/Map.java b/ojluni/src/main/java/java/util/Map.java
index ed365ec..cf07a11 100644
--- a/ojluni/src/main/java/java/util/Map.java
+++ b/ojluni/src/main/java/java/util/Map.java
@@ -1653,4 +1653,30 @@
         // KeyValueHolder checks for nulls
         return new KeyValueHolder<>(k, v);
     }
+
+    /**
+     * Returns an <a href="#unmodifiable">unmodifiable Map</a> containing the entries
+     * of the given Map. The given Map must not be null, and it must not contain any
+     * null keys or values. If the given Map is subsequently modified, the returned
+     * Map will not reflect such modifications.
+     *
+     * @implNote
+     * If the given Map is an <a href="#unmodifiable">unmodifiable Map</a>,
+     * calling copyOf will generally not create a copy.
+     *
+     * @param <K> the {@code Map}'s key type
+     * @param <V> the {@code Map}'s value type
+     * @param map a {@code Map} from which entries are drawn, must be non-null
+     * @return a {@code Map} containing the entries of the given {@code Map}
+     * @throws NullPointerException if map is null, or if it contains any null keys or values
+     * @since 10
+     */
+    @SuppressWarnings({"rawtypes","unchecked"})
+    static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
+        if (map instanceof ImmutableCollections.AbstractImmutableMap) {
+            return (Map<K,V>)map;
+        } else {
+            return (Map<K,V>)Map.ofEntries(map.entrySet().toArray(new Entry[0]));
+        }
+    }
 }
diff --git a/ojluni/src/main/java/java/util/PriorityQueue.java b/ojluni/src/main/java/java/util/PriorityQueue.java
index 7c929bd..90e59d1 100644
--- a/ojluni/src/main/java/java/util/PriorityQueue.java
+++ b/ojluni/src/main/java/java/util/PriorityQueue.java
@@ -72,7 +72,7 @@
  * ({@code peek}, {@code element}, and {@code size}).
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @since 1.5
diff --git a/ojluni/src/main/java/java/util/RandomAccess.java b/ojluni/src/main/java/java/util/RandomAccess.java
index 09ce29e..6b7e453 100644
--- a/ojluni/src/main/java/java/util/RandomAccess.java
+++ b/ojluni/src/main/java/java/util/RandomAccess.java
@@ -59,7 +59,7 @@
  * </pre>
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @since 1.4
diff --git a/ojluni/src/main/java/java/util/Set.java b/ojluni/src/main/java/java/util/Set.java
index 0499ea3..91497ca 100644
--- a/ojluni/src/main/java/java/util/Set.java
+++ b/ojluni/src/main/java/java/util/Set.java
@@ -91,7 +91,7 @@
  * </ul>
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <E> the type of elements maintained by this set
@@ -700,4 +700,30 @@
                 return new ImmutableCollections.SetN<>(elements);
         }
     }
+
+    /**
+     * Returns an <a href="#unmodifiable">unmodifiable Set</a> containing the elements
+     * of the given Collection. The given Collection must not be null, and it must not
+     * contain any null elements. If the given Collection contains duplicate elements,
+     * an arbitrary element of the duplicates is preserved. If the given Collection is
+     * subsequently modified, the returned Set will not reflect such modifications.
+     *
+     * @implNote
+     * If the given Collection is an <a href="#unmodifiable">unmodifiable Set</a>,
+     * calling copyOf will generally not create a copy.
+     *
+     * @param <E> the {@code Set}'s element type
+     * @param coll a {@code Collection} from which elements are drawn, must be non-null
+     * @return a {@code Set} containing the elements of the given {@code Collection}
+     * @throws NullPointerException if coll is null, or if it contains any nulls
+     * @since 10
+     */
+    @SuppressWarnings("unchecked")
+    static <E> Set<E> copyOf(Collection<? extends E> coll) {
+        if (coll instanceof ImmutableCollections.AbstractImmutableSet) {
+            return (Set<E>)coll;
+        } else {
+            return (Set<E>)Set.of(new HashSet<>(coll).toArray());
+        }
+    }
 }
diff --git a/ojluni/src/main/java/java/util/SortedMap.java b/ojluni/src/main/java/java/util/SortedMap.java
index 7f98152..15ddba5 100644
--- a/ojluni/src/main/java/java/util/SortedMap.java
+++ b/ojluni/src/main/java/java/util/SortedMap.java
@@ -93,7 +93,7 @@
  *   SortedMap&lt;String, V&gt; sub = m.subMap(low+"\0", high);</pre>
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <K> the type of keys maintained by this map
diff --git a/ojluni/src/main/java/java/util/SortedSet.java b/ojluni/src/main/java/java/util/SortedSet.java
index 3ea9329..1ba72dd 100644
--- a/ojluni/src/main/java/java/util/SortedSet.java
+++ b/ojluni/src/main/java/java/util/SortedSet.java
@@ -89,7 +89,7 @@
  *   SortedSet&lt;String&gt; sub = s.subSet(low+"\0", high);</pre>
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <E> the type of elements maintained by this set
diff --git a/ojluni/src/main/java/java/util/TEST_MAPPING b/ojluni/src/main/java/java/util/TEST_MAPPING
index e1434fb..a8ec92d 100644
--- a/ojluni/src/main/java/java/util/TEST_MAPPING
+++ b/ojluni/src/main/java/java/util/TEST_MAPPING
@@ -10,7 +10,9 @@
           "include-filter": "org.apache.harmony.tests.java.util"
         }
       ]
-    },
+    }
+  ],
+  "presubmit-large": [
     {
       "name": "CtsLibcoreOjTestCases",
       "options": [
@@ -23,4 +25,4 @@
       ]
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/util/TimeZone.java b/ojluni/src/main/java/java/util/TimeZone.java
index cf5e5f8..265a59d 100644
--- a/ojluni/src/main/java/java/util/TimeZone.java
+++ b/ojluni/src/main/java/java/util/TimeZone.java
@@ -40,6 +40,10 @@
 package java.util;
 
 import android.icu.text.TimeZoneNames;
+import com.android.i18n.timezone.ZoneInfoData;
+import com.android.i18n.timezone.ZoneInfoDb;
+import com.android.icu.util.ExtendedTimeZone;
+
 import java.io.IOException;
 import java.io.Serializable;
 import java.time.ZoneId;
@@ -47,7 +51,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import libcore.io.IoUtils;
-import libcore.timezone.ZoneInfoDb;
+import libcore.util.ZoneInfo;
 
 import dalvik.system.RuntimeHooks;
 
@@ -574,11 +578,9 @@
         }
 
         // In the database?
-        TimeZone zone = null;
-        try {
-            zone = ZoneInfoDb.getInstance().makeTimeZone(id);
-        } catch (IOException ignored) {
-        }
+
+        ZoneInfoData zoneInfoData = ZoneInfoDb.getInstance().makeZoneInfoData(id);
+        TimeZone zone = zoneInfoData == null ? null : ZoneInfo.createZoneInfo(zoneInfoData);
 
         // Custom time zone?
         if (zone == null && id.length() > 3 && id.startsWith("GMT")) {
@@ -745,7 +747,7 @@
         }
         defaultTimeZone = timeZone != null ? (TimeZone) timeZone.clone() : null;
         // Android-changed: notify ICU4J of changed default TimeZone.
-        android.icu.util.TimeZone.setICUDefault(null);
+        ExtendedTimeZone.clearDefaultTimeZone();
     }
 
     /**
diff --git a/ojluni/src/main/java/java/util/TreeMap.java b/ojluni/src/main/java/java/util/TreeMap.java
index 20d98bc..63d7c35 100644
--- a/ojluni/src/main/java/java/util/TreeMap.java
+++ b/ojluni/src/main/java/java/util/TreeMap.java
@@ -93,7 +93,7 @@
  * associated map using {@code put}.)
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <K> the type of keys maintained by this map
@@ -1343,7 +1343,7 @@
      */
     abstract static class NavigableSubMap<K,V> extends AbstractMap<K,V>
         implements NavigableMap<K,V>, java.io.Serializable {
-        // Android-changed: Explicitly add a serialVersionUID so that we're serialization
+        // Android-changed: Explicitly add a serialVersionUID so that we're serialization.
         // compatible with the Java-7 version of this class. Several new methods were added
         // in Java-8 but none of them have any bearing on the serialized format of the class
         // or require any additional state to be preserved.
@@ -1877,11 +1877,11 @@
         }
 
         public NavigableMap<K,V> headMap(K toKey, boolean inclusive) {
-            // BEGIN Android-changed: Fix for edge cases
+            // BEGIN Android-changed: Fix for edge cases.
             // if (!inRange(toKey, inclusive))
             if (!inRange(toKey) && !(!toEnd && m.compare(toKey, hi) == 0 &&
                 !hiInclusive && !inclusive))
-            // END Android-changed: Fix for edge cases
+            // END Android-changed: Fix for edge cases.
                 throw new IllegalArgumentException("toKey out of range");
             return new AscendingSubMap<>(m,
                                          fromStart, lo,    loInclusive,
@@ -1889,11 +1889,11 @@
         }
 
         public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
-            // BEGIN Android-changed: Fix for edge cases
+            // BEGIN Android-changed: Fix for edge cases.
             // if (!inRange(fromKey, inclusive))
             if (!inRange(fromKey) && !(!fromStart && m.compare(fromKey, lo) == 0 &&
                 !loInclusive && !inclusive))
-            // END Android-changed: Fix for edge cases
+            // END Android-changed: Fix for edge cases.
                 throw new IllegalArgumentException("fromKey out of range");
             return new AscendingSubMap<>(m,
                                          false, fromKey, inclusive,
@@ -1970,11 +1970,11 @@
         }
 
         public NavigableMap<K,V> headMap(K toKey, boolean inclusive) {
-            // BEGIN Android-changed: Fix for edge cases
+            // BEGIN Android-changed: Fix for edge cases.
             // if (!inRange(toKey, inclusive))
             if (!inRange(toKey) && !(!fromStart && m.compare(toKey, lo) == 0 &&
                 !loInclusive && !inclusive))
-            // END Android-changed: Fix for edge cases
+            // END Android-changed: Fix for edge cases.
                 throw new IllegalArgumentException("toKey out of range");
             return new DescendingSubMap<>(m,
                                           false, toKey, inclusive,
@@ -1982,11 +1982,11 @@
         }
 
         public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
-            // BEGIN Android-changed: Fix for edge cases
+            // BEGIN Android-changed: Fix for edge cases.
             // if (!inRange(fromKey, inclusive))
             if (!inRange(fromKey) && !(!toEnd && m.compare(fromKey, hi) == 0 &&
                 !hiInclusive && !inclusive))
-            // END Android-changed: Fix for edge cases
+            // END Android-changed: Fix for edge cases.
                 throw new IllegalArgumentException("fromKey out of range");
             return new DescendingSubMap<>(m,
                                           fromStart, lo, loInclusive,
diff --git a/ojluni/src/main/java/java/util/TreeSet.java b/ojluni/src/main/java/java/util/TreeSet.java
index 54eeeda..be215ba 100644
--- a/ojluni/src/main/java/java/util/TreeSet.java
+++ b/ojluni/src/main/java/java/util/TreeSet.java
@@ -74,7 +74,7 @@
  * should be used only to detect bugs.</i>
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <E> the type of elements maintained by this set
diff --git a/ojluni/src/main/java/java/util/Vector.java b/ojluni/src/main/java/java/util/Vector.java
index f184437..6c5247c 100644
--- a/ojluni/src/main/java/java/util/Vector.java
+++ b/ojluni/src/main/java/java/util/Vector.java
@@ -68,7 +68,7 @@
  *
  * <p>As of the Java 2 platform v1.2, this class was retrofitted to
  * implement the {@link List} interface, making it a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.  Unlike the new collection
  * implementations, {@code Vector} is synchronized.  If a thread-safe
  * implementation is not needed, it is recommended to use {@link
diff --git a/ojluni/src/main/java/java/util/WeakHashMap.java b/ojluni/src/main/java/java/util/WeakHashMap.java
index 7352391..68738df 100644
--- a/ojluni/src/main/java/java/util/WeakHashMap.java
+++ b/ojluni/src/main/java/java/util/WeakHashMap.java
@@ -120,7 +120,7 @@
  * should be used only to detect bugs.</i>
  *
  * <p>This class is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @param <K> the type of keys maintained by this map
diff --git a/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java b/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java
index 290fee1..da1a4fd 100644
--- a/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java
+++ b/ojluni/src/main/java/java/util/concurrent/BlockingDeque.java
@@ -197,7 +197,7 @@
  * the {@code BlockingDeque} in another thread.
  *
  * <p>This interface is a member of the
- * <a href="{@docRoot}/../technotes/guides/collections/index.html">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html">
  * Java Collections Framework</a>.
  *
  * @since 1.6
diff --git a/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java b/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java
index 976bfe4..e98e1be 100644
--- a/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java
+++ b/ojluni/src/main/java/java/util/concurrent/CompletableFuture.java
@@ -2407,7 +2407,6 @@
      * @param <U> the type of the value
      * @return a new CompletableFuture
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public <U> CompletableFuture<U> newIncompleteFuture() {
         return new CompletableFuture<U>();
@@ -2423,7 +2422,6 @@
      *
      * @return the executor
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public Executor defaultExecutor() {
         return ASYNC_POOL;
@@ -2442,7 +2440,6 @@
      *
      * @return the new CompletableFuture
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public CompletableFuture<T> copy() {
         return uniCopyStage();
@@ -2460,7 +2457,6 @@
      *
      * @return the new CompletionStage
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public CompletionStage<T> minimalCompletionStage() {
         return uniAsMinimalStage();
@@ -2476,7 +2472,6 @@
      * @param executor the executor to use for asynchronous execution
      * @return this CompletableFuture
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier,
                                               Executor executor) {
@@ -2495,7 +2490,6 @@
      * to complete this CompletableFuture
      * @return this CompletableFuture
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public CompletableFuture<T> completeAsync(Supplier<? extends T> supplier) {
         return completeAsync(supplier, defaultExecutor());
@@ -2512,7 +2506,6 @@
      *        {@code timeout} parameter
      * @return this CompletableFuture
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit) {
         if (unit == null)
@@ -2534,7 +2527,6 @@
      *        {@code timeout} parameter
      * @return this CompletableFuture
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public CompletableFuture<T> completeOnTimeout(T value, long timeout,
                                                   TimeUnit unit) {
@@ -2559,7 +2551,6 @@
      * @param executor the base executor
      * @return the new delayed executor
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public static Executor delayedExecutor(long delay, TimeUnit unit,
                                            Executor executor) {
@@ -2579,7 +2570,6 @@
      *        {@code delay} parameter
      * @return the new delayed executor
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public static Executor delayedExecutor(long delay, TimeUnit unit) {
         if (unit == null)
@@ -2596,7 +2586,6 @@
      * @param <U> the type of the value
      * @return the completed CompletionStage
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public static <U> CompletionStage<U> completedStage(U value) {
         return new MinimalStage<U>((value == null) ? NIL : value);
@@ -2610,7 +2599,6 @@
      * @param <U> the type of the value
      * @return the exceptionally completed CompletableFuture
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public static <U> CompletableFuture<U> failedFuture(Throwable ex) {
         if (ex == null) throw new NullPointerException();
@@ -2626,7 +2614,6 @@
      * @param <U> the type of the value
      * @return the exceptionally completed CompletionStage
      * @since 9
-     * @hide API from OpenJDK 9, not yet exposed on Android.
      */
     public static <U> CompletionStage<U> failedStage(Throwable ex) {
         if (ex == null) throw new NullPointerException();
diff --git a/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
index c32e2d1..6b9fcc3 100644
--- a/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
+++ b/ojluni/src/main/java/java/util/concurrent/ScheduledExecutorService.java
@@ -80,7 +80,7 @@
  *     final Runnable beeper = new Runnable() {
  *       public void run() { System.out.println("beep"); }
  *     };
- *     final ScheduledFuture<?> beeperHandle =
+ *     final ScheduledFuture&lt;?&lt; beeperHandle =
  *       scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
  *     scheduler.schedule(new Runnable() {
  *       public void run() { beeperHandle.cancel(true); }
diff --git a/ojluni/src/main/java/java/util/jar/Pack200.java b/ojluni/src/main/java/java/util/jar/Pack200.java
index 9211f0a..26b2ce3 100644
--- a/ojluni/src/main/java/java/util/jar/Pack200.java
+++ b/ojluni/src/main/java/java/util/jar/Pack200.java
@@ -96,7 +96,7 @@
  * The deployment applications can use "Accept-Encoding=pack200-gzip". This
  * indicates to the server that the client application desires a version of
  * the file encoded with Pack200 and further compressed with gzip. Please
- * refer to  <a href="{@docRoot}/../technotes/guides/deployment/deployment-guide/pack200.html">Java Deployment Guide</a> for more details and
+ * refer to  <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/deployment/deployment-guide/pack200.html">Java Deployment Guide</a> for more details and
  * techniques.
  * <p>
  * Unless otherwise noted, passing a <tt>null</tt> argument to a constructor or
diff --git a/ojluni/src/main/java/java/util/regex/Pattern.java b/ojluni/src/main/java/java/util/regex/Pattern.java
index a4ead58..bd7ba5b 100644
--- a/ojluni/src/main/java/java/util/regex/Pattern.java
+++ b/ojluni/src/main/java/java/util/regex/Pattern.java
@@ -883,7 +883,10 @@
      */
     public static final int UNICODE_CASE = 0x40;
 
+    // Android-changed: Android does not support CANON_EQ flag.
     /**
+     * This flag is not supported on Android.
+     *
      * Enables canonical equivalence.
      *
      * <p> When this flag is specified then two characters will be considered
@@ -901,15 +904,15 @@
 
     // Android-changed: Android always uses unicode character classes.
     /**
+     * This flag is not supported on Android, and Unicode character classes are always
+     * used.
+     *
      * Enables the Unicode version of <i>Predefined character classes</i> and
      * <i>POSIX character classes</i> as defined by <a href="http://www.unicode.org/reports/tr18/"><i>Unicode Technical
      * Standard #18: Unicode Regular Expression</i></a>
      * <i>Annex C: Compatibility Properties</i>.
      * <p>
      *
-     * This flag has no effect on Android, unicode character classes are always
-     * used.
-     *
      * @since 1.7
      */
     public static final int UNICODE_CHARACTER_CLASS = 0x100;
@@ -959,6 +962,7 @@
         return new Pattern(regex, 0);
     }
 
+    // Android-changed: Android doesn't support CANON_EQ and UNICODE_CHARACTER_CLASS flags.
     /**
      * Compiles the given regular expression into a pattern with the given
      * flags.
@@ -969,8 +973,7 @@
      * @param  flags
      *         Match flags, a bit mask that may include
      *         {@link #CASE_INSENSITIVE}, {@link #MULTILINE}, {@link #DOTALL},
-     *         {@link #UNICODE_CASE}, {@link #CANON_EQ}, {@link #UNIX_LINES},
-     *         {@link #LITERAL}, {@link #UNICODE_CHARACTER_CLASS}
+     *         {@link #UNICODE_CASE}, {@link #UNIX_LINES}, {@link #LITERAL},
      *         and {@link #COMMENTS}
      *
      * @return the given regular expression compiled into a pattern with the given flags
diff --git a/ojluni/src/main/java/java/util/stream/TEST_MAPPING b/ojluni/src/main/java/java/util/stream/TEST_MAPPING
index 9673e09..d2efaa1 100644
--- a/ojluni/src/main/java/java/util/stream/TEST_MAPPING
+++ b/ojluni/src/main/java/java/util/stream/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "CtsLibcoreOjTestCases",
       "options": [
@@ -15,4 +15,4 @@
       ]
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/ojluni/src/main/java/java/util/zip/ZipEntry.java b/ojluni/src/main/java/java/util/zip/ZipEntry.java
index 0de6756..b9c1440 100644
--- a/ojluni/src/main/java/java/util/zip/ZipEntry.java
+++ b/ojluni/src/main/java/java/util/zip/ZipEntry.java
@@ -164,11 +164,12 @@
      */
     ZipEntry() {}
 
-    // Android-added: Add dataOffset for internal use.
+    // BEGIN Android-added: Add dataOffset for internal use.
     /** @hide */
     public long getDataOffset() {
         return dataOffset;
     }
+    // END Android-added: Add dataOffset for internal use.
 
     /**
      * Returns the name of the entry.
diff --git a/ojluni/src/main/java/java/util/zip/ZipFile.java b/ojluni/src/main/java/java/util/zip/ZipFile.java
index 851aab1..e06fb6d 100644
--- a/ojluni/src/main/java/java/util/zip/ZipFile.java
+++ b/ojluni/src/main/java/java/util/zip/ZipFile.java
@@ -81,7 +81,7 @@
     private final boolean locsig;  // if zip file starts with LOCSIG (usually true)
     private volatile boolean closeRequested = false;
 
-    // Android-added: CloseGuard support
+    // Android-added: CloseGuard support.
     private final CloseGuard guard = CloseGuard.get();
 
     // Android-added: Do not use unlink() to implement OPEN_DELETE.
@@ -243,7 +243,7 @@
                                                Integer.toHexString(mode));
         }
         String name = file.getPath();
-        // Android-removed: SecurityManager is always null
+        // Android-removed: SecurityManager is always null.
         /*
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
@@ -260,16 +260,16 @@
         if (charset == null)
             throw new NullPointerException("charset is null");
         this.zc = ZipCoder.get(charset);
-        // Android-removed: Skip perf counters
+        // Android-removed: Skip perf counters.
         // long t0 = System.nanoTime();
         jzfile = open(name, mode, file.lastModified(), usemmap);
-        // Android-removed: Skip perf counters
+        // Android-removed: Skip perf counters.
         // sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
         // sun.misc.PerfCounter.getZipFileCount().increment();
         this.name = name;
         this.total = getTotal(jzfile);
         this.locsig = startsWithLOC(jzfile);
-        // Android-added: CloseGuard support
+        // Android-added: CloseGuard support.
         guard.open("close");
     }
 
@@ -668,7 +668,7 @@
     public void close() throws IOException {
         if (closeRequested)
             return;
-        // Android-added: CloseGuard support
+        // Android-added: CloseGuard support.
         if (guard != null) {
             guard.close();
         }
@@ -739,7 +739,7 @@
      * @see    java.util.zip.ZipFile#close()
      */
     protected void finalize() throws IOException {
-        // Android-added: CloseGuard support
+        // Android-added: CloseGuard support.
         if (guard != null) {
             guard.warnIfOpen();
         }
diff --git a/ojluni/src/main/java/javax/crypto/Cipher.java b/ojluni/src/main/java/javax/crypto/Cipher.java
index 7968f2f..a872317 100644
--- a/ojluni/src/main/java/javax/crypto/Cipher.java
+++ b/ojluni/src/main/java/javax/crypto/Cipher.java
@@ -242,7 +242,7 @@
  * </table>
  *
  * These transformations are described in the
- * <a href="{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
  * Cipher section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -597,7 +597,7 @@
      * @param transformation the name of the transformation, e.g.,
      * <i>DES/CBC/PKCS5Padding</i>.
      * See the Cipher section in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard transformation names.
      *
@@ -634,7 +634,7 @@
      * @param transformation the name of the transformation,
      * e.g., <i>DES/CBC/PKCS5Padding</i>.
      * See the Cipher section in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard transformation names.
      *
@@ -686,7 +686,7 @@
      * @param transformation the name of the transformation,
      * e.g., <i>DES/CBC/PKCS5Padding</i>.
      * See the Cipher section in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Cipher">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard transformation names.
      *
@@ -2477,7 +2477,7 @@
      * For more information on default key size in JCE jurisdiction
      * policy files, please see Appendix E in the
      * <a href=
-     *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppC">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppC">
      * Java Cryptography Architecture Reference Guide</a>.
      *
      * @param transformation the cipher transformation.
diff --git a/ojluni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java b/ojluni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
index 5eeb481..b8bf169 100644
--- a/ojluni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
+++ b/ojluni/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java
@@ -115,7 +115,7 @@
      *
      * @param algName encryption algorithm name. See Appendix A in the
      * <a href=
-     *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
      * Java Cryptography Architecture Reference Guide</a>
      * for information about standard Cipher algorithm names.
      * @param encryptedData encrypted data. The contents of
@@ -198,7 +198,7 @@
      * in the constructor when such mapping is available.
      * See Appendix A in the
      * <a href=
-     *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
      * Java Cryptography Architecture Reference Guide</a>
      * for information about standard Cipher algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/crypto/ExemptionMechanism.java b/ojluni/src/main/java/javax/crypto/ExemptionMechanism.java
index b90ab43..f991a66 100644
--- a/ojluni/src/main/java/javax/crypto/ExemptionMechanism.java
+++ b/ojluni/src/main/java/javax/crypto/ExemptionMechanism.java
@@ -116,7 +116,7 @@
      * mechanism.
      * See the ExemptionMechanism section in the
      * <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Exemption">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard exemption mechanism names.
      *
@@ -155,7 +155,7 @@
      * @param algorithm the standard name of the requested exemption mechanism.
      * See the ExemptionMechanism section in the
      * <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Exemption">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard exemption mechanism names.
      *
@@ -199,7 +199,7 @@
      * @param algorithm the standard name of the requested exemption mechanism.
      * See the ExemptionMechanism section in the
      * <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Exemption">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard exemption mechanism names.
      *
diff --git a/ojluni/src/main/java/javax/crypto/KeyAgreement.java b/ojluni/src/main/java/javax/crypto/KeyAgreement.java
index fd79680..f6decbc 100644
--- a/ojluni/src/main/java/javax/crypto/KeyAgreement.java
+++ b/ojluni/src/main/java/javax/crypto/KeyAgreement.java
@@ -75,7 +75,7 @@
  * </table>
  *
  * This algorithm is described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyAgreement">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyAgreement">
  * KeyAgreement section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -177,7 +177,7 @@
      * @param algorithm the standard name of the requested key agreement
      * algorithm.
      * See the KeyAgreement section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyAgreement">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyAgreement">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -226,7 +226,7 @@
      * @param algorithm the standard name of the requested key agreement
      * algorithm.
      * See the KeyAgreement section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyAgreement">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyAgreement">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -272,7 +272,7 @@
      * @param algorithm the standard name of the requested key agreement
      * algorithm.
      * See the KeyAgreement section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyAgreement">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyAgreement">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/crypto/KeyGenerator.java b/ojluni/src/main/java/javax/crypto/KeyGenerator.java
index 5951eef..0b30338 100644
--- a/ojluni/src/main/java/javax/crypto/KeyGenerator.java
+++ b/ojluni/src/main/java/javax/crypto/KeyGenerator.java
@@ -156,7 +156,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyGenerator">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyGenerator">
  * KeyGenerator section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -272,7 +272,7 @@
      *
      * @param algorithm the standard name of the requested key algorithm.
      * See the KeyGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -305,7 +305,7 @@
      *
      * @param algorithm the standard name of the requested key algorithm.
      * See the KeyGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -349,7 +349,7 @@
      *
      * @param algorithm the standard name of the requested key algorithm.
      * See the KeyGenerator section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#KeyGenerator">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyGenerator">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/crypto/Mac.java b/ojluni/src/main/java/javax/crypto/Mac.java
index f70a697..749b627 100644
--- a/ojluni/src/main/java/javax/crypto/Mac.java
+++ b/ojluni/src/main/java/javax/crypto/Mac.java
@@ -142,7 +142,7 @@
  * </table>
  *
  * These algorithms are described in the
- * <a href="{@docRoot}/../technotes/guides/security/StandardNames.html#Mac">
+ * <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac">
  * Mac section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -243,7 +243,7 @@
      *
      * @param algorithm the standard name of the requested MAC algorithm.
      * See the Mac section in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -287,7 +287,7 @@
      *
      * @param algorithm the standard name of the requested MAC algorithm.
      * See the Mac section in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -327,7 +327,7 @@
      *
      * @param algorithm the standard name of the requested MAC algorithm.
      * See the Mac section in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Mac">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/crypto/SecretKeyFactory.java b/ojluni/src/main/java/javax/crypto/SecretKeyFactory.java
index bd8b271..659df8d 100644
--- a/ojluni/src/main/java/javax/crypto/SecretKeyFactory.java
+++ b/ojluni/src/main/java/javax/crypto/SecretKeyFactory.java
@@ -251,7 +251,7 @@
  * </table>
  *
  * These algorithms are described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecretKeyFactory">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory">
  * SecretKeyFactory section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -324,7 +324,7 @@
      * @param algorithm the standard name of the requested secret-key
      * algorithm.
      * See the SecretKeyFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecretKeyFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -359,7 +359,7 @@
      * @param algorithm the standard name of the requested secret-key
      * algorithm.
      * See the SecretKeyFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecretKeyFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
@@ -405,7 +405,7 @@
      * @param algorithm the standard name of the requested secret-key
      * algorithm.
      * See the SecretKeyFactory section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SecretKeyFactory">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory">
      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/crypto/interfaces/package.html b/ojluni/src/main/java/javax/crypto/interfaces/package.html
index 8b18ce2..75ef6fe 100644
--- a/ojluni/src/main/java/javax/crypto/interfaces/package.html
+++ b/ojluni/src/main/java/javax/crypto/interfaces/package.html
@@ -46,7 +46,7 @@
 cryptographic provider developer guide:
 <ul>
   <li><a href=
-    "{@docRoot}/../technotes/guides/security/crypto/HowToImplAProvider.html">
+    "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
     <b>How to Implement a Provider for the
     Java<FONT SIZE=-2><SUP>TM</SUP></FONT> Cryptography Architecture
     </b></a></li>
@@ -65,7 +65,7 @@
 <ul>
   <li>
     <a href=
-      "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html">
+      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">
       <b>Java<FONT SIZE=-2><SUP>TM</SUP></FONT>
       Cryptography Architecture API Specification and Reference
       </b></a></li>
diff --git a/ojluni/src/main/java/javax/crypto/package.html b/ojluni/src/main/java/javax/crypto/package.html
index 1518573..bd4c05a 100644
--- a/ojluni/src/main/java/javax/crypto/package.html
+++ b/ojluni/src/main/java/javax/crypto/package.html
@@ -47,7 +47,7 @@
 <h2>Package Specification</h2>
 
 <ul>
-  <li><a href="{@docRoot}/../technotes/guides/security/StandardNames.html"><b>
+  <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html"><b>
     <b>Java<FONT SIZE=-2><SUP>TM</SUP></FONT>
     Cryptography Architecture Standard Algorithm Name
     Documentation</b></a></li>
@@ -59,13 +59,13 @@
 <ul>
   <li>
     <a href=
-      "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html">
+      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">
       <b>Java<FONT SIZE=-2><SUP>TM</SUP></FONT>
        Cryptography Architecture (JCA) Reference Guide
       </b></a></li>
   <li>
     <a href=
-      "{@docRoot}/../technotes/guides/security/crypto/HowToImplAProvider.html">
+      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
       <b>How to Implement a Provider in the
       Java<FONT SIZE=-2><SUP>TM</SUP></FONT> Cryptography Architecture
       </b></a></li>
diff --git a/ojluni/src/main/java/javax/crypto/spec/SecretKeySpec.java b/ojluni/src/main/java/javax/crypto/spec/SecretKeySpec.java
index 767c0d2..ff1258c 100644
--- a/ojluni/src/main/java/javax/crypto/spec/SecretKeySpec.java
+++ b/ojluni/src/main/java/javax/crypto/spec/SecretKeySpec.java
@@ -82,7 +82,7 @@
      * @param algorithm the name of the secret-key algorithm to be associated
      * with the given key material.
      * See Appendix A in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
      * Java Cryptography Architecture Reference Guide</a>
      * for information about standard algorithm names.
      * @exception IllegalArgumentException if <code>algorithm</code>
@@ -127,7 +127,7 @@
      * @param algorithm the name of the secret-key algorithm to be associated
      * with the given key material.
      * See Appendix A in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
      * Java Cryptography Architecture Reference Guide</a>
      * for information about standard algorithm names.
      * @exception IllegalArgumentException if <code>algorithm</code>
diff --git a/ojluni/src/main/java/javax/crypto/spec/package.html b/ojluni/src/main/java/javax/crypto/spec/package.html
index b8fd807..3e30aa1 100644
--- a/ojluni/src/main/java/javax/crypto/spec/package.html
+++ b/ojluni/src/main/java/javax/crypto/spec/package.html
@@ -61,13 +61,13 @@
 <ul>
   <li>
     <a href=
-      "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html">
+      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html">
       <b>Java<FONT SIZE=-2><SUP>TM</SUP></FONT>
       Cryptography Architecture API Specification and Reference
       </b></a></li>
   <li>
     <a href=
-      "{@docRoot}/../technotes/guides/security/crypto/HowToImplAProvider.html">
+      "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html">
       <b>How to Implement a Provider for the
       Java<FONT SIZE=-2><SUP>TM</SUP></FONT> Cryptography Architecture
       </b></a></li>
diff --git a/ojluni/src/main/java/javax/net/ssl/ExtendedSSLSession.java b/ojluni/src/main/java/javax/net/ssl/ExtendedSSLSession.java
index 70f98ce..2d832f3 100644
--- a/ojluni/src/main/java/javax/net/ssl/ExtendedSSLSession.java
+++ b/ojluni/src/main/java/javax/net/ssl/ExtendedSSLSession.java
@@ -45,7 +45,7 @@
      * The signature algorithm name must be a standard Java Security
      * name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
      * See Appendix A in the <a href=
-     * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
      * Java Cryptography Architecture API Specification &amp; Reference </a>
      * for information about standard algorithm names.
      * <p>
@@ -73,7 +73,7 @@
      * The signature algorithm name must be a standard Java Security
      * name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
      * See Appendix A in the <a href=
-     * "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
      * Java Cryptography Architecture API Specification &amp; Reference </a>
      * for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
index c3e3c30..9d5a738 100644
--- a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
+++ b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -30,7 +30,6 @@
 import java.net.HttpURLConnection;
 import java.security.Principal;
 import java.security.cert.X509Certificate;
-import libcore.api.CorePlatformApi;
 
 /**
  * <code>HttpsURLConnection</code> extends <code>HttpURLConnection</code>
@@ -324,16 +323,18 @@
 
     // BEGIN Android-added: Core platform API to obtain a strict hostname verifier
     /**
-     * Obtains a stricter <code>HostnameVerifier</code>.
+     * Obtains a stricter {@code HostnameVerifier}.
      *
-     * The <code>HostnameVerifier</code> returned by this method will reject certificates
+     * The {@code HostnameVerifier} returned by this method will reject certificates
      * with wildcards for top-level domains such "*.com".
      *
+     * This is a vendor hook (called from Zygote init code) to allow stricter hostname
+     * checking on NIAP-certified devices.
+     *
      * @see com.squareup.okhttp.internal.tls.OkHostnameVerifier
      *
      * @hide
      */
-    @CorePlatformApi
     public static HostnameVerifier getStrictHostnameVerifier() {
         try {
             return (HostnameVerifier) Class
diff --git a/ojluni/src/main/java/javax/net/ssl/KeyManagerFactory.java b/ojluni/src/main/java/javax/net/ssl/KeyManagerFactory.java
index 7ae790c..04af51a 100644
--- a/ojluni/src/main/java/javax/net/ssl/KeyManagerFactory.java
+++ b/ojluni/src/main/java/javax/net/ssl/KeyManagerFactory.java
@@ -134,7 +134,7 @@
      *
      * @param algorithm the standard name of the requested algorithm.
      *          See the <a href=
-     *  "{@docRoot}/../technotes/guides/security/jsse/JSSERefGuide.html">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html">
      *          Java Secure Socket Extension Reference Guide </a>
      *          for information about standard algorithm names.
      *
@@ -170,7 +170,7 @@
 
      * @param algorithm the standard name of the requested algorithm.
      *          See the <a href=
-     *  "{@docRoot}/../technotes/guides/security/jsse/JSSERefGuide.html">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html">
      *          Java Secure Socket Extension Reference Guide </a>
      *          for information about standard algorithm names.
      *
@@ -211,7 +211,7 @@
      *
      * @param algorithm the standard name of the requested algorithm.
      *          See the <a href=
-     *  "{@docRoot}/../technotes/guides/security/jsse/JSSERefGuide.html">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html">
      *          Java Secure Socket Extension Reference Guide </a>
      *          for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLContext.java b/ojluni/src/main/java/javax/net/ssl/SSLContext.java
index eb7322c..b580a72 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLContext.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLContext.java
@@ -82,7 +82,7 @@
  * </table>
  *
  * This protocol is described in the <a href=
- * "{@docRoot}/../technotes/guides/security/StandardNames.html#SSLContext">
+ * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">
  * SSLContext section</a> of the
  * Java Cryptography Architecture Standard Algorithm Name Documentation.
  *
@@ -180,7 +180,7 @@
      *
      * @param protocol the standard name of the requested protocol.
      *          See the SSLContext section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SSLContext">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">
      *          Java Cryptography Architecture Standard Algorithm Name
      *          Documentation</a>
      *          for information about standard protocol names.
@@ -216,7 +216,7 @@
      *
      * @param protocol the standard name of the requested protocol.
      *          See the SSLContext section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SSLContext">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">
      *          Java Cryptography Architecture Standard Algorithm Name
      *          Documentation</a>
      *          for information about standard protocol names.
@@ -256,7 +256,7 @@
      *
      * @param protocol the standard name of the requested protocol.
      *          See the SSLContext section in the <a href=
-     * "{@docRoot}/../technotes/guides/security/StandardNames.html#SSLContext">
+     * "https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SSLContext">
      *          Java Cryptography Architecture Standard Algorithm Name
      *          Documentation</a>
      *          for information about standard protocol names.
diff --git a/ojluni/src/main/java/javax/net/ssl/SSLParameters.java b/ojluni/src/main/java/javax/net/ssl/SSLParameters.java
index 1b9b3fb..41dcf0f 100644
--- a/ojluni/src/main/java/javax/net/ssl/SSLParameters.java
+++ b/ojluni/src/main/java/javax/net/ssl/SSLParameters.java
@@ -257,7 +257,7 @@
      *
      * @param algorithm The standard string name of the endpoint
      *     identification algorithm (or null).  See Appendix A in the <a href=
-     *   "{@docRoot}/../technotes/guides/security/crypto/CryptoSpec.html#AppA">
+     *   "https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#AppA">
      *     Java Cryptography Architecture API Specification &amp; Reference </a>
      *     for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/net/ssl/TrustManagerFactory.java b/ojluni/src/main/java/javax/net/ssl/TrustManagerFactory.java
index d76f5fb..7322169 100644
--- a/ojluni/src/main/java/javax/net/ssl/TrustManagerFactory.java
+++ b/ojluni/src/main/java/javax/net/ssl/TrustManagerFactory.java
@@ -136,7 +136,7 @@
      *
      * @param algorithm the standard name of the requested trust management
      *          algorithm.  See the <a href=
-     *  "{@docRoot}/../technotes/guides/security/jsse/JSSERefGuide.html">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html">
      *          Java Secure Socket Extension Reference Guide </a>
      *          for information about standard algorithm names.
      *
@@ -172,7 +172,7 @@
      *
      * @param algorithm the standard name of the requested trust management
      *          algorithm.  See the <a href=
-     *  "{@docRoot}/../technotes/guides/security/jsse/JSSERefGuide.html">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html">
      *          Java Secure Socket Extension Reference Guide </a>
      *          for information about standard algorithm names.
      *
@@ -213,7 +213,7 @@
      *
      * @param algorithm the standard name of the requested trust management
      *          algorithm.  See the <a href=
-     *  "{@docRoot}/../technotes/guides/security/jsse/JSSERefGuide.html">
+     *  "https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html">
      *          Java Secure Socket Extension Reference Guide </a>
      *          for information about standard algorithm names.
      *
diff --git a/ojluni/src/main/java/javax/net/ssl/package.html b/ojluni/src/main/java/javax/net/ssl/package.html
index 5213137..0f87f45 100644
--- a/ojluni/src/main/java/javax/net/ssl/package.html
+++ b/ojluni/src/main/java/javax/net/ssl/package.html
@@ -37,7 +37,7 @@
 <h2>Package Specification</h2>
 
 <ul>
-  <li><a href="{@docRoot}/../technotes/guides/security/StandardNames.html">
+  <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">
     <b>Java<FONT SIZE=-2><SUP>TM</SUP></FONT>
     Cryptography Architecture Standard Algorithm Name
     Documentation</b></a></li>
diff --git a/ojluni/src/main/java/javax/security/auth/login/package-info.java b/ojluni/src/main/java/javax/security/auth/login/package-info.java
index 5b43480..6bb5a06 100644
--- a/ojluni/src/main/java/javax/security/auth/login/package-info.java
+++ b/ojluni/src/main/java/javax/security/auth/login/package-info.java
@@ -28,7 +28,7 @@
  * <h2>Package Specification</h2>
  *
  * <ul>
- *   <li><a href="{@docRoot}/../technotes/guides/security/StandardNames.html">
+ *   <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">
  *     <b>Java&trade;
  *     Cryptography Architecture Standard Algorithm Name
  *     Documentation</b></a></li>
diff --git a/ojluni/src/main/java/sun/misc/Cleaner.java b/ojluni/src/main/java/sun/misc/Cleaner.java
index 5e92f35..e4826f6 100644
--- a/ojluni/src/main/java/sun/misc/Cleaner.java
+++ b/ojluni/src/main/java/sun/misc/Cleaner.java
@@ -55,7 +55,6 @@
  *
  * @author Mark Reinhold
  */
-
 public class Cleaner
     extends PhantomReference<Object>
 {
diff --git a/ojluni/src/main/java/sun/misc/SharedSecrets.java b/ojluni/src/main/java/sun/misc/SharedSecrets.java
index 45cd489..226dbf7 100644
--- a/ojluni/src/main/java/sun/misc/SharedSecrets.java
+++ b/ojluni/src/main/java/sun/misc/SharedSecrets.java
@@ -35,7 +35,7 @@
     for this purpose, namely the loss of compile-time checking. */
 
 public class SharedSecrets {
-    // BEGIN Android-removed: Pruned unused access interfaces
+    // BEGIN Android-removed: Pruned unused access interfaces.
     /*
     private static final Unsafe unsafe = Unsafe.getUnsafe();
     private static JavaUtilJarAccess javaUtilJarAccess;
@@ -46,9 +46,9 @@
     private static JavaNetHttpCookieAccess javaNetHttpCookieAccess;
     private static JavaNioAccess javaNioAccess;
     */
-    // END Android-removed: Pruned unused access interfaces
+    // END Android-removed: Pruned unused access interfaces.
     private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
-    // BEGIN Android-removed: Pruned unused access interfaces
+    // BEGIN Android-removed: Pruned unused access interfaces.
     /*
     private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess;
     private static JavaSecurityAccess javaSecurityAccess;
@@ -129,7 +129,7 @@
         return javaIOAccess;
     }
     */
-    // END Android-removed: Pruned unused access interfaces
+    // END Android-removed: Pruned unused access interfaces.
 
     public static void setJavaIOFileDescriptorAccess(JavaIOFileDescriptorAccess jiofda) {
         javaIOFileDescriptorAccess = jiofda;
@@ -150,7 +150,7 @@
         return javaIOFileDescriptorAccess;
     }
 
-    // BEGIN Android-removed: Pruned unused access interfaces
+    // BEGIN Android-removed: Pruned unused access interfaces.
     /*
     public static void setJavaOISAccess(JavaOISAccess access) {
         javaOISAccess = access;
@@ -221,5 +221,5 @@
         javaObjectInputStreamAccess = access;
     }
     */
-    // END Android-removed: Pruned unused access interfaces
+    // END Android-removed: Pruned unused access interfaces.
 }
diff --git a/ojluni/src/main/java/sun/misc/URLClassPath.java b/ojluni/src/main/java/sun/misc/URLClassPath.java
index e9a89bc..0961d7a 100644
--- a/ojluni/src/main/java/sun/misc/URLClassPath.java
+++ b/ojluni/src/main/java/sun/misc/URLClassPath.java
@@ -360,7 +360,7 @@
         lookupCacheEnabled = false;
     }
 
-    // BEGIN Android-changed: No lookup chache support
+    // BEGIN Android-changed: No lookup cache support.
     /*
     private static native URL[] getLookupCacheURLs(ClassLoader loader);
     private static native int[] getLookupCacheForClassLoader(ClassLoader loader,
@@ -380,7 +380,7 @@
                                             String className) {
         return false;
     }
-    // END Android-changed: No lookup chache support
+    // END Android-changed: No lookup cache support.
 
 
     synchronized boolean knownToNotExist(String className) {
@@ -816,7 +816,7 @@
         private final HashMap<String, Loader> lmap;
         private final AccessControlContext acc;
         private boolean closed = false;
-        // Android-changed: Not needed, called directly
+        // Android-changed: Not needed, called directly.
         // private static final sun.misc.JavaUtilZipFileAccess zipAccess =
         //      sun.misc.SharedSecrets.getJavaUtilZipFileAccess();
 
diff --git a/ojluni/src/main/java/sun/misc/Unsafe.java b/ojluni/src/main/java/sun/misc/Unsafe.java
index 2473f65..e538b73 100644
--- a/ojluni/src/main/java/sun/misc/Unsafe.java
+++ b/ojluni/src/main/java/sun/misc/Unsafe.java
@@ -73,7 +73,7 @@
      * Gets the raw byte offset from the start of an object's memory to
      * the memory used to store the indicated instance field.
      *
-     * @param field non-null; the field in question, which must be an
+     * @param field non-{@code null}; the field in question, which must be an
      * instance field
      * @return the offset to the field
      */
@@ -88,7 +88,7 @@
      * Gets the offset from the start of an array object's memory to
      * the memory used to store its initial (zeroeth) element.
      *
-     * @param clazz non-null; class in question; must be an array class
+     * @param clazz non-{@code null}; class in question; must be an array class
      * @return the offset to the initial element
      */
     public int arrayBaseOffset(Class clazz) {
@@ -102,7 +102,7 @@
     /**
      * Gets the size of each element of the given array class.
      *
-     * @param clazz non-null; class in question; must be an array class
+     * @param clazz non-{@code null}; class in question; must be an array class
      * @return &gt; 0; the size of each element of the array
      */
     public int arrayIndexScale(Class clazz) {
@@ -119,114 +119,114 @@
     private static native int getArrayIndexScaleForComponentType(Class component_class);
 
     /**
-     * Performs a compare-and-set operation on an <code>int</code>
+     * Performs a compare-and-set operation on an {@code int}
      * field within the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param expectedValue expected value of the field
      * @param newValue new value to store in the field if the contents are
      * as expected
-     * @return <code>true</code> if the new value was in fact stored, and
-     * <code>false</code> if not
+     * @return {@code true} if the new value was in fact stored, and
+     * {@code false} if not
      */
     @FastNative
     public native boolean compareAndSwapInt(Object obj, long offset,
             int expectedValue, int newValue);
 
     /**
-     * Performs a compare-and-set operation on a <code>long</code>
+     * Performs a compare-and-set operation on a {@code long}
      * field within the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param expectedValue expected value of the field
      * @param newValue new value to store in the field if the contents are
      * as expected
-     * @return <code>true</code> if the new value was in fact stored, and
-     * <code>false</code> if not
+     * @return {@code true} if the new value was in fact stored, and
+     * {@code false} if not
      */
     @FastNative
     public native boolean compareAndSwapLong(Object obj, long offset,
             long expectedValue, long newValue);
 
     /**
-     * Performs a compare-and-set operation on an <code>Object</code>
+     * Performs a compare-and-set operation on an {@code obj}
      * field (that is, a reference field) within the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param expectedValue expected value of the field
      * @param newValue new value to store in the field if the contents are
      * as expected
-     * @return <code>true</code> if the new value was in fact stored, and
-     * <code>false</code> if not
+     * @return {@code true} if the new value was in fact stored, and
+     * {@code false} if not
      */
     @FastNative
     public native boolean compareAndSwapObject(Object obj, long offset,
             Object expectedValue, Object newValue);
 
     /**
-     * Gets an <code>int</code> field from the given object,
-     * using <code>volatile</code> semantics.
+     * Gets an {@code int} field from the given object,
+     * using {@code volatile} semantics.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @return the retrieved value
      */
     @FastNative
     public native int getIntVolatile(Object obj, long offset);
 
     /**
-     * Stores an <code>int</code> field into the given object,
-     * using <code>volatile</code> semantics.
+     * Stores an {@code int} field into the given object,
+     * using {@code volatile} semantics.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param newValue the value to store
      */
     @FastNative
     public native void putIntVolatile(Object obj, long offset, int newValue);
 
     /**
-     * Gets a <code>long</code> field from the given object,
-     * using <code>volatile</code> semantics.
+     * Gets a {@code long} field from the given object,
+     * using {@code volatile} semantics.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @return the retrieved value
      */
     @FastNative
     public native long getLongVolatile(Object obj, long offset);
 
     /**
-     * Stores a <code>long</code> field into the given object,
-     * using <code>volatile</code> semantics.
+     * Stores a {@code long} field into the given object,
+     * using {@code volatile} semantics.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param newValue the value to store
      */
     @FastNative
     public native void putLongVolatile(Object obj, long offset, long newValue);
 
     /**
-     * Gets an <code>Object</code> field from the given object,
-     * using <code>volatile</code> semantics.
+     * Gets an {@code obj} field from the given object,
+     * using {@code volatile} semantics.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @return the retrieved value
      */
     @FastNative
     public native Object getObjectVolatile(Object obj, long offset);
 
     /**
-     * Stores an <code>Object</code> field into the given object,
-     * using <code>volatile</code> semantics.
+     * Stores an {@code obj} field into the given object,
+     * using {@code volatile} semantics.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param newValue the value to store
      */
     @FastNative
@@ -234,20 +234,20 @@
             Object newValue);
 
     /**
-     * Gets an <code>int</code> field from the given object.
+     * Gets an {@code int} field from the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing int field
+     * @param offset offset to the field within {@code obj}
      * @return the retrieved value
      */
     @FastNative
     public native int getInt(Object obj, long offset);
 
     /**
-     * Stores an <code>int</code> field into the given object.
+     * Stores an {@code int} field into the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing int field
+     * @param offset offset to the field within {@code obj}
      * @param newValue the value to store
      */
     @FastNative
@@ -255,25 +255,29 @@
 
     /**
      * Lazy set an int field.
+     *
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
      */
     @FastNative
     public native void putOrderedInt(Object obj, long offset, int newValue);
 
     /**
-     * Gets a <code>long</code> field from the given object.
+     * Gets a {@code long} field from the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @return the retrieved value
      */
     @FastNative
     public native long getLong(Object obj, long offset);
 
     /**
-     * Stores a <code>long</code> field into the given object.
+     * Stores a {@code long} field into the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param newValue the value to store
      */
     @FastNative
@@ -281,25 +285,29 @@
 
     /**
      * Lazy set a long field.
+     *
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
      */
     @FastNative
     public native void putOrderedLong(Object obj, long offset, long newValue);
 
     /**
-     * Gets an <code>Object</code> field from the given object.
+     * Gets an {@code obj} field from the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @return the retrieved value
      */
     @FastNative
     public native Object getObject(Object obj, long offset);
 
     /**
-     * Stores an <code>Object</code> field into the given object.
+     * Stores an {@code obj} field into the given object.
      *
-     * @param obj non-null; object containing the field
-     * @param offset offset to the field within <code>obj</code>
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
      * @param newValue the value to store
      */
     @FastNative
@@ -307,34 +315,132 @@
 
     /**
      * Lazy set an object field.
+     *
+     * @param obj non-{@code null}; object containing the field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
      */
     @FastNative
     public native void putOrderedObject(Object obj, long offset,
             Object newValue);
 
-
+    /**
+     * Gets a {@code boolean} field from the given object.
+     *
+     * @param obj non-{@code null}; object containing boolean field
+     * @param offset offset to the field within {@code obj}
+     * @return the retrieved value
+     */
     @FastNative
     public native boolean getBoolean(Object obj, long offset);
+
+    /**
+     * Stores a {@code boolean} field into the given object.
+     *
+     * @param obj non-{@code null}; object containing boolean field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putBoolean(Object obj, long offset, boolean newValue);
+
+    /**
+     * Gets a {@code byte} field from the given object.
+     *
+     * @param obj non-{@code null}; object containing byte field
+     * @param offset offset to the field within {@code obj}
+     * @return the retrieved value
+     */
     @FastNative
     public native byte getByte(Object obj, long offset);
+
+    /**
+     * Stores a {@code byte} field into the given object.
+     *
+     * @param obj non-{@code null}; object containing byte field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putByte(Object obj, long offset, byte newValue);
+
+    /**
+     * Gets a {@code char} field from the given object.
+     *
+     * @param obj non-{@code null}; object containing char field
+     * @param offset offset to the field within {@code obj}
+     * @return the retrieved value
+     */
     @FastNative
     public native char getChar(Object obj, long offset);
+
+    /**
+     * Stores a {@code char} field into the given object.
+     *
+     * @param obj non-{@code null}; object containing char field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putChar(Object obj, long offset, char newValue);
+
+    /**
+     * Gets a {@code short} field from the given object.
+     *
+     * @param obj non-{@code null}; object containing short field
+     * @param offset offset to the field within {@code obj}
+     * @return the retrieved value
+     */
     @FastNative
     public native short getShort(Object obj, long offset);
+
+    /**
+     * Stores a {@code short} field into the given object.
+     *
+     * @param obj non-{@code null}; object containing short field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putShort(Object obj, long offset, short newValue);
+
+    /**
+     * Gets a {@code float} field from the given object.
+     *
+     * @param obj non-{@code null}; object containing float field
+     * @param offset offset to the field within {@code obj}
+     * @return the retrieved value
+     */
     @FastNative
     public native float getFloat(Object obj, long offset);
+
+    /**
+     * Stores a {@code float} field into the given object.
+     *
+     * @param obj non-{@code null}; object containing float field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putFloat(Object obj, long offset, float newValue);
+
+    /**
+     * Gets a {@code double} field from the given object.
+     *
+     * @param obj non-{@code null}; object containing double field
+     * @param offset offset to the field within {@code obj}
+     * @return the retrieved value
+     */
     @FastNative
     public native double getDouble(Object obj, long offset);
+
+    /**
+     * Stores a {@code double} field into the given object.
+     *
+     * @param obj non-{@code null}; object containing double field
+     * @param offset offset to the field within {@code obj}
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putDouble(Object obj, long offset, double newValue);
 
@@ -349,10 +455,11 @@
      * in-depth information of the behavior of this method.</p>
      *
      * @param absolute whether the given time value is absolute
-     * milliseconds-since-the-epoch (<code>true</code>) or relative
-     * nanoseconds-from-now (<code>false</code>)
+     * milliseconds-since-the-epoch ({@code true}) or relative
+     * nanoseconds-from-now ({@code false})
      * @param time the (absolute millis or relative nanos) time value
      */
+
     public native void park(boolean absolute, long time);
     /**
      * Unparks the given object, which must be a {@link Thread}.
@@ -360,81 +467,219 @@
      * <p>See {@link java.util.concurrent.locks.LockSupport} for more
      * in-depth information of the behavior of this method.</p>
      *
-     * @param obj non-null; the object to unpark
+     * @param obj non-{@code null}; the object to unpark
      */
     @FastNative
     public native void unpark(Object obj);
+
     /**
      * Allocates an instance of the given class without running the constructor.
      * The class' <clinit> will be run, if necessary.
      */
     public native Object allocateInstance(Class<?> c);
 
+    /**
+     * Gets the size of the address value, in bytes.
+     *
+     * @return the size of the address, in bytes
+     */
     @FastNative
     public native int addressSize();
 
+    /**
+     * Gets the size of the memory page, in bytes.
+     *
+     * @return the size of the page
+     */
     @FastNative
     public native int pageSize();
 
+    /**
+     * Allocates a memory block of size {@code bytes}.
+     *
+     * @param bytes size of the memory block
+     * @return address of the allocated memory
+     */
     @FastNative
     public native long allocateMemory(long bytes);
 
+    /**
+     * Frees previously allocated memory at given address.
+     *
+     * @param address address of the freed memory
+     */
     @FastNative
     public native void freeMemory(long address);
 
+    /**
+     * Fills given memory block with a given value.
+     *
+     * @param address address of the memoory block
+     * @param bytes length of the memory block, in bytes
+     * @param value fills memory with this value
+     */
     @FastNative
     public native void setMemory(long address, long bytes, byte value);
 
+    /**
+     * Gets {@code byte} from given address in memory.
+     *
+     * @param address address in memory
+     * @return {@code byte} value
+     */
     @FastNative
     public native byte getByte(long address);
 
+    /**
+     * Stores a {@code byte} into the given memory address.
+     *
+     * @param address address in memory where to store the value
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putByte(long address, byte x);
 
+    /**
+     * Gets {@code short} from given address in memory.
+     *
+     * @param address address in memory
+     * @return {@code short} value
+     */
     @FastNative
     public native short getShort(long address);
 
+    /**
+     * Stores a {@code short} into the given memory address.
+     *
+     * @param address address in memory where to store the value
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putShort(long address, short x);
 
+    /**
+     * Gets {@code char} from given address in memory.
+     *
+     * @param address address in memory
+     * @return {@code char} value
+     */
     @FastNative
     public native char getChar(long address);
 
+    /**
+     * Stores a {@code char} into the given memory address.
+     *
+     * @param address address in memory where to store the value
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putChar(long address, char x);
 
+    /**
+     * Gets {@code int} from given address in memory.
+     *
+     * @param address address in memory
+     * @return {@code int} value
+     */
     @FastNative
     public native int getInt(long address);
 
+    /**
+     * Stores a {@code int} into the given memory address.
+     *
+     * @param address address in memory where to store the value
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putInt(long address, int x);
 
+
+    /**
+     * Gets {@code long} from given address in memory.
+     *
+     * @param address address in memory
+     * @return {@code long} value
+     */
     @FastNative
     public native long getLong(long address);
 
+    /**
+     * Stores a {@code long} into the given memory address.
+     *
+     * @param address address in memory where to store the value
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putLong(long address, long x);
 
+    /**
+     * Gets {@code long} from given address in memory.
+     *
+     * @param address address in memory
+     * @return {@code long} value
+     */
     @FastNative
     public native float getFloat(long address);
 
+    /**
+     * Stores a {@code float} into the given memory address.
+     *
+     * @param address address in memory where to store the value
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putFloat(long address, float x);
 
+    /**
+     * Gets {@code double} from given address in memory.
+     *
+     * @param address address in memory
+     * @return {@code double} value
+     */
     @FastNative
     public native double getDouble(long address);
 
+    /**
+     * Stores a {@code double} into the given memory address.
+     *
+     * @param address address in memory where to store the value
+     * @param newValue the value to store
+     */
     @FastNative
     public native void putDouble(long address, double x);
 
+    /**
+     * Copies given memory block to a primitive array.
+     *
+     * @param srcAddr address to copy memory from
+     * @param dst address to copy memory to
+     * @param dstOffset offset in {@code dst}
+     * @param bytes number of bytes to copy
+     */
     @FastNative
     public native void copyMemoryToPrimitiveArray(long srcAddr,
             Object dst, long dstOffset, long bytes);
 
+    /**
+     * Treat given primitive array as a continuous memory block and
+     * copy it to given memory address.
+     *
+     * @param src primitive array to copy data from
+     * @param srcOffset offset in {@code src} to copy from
+     * @param dstAddr memory address to copy data to
+     * @param bytes number of bytes to copy
+     */
     @FastNative
     public native void copyMemoryFromPrimitiveArray(Object src, long srcOffset,
             long dstAddr, long bytes);
 
+    /**
+     * Sets all bytes in a given block of memory to a copy of another block.
+     *
+     * @param srcAddr address of the source memory to be copied from
+     * @param dstAddr address of the destination memory to copy to
+     * @param bytes number of bytes to copy
+     */
     @FastNative
     public native void copyMemory(long srcAddr, long dstAddr, long bytes);
 
diff --git a/ojluni/src/main/java/sun/misc/VM.java b/ojluni/src/main/java/sun/misc/VM.java
index f9060ba..0c5c4e5 100644
--- a/ojluni/src/main/java/sun/misc/VM.java
+++ b/ojluni/src/main/java/sun/misc/VM.java
@@ -93,7 +93,7 @@
         return STATE_GREEN;
     }
 
-    // Android-removed: Not used
+    // Android-removed: Not used.
     /** @deprecated */
     // @Deprecated
     // public static void registerVMNotification(VMNotification n) { }
@@ -233,7 +233,7 @@
         return allowArraySyntax;
     }
 
-    // BEGIN Android-removed: Not used on android
+    // BEGIN Android-removed: Not used on android.
     /**
      * Returns true if the given class loader is in the system domain
      * in which all permissions are granted.
@@ -241,7 +241,7 @@
     // public static boolean isSystemDomainLoader(ClassLoader loader) {
     //     return loader == null;
     // }
-    // END Android-removed: Not used on android
+    // END Android-removed: Not used on android.
 
     /**
      * Returns the system property of the specified key saved at
@@ -403,7 +403,7 @@
     private final static int JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010;
     private final static int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020;
 
-    // BEGIN Android-removed: latestUserDefinedLoader()/initialize() not supported
+    // BEGIN Android-removed: latestUserDefinedLoader()/initialize() not supported.
     // /*
     //  * Returns the first non-null class loader up the execution stack,
     //  * or null if only code from the null class loader is on the stack.
@@ -414,5 +414,5 @@
     //     initialize();
     // }
     // private native static void initialize();
-    // END Android-removed: latestUserDefinedLoader()/initialize() not supported
+    // END Android-removed: latestUserDefinedLoader()/initialize() not supported.
 }
diff --git a/ojluni/src/main/java/sun/nio/fs/UnixChannelFactory.java b/ojluni/src/main/java/sun/nio/fs/UnixChannelFactory.java
index 9491fd9..c6f074e 100644
--- a/ojluni/src/main/java/sun/nio/fs/UnixChannelFactory.java
+++ b/ojluni/src/main/java/sun/nio/fs/UnixChannelFactory.java
@@ -47,13 +47,6 @@
     private static final JavaIOFileDescriptorAccess fdAccess =
         SharedSecrets.getJavaIOFileDescriptorAccess();
 
-    static {
-        // b/151107960. This class is on the preloaded-classes-blacklist.
-        // It would be instantiated during AOT now without this magic
-        // function call and consequently fail 'atest PreloadCheck'.
-        dalvik.system.VMRuntime.doNotInitializeInAot();
-    }
-
     protected UnixChannelFactory() {
     }
 
diff --git a/ojluni/src/main/java/sun/reflect/misc/ReflectUtil.java b/ojluni/src/main/java/sun/reflect/misc/ReflectUtil.java
index b4fdf70..6af86d5 100644
--- a/ojluni/src/main/java/sun/reflect/misc/ReflectUtil.java
+++ b/ojluni/src/main/java/sun/reflect/misc/ReflectUtil.java
@@ -116,7 +116,7 @@
         return false;
     }
 
-    // Android-removed: Dead code: Unused method conservativeCheckMemberAccess()
+    // Android-removed: Dead code: Unused method conservativeCheckMemberAccess().
 
     /**
      * Checks package access on the given class.
@@ -262,6 +262,6 @@
         return Proxy.isProxyClass(cls) && !pkg.isEmpty();
     }
 
-    // Android-removed: Dead code: unused method checkProxyMethod()
-    // Android-removed: Dead code: unused method isVMAnonymousClass()
+    // Android-removed: Dead code: unused method checkProxyMethod().
+    // Android-removed: Dead code: unused method isVMAnonymousClass().
 }
diff --git a/ojluni/src/main/java/sun/security/jca/Providers.java b/ojluni/src/main/java/sun/security/jca/Providers.java
index a9634f0..dbd40ee 100644
--- a/ojluni/src/main/java/sun/security/jca/Providers.java
+++ b/ojluni/src/main/java/sun/security/jca/Providers.java
@@ -140,8 +140,10 @@
     /**
      * Start JAR verification. This sets a special provider list for
      * the current thread. You MUST save the return value from this
-     * method and you MUST call stopJarVerification() with that object
+     * method and you MUST call {@link #stopJarVerification(Object)} with that object
      * once you are done.
+     *
+     * @return old thread-local provider
      */
     public static Object startJarVerification() {
         ProviderList currentList = getProviderList();
@@ -151,7 +153,11 @@
     }
 
     /**
-     * Stop JAR verification. Call once you have completed JAR verification.
+     * Stop JAR verification. Call once you have completed JAR verification,
+     * passing previously saved return value of {@link #startJarVerification()}.
+     *
+     * @param obj previously saved from {@link #startJarVerification()} old
+     *            thread-local provider list
      */
     public static void stopJarVerification(Object obj) {
         // restore old thread-local provider list
@@ -348,194 +354,11 @@
     }
 
     // The set of algorithms that are deprecated.  This list is created using
-    // libcore/tools/crypto/src/java/libcore/java/security/ProviderOverlap.java, with
-    // additional Ciphers added manually (see comment below).
+    // libcore/tools/crypto/src/java/libcore/java/security/ProviderOverlap.java.
     private static final Set<String> DEPRECATED_ALGORITHMS = new HashSet<String>();
     static {
         DEPRECATED_ALGORITHMS.addAll(Arrays.asList(
-                "ALGORITHMPARAMETERS.1.2.840.113549.3.7",
-                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.2",
-                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.22",
-                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.26",
-                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.42",
-                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.46",
-                "ALGORITHMPARAMETERS.2.16.840.1.101.3.4.1.6",
-                "ALGORITHMPARAMETERS.AES",
-                "ALGORITHMPARAMETERS.DESEDE",
-                "ALGORITHMPARAMETERS.EC",
-                "ALGORITHMPARAMETERS.GCM",
-                "ALGORITHMPARAMETERS.OAEP",
-                "ALGORITHMPARAMETERS.TDEA",
-                "CERTIFICATEFACTORY.X.509",
-                "CERTIFICATEFACTORY.X509",
-                // List of Ciphers produced by ProviderOverlap:
-                "CIPHER.1.2.840.113549.3.4",
-                "CIPHER.2.16.840.1.101.3.4.1.26",
-                "CIPHER.2.16.840.1.101.3.4.1.46",
-                "CIPHER.2.16.840.1.101.3.4.1.6",
-                "CIPHER.AES/GCM/NOPADDING",
-                "CIPHER.ARC4",
-                "CIPHER.ARCFOUR",
-                "CIPHER.OID.1.2.840.113549.3.4",
-                "CIPHER.RC4",
-                // End of Ciphers produced by ProviderOverlap
-                // Additional ciphers transformations that will resolve to the same things as
-                // the automatically-produced overlap due to the Cipher transformation rules.
-                // These have been added manually.
-                "CIPHER.ARC4/ECB/NOPADDING",
-                "CIPHER.ARC4/NONE/NOPADDING",
-                "CIPHER.ARCFOUR/ECB/NOPADDING",
-                "CIPHER.ARCFOUR/NONE/NOPADDING",
-                "CIPHER.RC4/ECB/NOPADDING",
-                "CIPHER.RC4/NONE/NOPADDING",
-                // End of additional Ciphers
-                "KEYAGREEMENT.ECDH",
-                "KEYFACTORY.1.2.840.10045.2.1",
-                "KEYFACTORY.1.2.840.113549.1.1.1",
-                "KEYFACTORY.1.2.840.113549.1.1.7",
-                "KEYFACTORY.1.3.133.16.840.63.0.2",
-                "KEYFACTORY.2.5.8.1.1",
-                "KEYFACTORY.EC",
-                "KEYFACTORY.RSA",
-                "KEYGENERATOR.1.2.840.113549.2.10",
-                "KEYGENERATOR.1.2.840.113549.2.11",
-                "KEYGENERATOR.1.2.840.113549.2.7",
-                "KEYGENERATOR.1.2.840.113549.2.8",
-                "KEYGENERATOR.1.2.840.113549.2.9",
-                "KEYGENERATOR.1.3.6.1.5.5.8.1.1",
-                "KEYGENERATOR.1.3.6.1.5.5.8.1.2",
-                "KEYGENERATOR.2.16.840.1.101.3.4.2.1",
-                "KEYGENERATOR.AES",
-                "KEYGENERATOR.DESEDE",
-                "KEYGENERATOR.HMAC-MD5",
-                "KEYGENERATOR.HMAC-SHA1",
-                "KEYGENERATOR.HMAC-SHA224",
-                "KEYGENERATOR.HMAC-SHA256",
-                "KEYGENERATOR.HMAC-SHA384",
-                "KEYGENERATOR.HMAC-SHA512",
-                "KEYGENERATOR.HMAC/MD5",
-                "KEYGENERATOR.HMAC/SHA1",
-                "KEYGENERATOR.HMAC/SHA224",
-                "KEYGENERATOR.HMAC/SHA256",
-                "KEYGENERATOR.HMAC/SHA384",
-                "KEYGENERATOR.HMAC/SHA512",
-                "KEYGENERATOR.HMACMD5",
-                "KEYGENERATOR.HMACSHA1",
-                "KEYGENERATOR.HMACSHA224",
-                "KEYGENERATOR.HMACSHA256",
-                "KEYGENERATOR.HMACSHA384",
-                "KEYGENERATOR.HMACSHA512",
-                "KEYGENERATOR.TDEA",
-                "KEYPAIRGENERATOR.1.2.840.10045.2.1",
-                "KEYPAIRGENERATOR.1.2.840.113549.1.1.1",
-                "KEYPAIRGENERATOR.1.2.840.113549.1.1.7",
-                "KEYPAIRGENERATOR.1.3.133.16.840.63.0.2",
-                "KEYPAIRGENERATOR.2.5.8.1.1",
-                "KEYPAIRGENERATOR.EC",
-                "KEYPAIRGENERATOR.RSA",
-                "MAC.1.2.840.113549.2.10",
-                "MAC.1.2.840.113549.2.11",
-                "MAC.1.2.840.113549.2.7",
-                "MAC.1.2.840.113549.2.8",
-                "MAC.1.2.840.113549.2.9",
-                "MAC.1.3.6.1.5.5.8.1.1",
-                "MAC.1.3.6.1.5.5.8.1.2",
-                "MAC.2.16.840.1.101.3.4.2.1",
-                "MAC.HMAC-MD5",
-                "MAC.HMAC-SHA1",
-                "MAC.HMAC-SHA224",
-                "MAC.HMAC-SHA256",
-                "MAC.HMAC-SHA384",
-                "MAC.HMAC-SHA512",
-                "MAC.HMAC/MD5",
-                "MAC.HMAC/SHA1",
-                "MAC.HMAC/SHA224",
-                "MAC.HMAC/SHA256",
-                "MAC.HMAC/SHA384",
-                "MAC.HMAC/SHA512",
-                "MAC.HMACMD5",
-                "MAC.HMACSHA1",
-                "MAC.HMACSHA224",
-                "MAC.HMACSHA256",
-                "MAC.HMACSHA384",
-                "MAC.HMACSHA512",
-                "MAC.PBEWITHHMACSHA224",
-                "MAC.PBEWITHHMACSHA256",
-                "MAC.PBEWITHHMACSHA384",
-                "MAC.PBEWITHHMACSHA512",
-                "MESSAGEDIGEST.1.2.840.113549.2.5",
-                "MESSAGEDIGEST.1.3.14.3.2.26",
-                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.1",
-                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.2",
-                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.3",
-                "MESSAGEDIGEST.2.16.840.1.101.3.4.2.4",
-                "MESSAGEDIGEST.MD5",
-                "MESSAGEDIGEST.SHA",
-                "MESSAGEDIGEST.SHA-1",
-                "MESSAGEDIGEST.SHA-224",
-                "MESSAGEDIGEST.SHA-256",
-                "MESSAGEDIGEST.SHA-384",
-                "MESSAGEDIGEST.SHA-512",
-                "MESSAGEDIGEST.SHA1",
-                "MESSAGEDIGEST.SHA224",
-                "MESSAGEDIGEST.SHA256",
-                "MESSAGEDIGEST.SHA384",
-                "MESSAGEDIGEST.SHA512",
-                "SECRETKEYFACTORY.DESEDE",
-                "SECRETKEYFACTORY.TDEA",
-                "SIGNATURE.1.2.840.10045.4.1",
-                "SIGNATURE.1.2.840.10045.4.3.1",
-                "SIGNATURE.1.2.840.10045.4.3.2",
-                "SIGNATURE.1.2.840.10045.4.3.3",
-                "SIGNATURE.1.2.840.10045.4.3.4",
-                "SIGNATURE.1.2.840.113549.1.1.11",
-                "SIGNATURE.1.2.840.113549.1.1.12",
-                "SIGNATURE.1.2.840.113549.1.1.13",
-                "SIGNATURE.1.2.840.113549.1.1.14",
-                "SIGNATURE.1.2.840.113549.1.1.4",
-                "SIGNATURE.1.2.840.113549.1.1.5",
-                "SIGNATURE.1.3.14.3.2.29",
-                "SIGNATURE.ECDSA",
-                "SIGNATURE.ECDSAWITHSHA1",
-                "SIGNATURE.MD5/RSA",
-                "SIGNATURE.MD5WITHRSA",
-                "SIGNATURE.MD5WITHRSAENCRYPTION",
-                "SIGNATURE.NONEWITHECDSA",
-                "SIGNATURE.OID.1.2.840.10045.4.3.1",
-                "SIGNATURE.OID.1.2.840.10045.4.3.2",
-                "SIGNATURE.OID.1.2.840.10045.4.3.3",
-                "SIGNATURE.OID.1.2.840.10045.4.3.4",
-                "SIGNATURE.OID.1.2.840.113549.1.1.11",
-                "SIGNATURE.OID.1.2.840.113549.1.1.12",
-                "SIGNATURE.OID.1.2.840.113549.1.1.13",
-                "SIGNATURE.OID.1.2.840.113549.1.1.14",
-                "SIGNATURE.OID.1.2.840.113549.1.1.4",
-                "SIGNATURE.OID.1.2.840.113549.1.1.5",
-                "SIGNATURE.OID.1.3.14.3.2.29",
-                "SIGNATURE.SHA1/RSA",
-                "SIGNATURE.SHA1WITHECDSA",
-                "SIGNATURE.SHA1WITHRSA",
-                "SIGNATURE.SHA1WITHRSAENCRYPTION",
-                "SIGNATURE.SHA224/ECDSA",
-                "SIGNATURE.SHA224/RSA",
-                "SIGNATURE.SHA224WITHECDSA",
-                "SIGNATURE.SHA224WITHRSA",
-                "SIGNATURE.SHA224WITHRSAENCRYPTION",
-                "SIGNATURE.SHA256/ECDSA",
-                "SIGNATURE.SHA256/RSA",
-                "SIGNATURE.SHA256WITHECDSA",
-                "SIGNATURE.SHA256WITHRSA",
-                "SIGNATURE.SHA256WITHRSAENCRYPTION",
-                "SIGNATURE.SHA384/ECDSA",
-                "SIGNATURE.SHA384/RSA",
-                "SIGNATURE.SHA384WITHECDSA",
-                "SIGNATURE.SHA384WITHRSA",
-                "SIGNATURE.SHA384WITHRSAENCRYPTION",
-                "SIGNATURE.SHA512/ECDSA",
-                "SIGNATURE.SHA512/RSA",
-                "SIGNATURE.SHA512WITHECDSA",
-                "SIGNATURE.SHA512WITHRSA",
-                "SIGNATURE.SHA512WITHRSAENCRYPTION"
+            "KEYFACTORY.RSA"
         ));
     }
 
diff --git a/ojluni/src/main/java/sun/security/pkcs/ContentInfo.java b/ojluni/src/main/java/sun/security/pkcs/ContentInfo.java
index bc78d0f..a5f5bc7 100644
--- a/ojluni/src/main/java/sun/security/pkcs/ContentInfo.java
+++ b/ojluni/src/main/java/sun/security/pkcs/ContentInfo.java
@@ -34,7 +34,6 @@
  *
  * @author Benjamin Renaud
  */
-
 public class ContentInfo {
 
     // pkcs7 pre-defined content types
@@ -199,6 +198,8 @@
     /**
      * Returns a byte array representation of the data held in
      * the content field.
+     * @return             byte array representation of data held in content field
+     * @throws IOException if content bytes are invalid
      */
     public byte[] getContentBytes() throws IOException {
         if (content == null)
diff --git a/ojluni/src/main/java/sun/security/pkcs/PKCS7.java b/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
index d3cd7e0..930d3d4 100644
--- a/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
+++ b/ojluni/src/main/java/sun/security/pkcs/PKCS7.java
@@ -113,9 +113,10 @@
      * encoded bytes from the InputStream.
      *
      * @param in an input stream holding at least one PKCS7 block.
-     * @exception ParsingException on parsing errors.
-     * @exception IOException on other errors.
+     * @throws ParsingException on parsing errors.
+     * @throws IOException on other errors.
      */
+
     public PKCS7(InputStream in) throws ParsingException, IOException {
         DataInputStream dis = new DataInputStream(in);
         byte[] data = new byte[dis.available()];
@@ -142,6 +143,7 @@
      * @param bytes the encoded bytes.
      * @exception ParsingException on parsing errors.
      */
+
     public PKCS7(byte[] bytes) throws ParsingException {
         try {
             DerInputStream derin = new DerInputStream(bytes);
@@ -614,6 +616,7 @@
      * @exception NoSuchAlgorithmException on unrecognized algorithms.
      * @exception SignatureException on signature handling errors.
      */
+
     public SignerInfo verify(SignerInfo info, InputStream dataInputStream)
     throws NoSuchAlgorithmException, SignatureException, IOException {
         return info.verify(this, dataInputStream);
@@ -628,6 +631,7 @@
      * @exception NoSuchAlgorithmException on unrecognized algorithms.
      * @exception SignatureException on signature handling errors.
      */
+
     public SignerInfo[] verify(byte[] bytes)
     throws NoSuchAlgorithmException, SignatureException {
 
@@ -689,6 +693,7 @@
      * @return a clone of the array of X.509 certificates or null if
      *         none are specified for the content type.
      */
+
     public X509Certificate[] getCertificates() {
         if (certificates != null)
             return certificates.clone();
@@ -713,6 +718,7 @@
      * @return the array of Signer Infos or null if none are specified
      *         for the content type.
      */
+
     public SignerInfo[] getSignerInfos() {
         return signerInfos;
     }
diff --git a/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java b/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java
index 88fcf06..ea12395 100644
--- a/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java
+++ b/ojluni/src/main/java/sun/security/pkcs/SignerInfo.java
@@ -251,10 +251,12 @@
         out.write(tmp.toByteArray());
     }
 
-
-
-    /*
-     * Returns the (user) certificate pertaining to this SignerInfo.
+    /**
+     * Returns the (user) certificate pertaining to this {@link SignerInfo}.
+     *
+     * @param block block of encrypted data
+     * @return certificate pertaining to the {@link SignerInfo}
+     * @throws IOException on decoding error
      */
     public X509Certificate getCertificate(PKCS7 block)
         throws IOException
@@ -262,8 +264,12 @@
         return block.getCertificate(certificateSerialNumber, issuerName);
     }
 
-    /*
-     * Returns the certificate chain pertaining to this SignerInfo.
+    /**
+     * Returns the certificate chain pertaining to this {@link #SignerInfo}.
+     *
+     * @param block block of encrypted data
+     * @return certificate chain pertaining to this {@link #SignerInfo}.
+     * @throws IOException on decoding error
      */
     public ArrayList<X509Certificate> getCertificateChain(PKCS7 block)
         throws IOException
@@ -427,10 +433,13 @@
             }
 
             X509Certificate cert = getCertificate(block);
-            PublicKey key = cert.getPublicKey();
+            // Android-changed: Null pointer fix from later upstream revision
+            // PublicKey key = cert.getPublicKey();
             if (cert == null) {
                 return null;
             }
+            // Android-changed: Null pointer fix from later upstream revision
+            PublicKey key = cert.getPublicKey();
 
             // check if the public key is restricted
             if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
diff --git a/ojluni/src/main/java/sun/security/util/DerInputStream.java b/ojluni/src/main/java/sun/security/util/DerInputStream.java
index 6608676..7f42bb0 100644
--- a/ojluni/src/main/java/sun/security/util/DerInputStream.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputStream.java
@@ -325,7 +325,7 @@
      *          (used to initialize an auto-growing data structure)
      * @return array of the values in the sequence
      */
-    // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
+    // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation.
     public DerValue[] getSequence(int startLen,
             boolean originalEncodedFormRetained) throws IOException {
         tag = (byte)buffer.read();
@@ -348,8 +348,8 @@
         return getSequence(
                 startLen,
                 false); // no need to retain original encoded form
-        // END Android-changed: Original encoded form needed for APKs parsing/validation
     }
+    // END Android-changed: Original encoded form needed for APKs parsing/validation.
 
     /**
      * Return a set of encoded entities.  ASN.1 sets are unordered,
@@ -381,7 +381,7 @@
      */
     public DerValue[] getSet(int startLen, boolean implicit)
         throws IOException {
-        // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
+        // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation.
         return getSet(
             startLen,
             implicit,
@@ -398,7 +398,7 @@
             }
         }
         return (readVector(startLen, originalEncodedFormRetained));
-        // END Android-changed: Original encoded form needed for APKs parsing/validation
+        // END Android-changed: Original encoded form needed for APKs parsing/validation.
     }
 
     /*
@@ -407,7 +407,7 @@
      * this same helper routine.
      */
     protected DerValue[] readVector(int startLen) throws IOException {
-        // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
+        // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation.
         return readVector(
             startLen,
             false); // no need to retain original encoded form
@@ -420,7 +420,7 @@
      */
     protected DerValue[] readVector(int startLen,
             boolean originalEncodedFormRetained) throws IOException {
-        // END Android-changed: Original encoded form needed for APKs parsing/validation
+        // END Android-changed: Original encoded form needed for APKs parsing/validation.
         DerInputStream  newstr;
 
         byte lenByte = (byte)buffer.read();
@@ -465,7 +465,7 @@
         DerValue value;
 
         do {
-            // Android-changed: Original encoded form needed for APKs parsing/validation
+            // Android-changed: Original encoded form needed for APKs parsing/validation.
             value = new DerValue(newstr.buffer, originalEncodedFormRetained);
             vec.addElement(value);
         } while (newstr.available() > 0);
diff --git a/ojluni/src/main/java/sun/security/util/DerValue.java b/ojluni/src/main/java/sun/security/util/DerValue.java
index 3045995..7828820 100644
--- a/ojluni/src/main/java/sun/security/util/DerValue.java
+++ b/ojluni/src/main/java/sun/security/util/DerValue.java
@@ -72,13 +72,13 @@
 
     private int                 length;
 
-    // BEGIN Android-added: Original encoded form needed for APKs parsing/validation
+    // BEGIN Android-added: Original encoded form needed for APKs parsing/validation.
     /**
      * The original encoded form of the whole value (tag, length, and value)
      * or null if the form was not provided or was not retained during parsing.
      */
     private byte[]              originalEncodedForm;
-    // END Android-added: Original encoded form needed for APKs parsing/validation
+    // END Android-added: Original encoded form needed for APKs parsing/validation.
 
     /*
      * The type starts at the first byte of the encoding, and
@@ -251,7 +251,7 @@
     /*
      * package private
      */
-    // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation
+    // BEGIN Android-changed: Original encoded form needed for APKs parsing/validation.
     DerValue(DerInputBuffer in, boolean originalEncodedFormRetained)
             throws IOException {
         // XXX must also parse BER-encoded constructed
@@ -297,7 +297,7 @@
             int consumed = in.getPos() - startPosInInput;
             originalEncodedForm = in.getSlice(startPosInInput, consumed);
         }
-    // END Android-changed: Original encoded form needed for APKs parsing/validation
+    // END Android-changed: Original encoded form needed for APKs parsing/validation.
     }
 
     /**
@@ -838,7 +838,7 @@
         }
     }
 
-    // BEGIN Android-added: Original encoded form needed for APKs parsing/validation
+    // BEGIN Android-added: Original encoded form needed for APKs parsing/validation.
     /**
      * Returns the original encoded form or {@code null} if the form was not
      * retained or is not available.
@@ -847,7 +847,7 @@
         return (originalEncodedForm != null)
                 ? originalEncodedForm.clone() : null;
     }
-    // END Android-added: Original encoded form needed for APKs parsing/validation
+    // END Android-added: Original encoded form needed for APKs parsing/validation.
 
     /**
      * Returns a DER-encoded value, such that if it's passed to the
diff --git a/ojluni/src/main/java/sun/security/util/ObjectIdentifier.java b/ojluni/src/main/java/sun/security/util/ObjectIdentifier.java
index 58f26c3..74c6539 100644
--- a/ojluni/src/main/java/sun/security/util/ObjectIdentifier.java
+++ b/ojluni/src/main/java/sun/security/util/ObjectIdentifier.java
@@ -49,7 +49,6 @@
  * @author Amit Kapoor
  * @author Hemma Prafullchandra
  */
-
 final public
 class ObjectIdentifier implements Serializable
 {
diff --git a/ojluni/src/main/java/sun/security/x509/AlgorithmId.java b/ojluni/src/main/java/sun/security/x509/AlgorithmId.java
index 260d4aa..71501d9 100644
--- a/ojluni/src/main/java/sun/security/x509/AlgorithmId.java
+++ b/ojluni/src/main/java/sun/security/x509/AlgorithmId.java
@@ -56,6 +56,7 @@
  * @author Amit Kapoor
  * @author Hemma Prafullchandra
  */
+@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
 public class AlgorithmId implements Serializable, DerEncoder {
 
     /** use serialVersionUID from JDK 1.1. for interoperability */
@@ -135,6 +136,9 @@
 
     /**
      * Marshal a DER-encoded "AlgorithmID" sequence on the DER stream.
+     *
+     * @param out {@link DerInputStream} to write encoded data to
+     * @throws IOException on encoding error
      */
     public final void encode(DerOutputStream out) throws IOException {
         derEncode(out);
@@ -225,7 +229,10 @@
      * return a name such as "MD5withRSA" for a signature algorithm on
      * some systems.  It also returns names like "OID.1.2.3.4", when
      * no particular name for the algorithm is known.
+     *
+     * @return name of the algorithm
      */
+    @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
     public String getName() {
         String algName = nameTable.get(algid);
         if (algName != null) {
diff --git a/ojluni/src/main/java/sun/security/x509/X509CertImpl.java b/ojluni/src/main/java/sun/security/x509/X509CertImpl.java
index 57a8f71..f15b998 100644
--- a/ojluni/src/main/java/sun/security/x509/X509CertImpl.java
+++ b/ojluni/src/main/java/sun/security/x509/X509CertImpl.java
@@ -192,7 +192,7 @@
         }
     }
 
-    // BEGIN Android-removed: unused code
+    // BEGIN Android-removed: unused code.
     /*
     /**
      * unmarshals an X.509 certificate from an input stream.  If the
@@ -280,7 +280,7 @@
         return der;
     }
     */
-    // END Android-removed: unused code
+    // END Android-removed: unused code.
 
     /**
      * Construct an initialized X509 Certificate. The certificate is stored
@@ -310,7 +310,7 @@
         }
     }
 
-    // BEGIN Android-added: Ctor to retain original encoded form for APKs parsing
+    // BEGIN Android-added: Ctor to retain original encoded form for APKs parsing.
     /**
      * Unmarshal a certificate from its encoded form, parsing a DER value.
      * This form of constructor is used by agents which need to examine
@@ -328,7 +328,7 @@
             throw new CertificateException("Unable to initialize, " + e, e);
         }
     }
-    // END Android-added: Ctor to retain original encoded form for APKs parsing
+    // END Android-added: Ctor to retain original encoded form for APKs parsing.
 
     /**
      * Appends the certificate to an output stream.
@@ -1814,7 +1814,7 @@
             throw new CertificateParsingException(
                       "invalid DER-encoded certificate data");
 
-        // Android-changed: Needed for providing encoded form of cert
+        // Android-changed: Needed for providing encoded form of cert.
         // signedCert = val.toByteArray();
         signedCert =
                 (originalEncodedForm != null)
@@ -1972,12 +1972,12 @@
     private ConcurrentHashMap<String,String> fingerprints =
             new ConcurrentHashMap<>(2);
 
-// BEGIN Android-removed
+// BEGIN Android-removed: unused code.
 //    public String getFingerprint(String algorithm) {
 //        return fingerprints.computeIfAbsent(algorithm,
 //                x -> getFingerprint(x, this));
 //    }
-// END Android-removed
+// END Android-removed: unused code.
 
     /**
      * Gets the requested finger print of the certificate. The result
diff --git a/ojluni/src/main/java/sun/util/locale/LocaleMatcher.java b/ojluni/src/main/java/sun/util/locale/LocaleMatcher.java
index 70fabc7..37eb416 100644
--- a/ojluni/src/main/java/sun/util/locale/LocaleMatcher.java
+++ b/ojluni/src/main/java/sun/util/locale/LocaleMatcher.java
@@ -212,7 +212,7 @@
             if (range.equals("*")) {
                 continue;
             }
-            // Android-changed: backport OpenJDK 9 fix for JDK-8166994
+            // Android-changed: backport OpenJDK 9 fix for JDK-8166994.
             String rangeForRegex = range.replace("*", "\\p{Alnum}*");
             while (rangeForRegex.length() > 0) {
                 for (String tag : tags) {
@@ -242,7 +242,7 @@
     }
 
     public static List<LanguageRange> parse(String ranges) {
-        // Android-changed: backport OpenJDK 9 fix for JDK-8166994
+        // Android-changed: backport OpenJDK 9 fix for JDK-8166994.
         ranges = ranges.replace(" ", "").toLowerCase();
         if (ranges.startsWith("accept-language:")) {
             ranges = ranges.substring(16); // delete unnecessary prefix
@@ -332,7 +332,7 @@
         return list;
     }
 
-    // BEGIN Android-added: backport OpenJDK 9 fix for JDK-8166994
+    // BEGIN Android-added: backport OpenJDK 9 fix for JDK-8166994.
     /**
      * A faster alternative approach to String.replaceFirst(), if the given
      * string is a literal String, not a regex.
@@ -347,7 +347,7 @@
                     + range.substring(pos + substr.length());
         }
     }
-    // END Android-added: backport OpenJDK 9 fix for JDK-8166994
+    // END Android-added: backport OpenJDK 9 fix for JDK-8166994.
 
     private static String[] getEquivalentsForLanguage(String range) {
         String r = range;
@@ -357,7 +357,7 @@
                 String equiv = LocaleEquivalentMaps.singleEquivMap.get(r);
                 // Return immediately for performance if the first matching
                 // subtag is found.
-// BEGIN Android-added: backport OpenJDK 9 fix for JDK-8166994
+// BEGIN Android-added: backport OpenJDK 9 fix for JDK-8166994.
 // Upstream bug: https://bugs.openjdk.java.net/browse/JDK-8166994
 // Upstream fix: http://hg.openjdk.java.net/jdk9/dev/jdk/rev/60837db5d445
                 return new String[]{replaceFirstSubStringMatch(range,
@@ -370,7 +370,7 @@
                             r, equivs[i]);
                 }
                 return result;
-// END Android-added: backport OpenJDK 9 fix for JDK-8166994
+// END Android-added: backport OpenJDK 9 fix for JDK-8166994.
             }
 
             // Truncate the last subtag simply.
diff --git a/ojluni/src/main/java/sun/util/locale/provider/CalendarDataUtility.java b/ojluni/src/main/java/sun/util/locale/provider/CalendarDataUtility.java
index b067413..eed420b 100644
--- a/ojluni/src/main/java/sun/util/locale/provider/CalendarDataUtility.java
+++ b/ojluni/src/main/java/sun/util/locale/provider/CalendarDataUtility.java
@@ -30,6 +30,8 @@
 
 import static java.util.Calendar.*;
 
+import libcore.util.NonNull;
+
 import java.util.Calendar;
 import java.util.LinkedHashMap;
 import java.util.Locale;
@@ -206,7 +208,9 @@
     private static String normalizeCalendarType(String requestID) {
         String type;
         // Android-changed: normalize "gregory" to "gregorian", not the other way around.
-        // See android.icu.text.DateFormatSymbols.CALENDAR_CLASSES for reference.
+        // Android maps BCP-47 calendar types to LDML defined calendar types, because it uses
+        // ICU directly while the upstream does the opposite because the upstream uses different
+        // data sources. See android.icu.text.DateFormatSymbols.CALENDAR_CLASSES for reference.
         // if (requestID.equals("gregorian") || requestID.equals("iso8601")) {
         //    type = "gregory";
         // } else if (requestID.startsWith("islamic")) {
@@ -285,9 +289,11 @@
         }
     }
 
-    private static DateFormatSymbols getDateFormatSymbols(String id, Locale locale) {
+    private static DateFormatSymbols getDateFormatSymbols(@NonNull String id, Locale locale) {
         String calendarType = normalizeCalendarType(id);
-        return new DateFormatSymbols(ULocale.forLocale(locale), calendarType);
+        ULocale uLocale = ULocale.forLocale(locale)
+                .setKeywordValue("calendar", calendarType);
+        return new DateFormatSymbols(uLocale);
     }
 
     /**
diff --git a/ojluni/src/main/native/Android.bp b/ojluni/src/main/native/Android.bp
index 45a5c98..162a73c 100644
--- a/ojluni/src/main/native/Android.bp
+++ b/ojluni/src/main/native/Android.bp
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_ojluni_license":
+    //   SPDX-license-identifier-GPL-2.0
+    default_applicable_licenses: ["libcore_ojluni_license"],
+}
+
 filegroup {
     name: "libopenjdk_native_srcs",
     visibility: [
diff --git a/ojluni/src/main/native/Character.cpp b/ojluni/src/main/native/Character.cpp
index 9fb24c3..3e27c67 100644
--- a/ojluni/src/main/native/Character.cpp
+++ b/ojluni/src/main/native/Character.cpp
@@ -24,7 +24,6 @@
 #include <nativehelper/JNIHelp.h>
 #include "nativehelper/jni_macros.h"
 #include "unicode/uchar.h"
-#include "unicode/uscript.h"
 #include <math.h>
 #include <stdio.h> // For BUFSIZ
 
diff --git a/ojluni/src/main/native/FileDescriptor_md.c b/ojluni/src/main/native/FileDescriptor_md.c
index 0404263..880f5ea 100644
--- a/ojluni/src/main/native/FileDescriptor_md.c
+++ b/ojluni/src/main/native/FileDescriptor_md.c
@@ -63,9 +63,11 @@
 }
 
 JNIEXPORT jboolean JNICALL FileDescriptor_isSocket(JNIEnv *env, jclass ignored, jint fd) {
-    int error;
-    socklen_t error_length = sizeof(error);
-    return TEMP_FAILURE_RETRY(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &error_length)) == 0;
+    // BEGIN Android-changed: isSocket - do not clear socket error code
+    int domain;
+    socklen_t domain_length = sizeof(domain);
+    return TEMP_FAILURE_RETRY(getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &domain_length)) == 0;
+    // END Android-changed: isSocket - do not clear socket error code
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/ojluni/src/main/native/UNIXProcess_md.c b/ojluni/src/main/native/UNIXProcess_md.c
index 40fdc18..a3790e8 100644
--- a/ojluni/src/main/native/UNIXProcess_md.c
+++ b/ojluni/src/main/native/UNIXProcess_md.c
@@ -119,8 +119,7 @@
 #ifndef START_CHILD_USE_VFORK
 // Android-changed: disable vfork under AddressSanitizer.
 //  #ifdef __linux__
-  #if defined(__linux__) && !__has_feature(address_sanitizer) && \
-      !__has_feature(hwaddress_sanitizer)
+  #if defined(__linux__) && !__has_feature(address_sanitizer)
     #define START_CHILD_USE_VFORK 1
   #else
     #define START_CHILD_USE_VFORK 0
diff --git a/ojluni/src/main/native/java_net_PlainDatagramSocketImpl.h b/ojluni/src/main/native/java_net_PlainDatagramSocketImpl.h
deleted file mode 100644
index fb1b78d..0000000
--- a/ojluni/src/main/native/java_net_PlainDatagramSocketImpl.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/* This file was generated from java/net/PlainDatagramSocketImpl.java and is
- * licensed under the same terms. The copyright and license information for
- * java/net/PlainDatagramSocketImpl.java follows.
- *
- * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class java_net_PlainDatagramSocketImpl */
-
-#ifndef _Included_java_net_PlainDatagramSocketImpl
-#define _Included_java_net_PlainDatagramSocketImpl
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    bind0
- * Signature: (ILjava/net/InetAddress;)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_bind0
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    send
- * Signature: (Ljava/net/DatagramPacket;)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_send
-  (JNIEnv *, jobject, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    peek
- * Signature: (Ljava/net/InetAddress;)I
- */
-JNIEXPORT jint JNICALL PlainDatagramSocketImpl_peek
-  (JNIEnv *, jobject, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    peekData
- * Signature: (Ljava/net/DatagramPacket;)I
- */
-JNIEXPORT jint JNICALL PlainDatagramSocketImpl_peekData
-  (JNIEnv *, jobject, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    receive0
- * Signature: (Ljava/net/DatagramPacket;)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_receive0
-  (JNIEnv *, jobject, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    setTimeToLive
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_setTimeToLive
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    getTimeToLive
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL PlainDatagramSocketImpl_getTimeToLive
-  (JNIEnv *, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    setTTL
- * Signature: (B)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_setTTL
-  (JNIEnv *, jobject, jbyte);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    getTTL
- * Signature: ()B
- */
-JNIEXPORT jbyte JNICALL PlainDatagramSocketImpl_getTTL
-  (JNIEnv *, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    join
- * Signature: (Ljava/net/InetAddress;Ljava/net/NetworkInterface;)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_join
-  (JNIEnv *, jobject, jobject, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    leave
- * Signature: (Ljava/net/InetAddress;Ljava/net/NetworkInterface;)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_leave
-  (JNIEnv *, jobject, jobject, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    datagramSocketCreate
- * Signature: ()V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_datagramSocketCreate
-  (JNIEnv *, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    datagramSocketClose
- * Signature: ()V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_datagramSocketClose
-  (JNIEnv *, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    socketSetOption
- * Signature: (ILjava/lang/Object;)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_socketSetOption
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    socketGetOption
- * Signature: (I)Ljava/lang/Object;
- */
-JNIEXPORT jobject JNICALL PlainDatagramSocketImpl_socketGetOption
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    connect0
- * Signature: (Ljava/net/InetAddress;I)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_connect0
-  (JNIEnv *, jobject, jobject, jint);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    disconnect0
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_disconnect0
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     java_net_PlainDatagramSocketImpl
- * Method:    init
- * Signature: ()V
- */
-JNIEXPORT void JNICALL PlainDatagramSocketImpl_init
-  (JNIEnv *, jclass);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/ojluni/src/main/native/java_net_PlainSocketImpl.h b/ojluni/src/main/native/java_net_PlainSocketImpl.h
deleted file mode 100644
index b094a41..0000000
--- a/ojluni/src/main/native/java_net_PlainSocketImpl.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/* This file was generated from java/net/PlainSocketImpl.java and is
- * licensed under the same terms. The copyright and license information for
- * java/net/PlainSocketImpl.java follows.
- *
- * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class java_net_PlainSocketImpl */
-
-#ifndef _Included_java_net_PlainSocketImpl
-#define _Included_java_net_PlainSocketImpl
-#ifdef __cplusplus
-extern "C" {
-#endif
-#undef java_net_PlainSocketImpl_SHUT_RD
-#define java_net_PlainSocketImpl_SHUT_RD 0L
-#undef java_net_PlainSocketImpl_SHUT_WR
-#define java_net_PlainSocketImpl_SHUT_WR 1L
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketCreate
- * Signature: (Z)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketCreate
-  (JNIEnv *, jobject, jboolean);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketConnect
- * Signature: (Ljava/net/InetAddress;II)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketConnect
-  (JNIEnv *, jobject, jobject, jint, jint);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketBind
- * Signature: (Ljava/net/InetAddress;I)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketBind
-  (JNIEnv *, jobject, jobject, jint);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketListen
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketListen
-  (JNIEnv *, jobject, jint);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketAccept
- * Signature: (Ljava/net/SocketImpl;)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketAccept
-  (JNIEnv *, jobject, jobject);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketAvailable
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL PlainSocketImpl_socketAvailable
-  (JNIEnv *, jobject);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketClose0
- * Signature: (Z)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketClose0
-  (JNIEnv *, jobject, jboolean);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketShutdown
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketShutdown
-  (JNIEnv *, jobject, jint);
-
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketSetOption
- * Signature: (IZLjava/lang/Object;)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketSetOption
-  (JNIEnv *, jobject, jint, jboolean, jobject);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketGetOption
- * Signature: (ILjava/lang/Object;)I
- */
-JNIEXPORT jint JNICALL PlainSocketImpl_socketGetOption
-  (JNIEnv *, jobject, jint, jobject);
-
-/*
- * Class:     java_net_PlainSocketImpl
- * Method:    socketSendUrgentData
- * Signature: (I)V
- */
-JNIEXPORT void JNICALL PlainSocketImpl_socketSendUrgentData
-  (JNIEnv *, jobject, jint);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/ojluni/src/main/native/socket_tagger_util.cpp b/ojluni/src/main/native/socket_tagger_util.cpp
index cdac52b..0d833f5 100644
--- a/ojluni/src/main/native/socket_tagger_util.cpp
+++ b/ojluni/src/main/native/socket_tagger_util.cpp
@@ -19,7 +19,8 @@
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#include <nativehelper/JNIHelp.h>
+#include <nativehelper/JNIPlatformHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
 
 #include "JniConstants.h"
 
@@ -28,6 +29,11 @@
       return fd;
     }
 
+    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
+    if (fileDescriptor == nullptr) {
+      return fd;
+    }
+
     jclass socketTaggerClass = JniConstants::GetSocketTaggerClass(env);
     jmethodID get = env->GetStaticMethodID(socketTaggerClass,
                                            "get",
@@ -35,7 +41,6 @@
     jobject socketTagger = env->CallStaticObjectMethod(socketTaggerClass, get);
     jmethodID tag = env->GetMethodID(socketTaggerClass, "tag", "(Ljava/io/FileDescriptor;)V");
 
-    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
     env->CallVoidMethod(socketTagger, tag, fileDescriptor);
     return fd;
 }
diff --git a/ojluni/src/test/java/awt/font/NumericShaper/EasternArabicTest.java b/ojluni/src/test/java/awt/font/NumericShaper/EasternArabicTest.java
new file mode 100644
index 0000000..2b9c1e4
--- /dev/null
+++ b/ojluni/src/test/java/awt/font/NumericShaper/EasternArabicTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6941948
+ * @summary Make sure that EASTERN_ARABIC works with the enum interface.
+ */
+
+package test.java.awt.font.NumericShaper;
+
+import java.awt.font.NumericShaper;
+import java.util.EnumSet;
+import static java.awt.font.NumericShaper.*;
+
+import org.testng.annotations.Test;
+
+public class EasternArabicTest {
+    static NumericShaper ns_old, ns_new;
+    static boolean err = false;
+
+    static String[][] testData = {
+        // Arabic "October 10"
+        {"\u0623\u0643\u062a\u0648\u0628\u0631 10",
+         "\u0623\u0643\u062a\u0648\u0628\u0631 \u06f1\u06f0"}, // EASTERN_ARABIC digits
+
+        // Tamil "Year 2009"
+        {"\u0b86\u0ba3\u0bcd\u0b9f\u0bc1 2009",
+         "\u0b86\u0ba3\u0bcd\u0b9f\u0bc1 \u0be8\u0be6\u0be6\u0bef"},
+        // "\u0be800\u0bef is returned by pre-JDK7 because Tamil zero was not
+        //  included in Unicode 4.0.0.
+
+        // Ethiopic "Syllable<HA> 2009"
+        {"\u1200 2009",
+         "\u1200 \u136a00\u1371"},
+        // Ethiopic zero doesn't exist even in Unicode 5.1.0.
+    };
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        ns_old = getContextualShaper(TAMIL|ETHIOPIC|EASTERN_ARABIC|ARABIC|THAI|LAO,
+                                     EUROPEAN);
+        ns_new = getContextualShaper(EnumSet.of(Range.THAI,
+                                                Range.TAMIL,
+                                                Range.ETHIOPIC,
+                                                Range.EASTERN_ARABIC,
+                                                Range.ARABIC,
+                                                Range.LAO),
+                                     Range.EUROPEAN);
+
+
+        StringBuilder cData = new StringBuilder();
+        StringBuilder cExpected = new StringBuilder();
+        for (int i = 0; i < testData.length; i++) {
+            String data = testData[i][0];
+            String expected = testData[i][1];
+            test(data, expected);
+            cData.append(data).append(' ');
+            cExpected.append(expected).append(' ');
+        }
+        test(cData.toString(), cExpected.toString());
+
+        if (err) {
+            throw new RuntimeException("shape() returned unexpected value.");
+        }
+    }
+
+    private static void test(String data, String expected) {
+        char[] text = data.toCharArray();
+        ns_old.shape(text, 0, text.length);
+        String got = new String(text);
+
+        if (!expected.equals(got)) {
+            err = true;
+            System.err.println("Error with traditional range.");
+            System.err.println("  text = " + data);
+            System.err.println("  got = " + got);
+            System.err.println("  expected = " + expected);
+        } else {
+            System.err.println("OK with traditional range.");
+            System.err.println("  text = " + data);
+            System.err.println("  got = " + got);
+            System.err.println("  expected = " + expected);
+        }
+
+        text = data.toCharArray();
+        ns_new.shape(text, 0, text.length);
+        got = new String(text);
+
+        if (!expected.equals(got)) {
+            err = true;
+            System.err.println("Error with new Enum range.");
+            System.err.println("  text = " + data);
+            System.err.println("  got = " + got);
+            System.err.println("  expected = " + expected);
+        } else {
+            System.err.println("OK with new Enum range.");
+            System.err.println("  text = " + data);
+            System.err.println("  got = " + got);
+            System.err.println("  expected = " + expected);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/awt/font/NumericShaper/EqualsTest.java b/ojluni/src/test/java/awt/font/NumericShaper/EqualsTest.java
new file mode 100644
index 0000000..ed1307e
--- /dev/null
+++ b/ojluni/src/test/java/awt/font/NumericShaper/EqualsTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6842557
+ * @summary confirm that an instance which is created with new Enum ranges is
+ * equivalent to another instance which is created with equivalent traditional
+ * ranges or the same Enum ranges.
+ */
+
+package test.java.awt.font.NumericShaper;
+
+import java.awt.font.NumericShaper;
+import java.util.EnumSet;
+import static java.awt.font.NumericShaper.*;
+
+import org.testng.annotations.Test;
+
+public class EqualsTest {
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        NumericShaper ns1 = getContextualShaper(ARABIC | TAMIL, TAMIL);
+        NumericShaper ns2 = getContextualShaper(
+                                EnumSet.of(Range.ARABIC, Range.TAMIL),
+                                Range.TAMIL);
+        NumericShaper ns3 = getContextualShaper(
+                                EnumSet.of(Range.ARABIC, Range.TAMIL),
+                                Range.TAMIL);
+        NumericShaper ns4 = getContextualShaper(
+                                EnumSet.of(Range.ARABIC, Range.TAMIL),
+                                Range.ARABIC);
+
+        if (!ns1.equals(ns2)) {
+            throw new RuntimeException("ns1 != ns2: ns1=" + ns1 + ", ns2=" + ns2);
+        }
+        if (!ns2.equals(ns1)) {
+            throw new RuntimeException("ns2 != ns1: ns1=" + ns1 + ", ns2=" + ns2);
+        }
+        if (!ns2.equals(ns3)) {
+            throw new RuntimeException("ns2 != ns3: ns2=" + ns2 + ", ns3=" + ns3);
+        }
+        if (ns1.equals(ns4)) {
+            throw new RuntimeException("ns1 == ns4: ns1=" + ns1 + ", ns4=" + ns4);
+        }
+        if (ns2.equals(ns4)) {
+            throw new RuntimeException("ns2 == ns4: ns2=" + ns2 + ", ns4=" + ns4);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/awt/font/NumericShaper/MTTest.java b/ojluni/src/test/java/awt/font/NumericShaper/MTTest.java
new file mode 100644
index 0000000..fb91728
--- /dev/null
+++ b/ojluni/src/test/java/awt/font/NumericShaper/MTTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6843181 6943963
+ * @summary Confirm that NumericShaper is thread-safe.
+ * @run main/timeout=300/othervm MTTest
+ */
+
+package test.java.awt.font.NumericShaper;
+
+import java.awt.font.NumericShaper;
+import java.util.Arrays;
+import java.util.EnumSet;
+import static java.awt.font.NumericShaper.*;
+
+import org.testng.annotations.Test;
+
+public class MTTest {
+    static volatile boolean runrun = true;
+    static volatile boolean err = false;
+
+    final static String text = "-123 (English) 456.00 (Arabic) \u0641\u0642\u0643 -456 (Thai) \u0e01\u0e33 01.23";
+    final static char[] expected1 = "-123 (English) 456.00 (Arabic) \u0641\u0642\u0643 -\u06f4\u06f5\u06f6 (Thai) \u0e01\u0e33 \u0e50\u0e51.\u0e52\u0e53".toCharArray(); // for EASTERN_ARABIC
+    final static char[] expected2 = "-123 (English) 456.00 (Arabic) \u0641\u0642\u0643 -\u0664\u0665\u0666 (Thai) \u0e01\u0e33 \u0e50\u0e51.\u0e52\u0e53".toCharArray(); // for ARABIC
+
+    static NumericShaper ns1, ns2, ns3, ns4;
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        System.out.println("original: " + text);
+        ns1 = getContextualShaper(EnumSet.of(Range.EASTERN_ARABIC, Range.THAI),
+                                  Range.EUROPEAN);
+        ns2 = getContextualShaper(EnumSet.of(Range.ARABIC, Range.THAI),
+                                  Range.EUROPEAN);
+        System.out.println("expected for Eastern-Arabic & Thai: " +
+                           String.valueOf(expected1));
+        System.out.println("expected for Arabic & Thai: " +
+                           String.valueOf(expected2));
+
+        ns3 = getContextualShaper(EASTERN_ARABIC|THAI, EUROPEAN);
+        ns4 = getContextualShaper(ARABIC|THAI, EUROPEAN);
+
+        Thread th1 = new Thread(new Work(ns1, expected1));
+        Thread th2 = new Thread(new Work(ns2, expected2));
+        Thread th3 = new Thread(new Work(ns1, expected1));
+        Thread th4 = new Thread(new Work(ns2, expected2));
+        Thread th5 = new Thread(new Work(ns3, expected1));
+        Thread th6 = new Thread(new Work(ns4, expected2));
+        Thread th7 = new Thread(new Work(ns3, expected1));
+        Thread th8 = new Thread(new Work(ns4, expected2));
+
+        th1.start();
+        th2.start();
+        th3.start();
+        th4.start();
+        th5.start();
+        th6.start();
+        th7.start();
+        th8.start();
+
+        try {
+            // Android-changed: Shorten the test from 180s to 15s. http://b/183290003
+            // for (int i = 0; runrun && i < 180; i++) {
+            for (int i = 0; runrun && i < 15; i++) {
+                Thread.sleep(1000); // 1 seconds
+            }
+            runrun = false;
+            th1.join();
+            th2.join();
+            th3.join();
+            th4.join();
+            th5.join();
+            th6.join();
+            th7.join();
+            th8.join();
+        }
+        catch (InterruptedException e) {
+        }
+
+        if (err) {
+            throw new RuntimeException("Thread-safe test failed.");
+        }
+    }
+
+    private static class Work implements Runnable {
+        NumericShaper ns;
+        char[] expectedText;
+
+        Work(NumericShaper ns, char[] expectedText) {
+            this.ns = ns;
+            this.expectedText = expectedText;
+
+        }
+
+        public void run() {
+            int count = 0;
+            while (runrun) {
+                char[] t = text.toCharArray();
+                count++;
+                try {
+                    ns.shape(t, 0, t.length);
+                } catch (Exception e) {
+                    System.err.println("Error: Unexpected exception: " + e);
+                    runrun = false;
+                    err = true;
+                    return;
+                }
+                if (!Arrays.equals(t, expectedText)) {
+                    System.err.println("Error: shape() returned unexpected value: ");
+                    System.err.println("count = " + count);
+                    System.err.println("   expected: " + String.valueOf(expectedText));
+                    System.err.println("        got: " + String.valueOf(t));
+                    runrun = false;
+                    err = true;
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/ojluni/src/test/java/awt/font/NumericShaper/ShapingTest.java b/ojluni/src/test/java/awt/font/NumericShaper/ShapingTest.java
new file mode 100644
index 0000000..62bcefb
--- /dev/null
+++ b/ojluni/src/test/java/awt/font/NumericShaper/ShapingTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6842557 6943963 6959267
+ * @summary confirm that shaping works as expected. (Mainly for new characters which were added in Unicode 5 and 6)
+ * used where appropriate.
+ */
+
+package test.java.awt.font.NumericShaper;
+
+import java.awt.font.NumericShaper;
+import java.util.EnumSet;
+import static java.awt.font.NumericShaper.*;
+
+import org.testng.annotations.Test;
+
+public class ShapingTest {
+
+    private static boolean err = false;
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        test6842557();
+        test6943963();
+        test6903266();
+
+        if (err) {
+            throw new RuntimeException("shape() returned unexpected value.");
+        }
+    }
+
+    private static void test6842557() {
+        NumericShaper ns_old = getContextualShaper(ARABIC | TAMIL | ETHIOPIC,
+                                   EUROPEAN);
+        NumericShaper ns_new = getContextualShaper(EnumSet.of(
+                                   Range.ARABIC, Range.TAMIL, Range.ETHIOPIC),
+                                   Range.EUROPEAN);
+
+        String[][] data = {
+           // Arabic "October 10"
+          {"\u0623\u0643\u062a\u0648\u0628\u0631 10",
+           "\u0623\u0643\u062a\u0648\u0628\u0631 \u0661\u0660"},
+
+           // Tamil "Year 2009"
+          {"\u0b86\u0ba3\u0bcd\u0b9f\u0bc1 2009",
+           "\u0b86\u0ba3\u0bcd\u0b9f\u0bc1 \u0be8\u0be6\u0be6\u0bef"},
+           // "\u0be800\u0bef is returned by pre-JDK7 because Tamil zero was not
+           //  included in Unicode 4.0.0.
+
+           // Ethiopic "Syllable<HA> 2009"
+          {"\u1200 2009",
+           "\u1200 \u136a00\u1371"},
+           // Ethiopic zero doesn't exist even in Unicode 5.1.0.
+        };
+
+        for (int i = 0; i < data.length; i++) {
+            checkResult("ARABIC | TAMIL | ETHIOPIC",
+                        ns_old, data[i][0], data[i][1]);
+
+            checkResult("Range.ARABIC, Range.TAMIL, Range.ETHIOPIC",
+                        ns_new, data[i][0], data[i][1]);
+        }
+    }
+
+    private static void test6943963() {
+        // Needed to reproduce this bug.
+        NumericShaper ns_dummy = getContextualShaper(ARABIC | TAMIL | ETHIOPIC,
+                                   EUROPEAN);
+        char[] c = "\u1200 1".toCharArray();
+        ns_dummy.shape(c, 0, c.length);
+
+
+        String given = "\u0627\u0628 456";
+        String expected_ARABIC = "\u0627\u0628 \u0664\u0665\u0666";
+        String expected_EASTERN_ARABIC = "\u0627\u0628 \u06f4\u06f5\u06f6";
+
+        NumericShaper ns = getContextualShaper(ARABIC);
+        checkResult("ARABIC", ns, given, expected_ARABIC);
+
+        ns = getContextualShaper(EnumSet.of(Range.ARABIC));
+        checkResult("Range.ARABIC", ns, given, expected_ARABIC);
+
+        ns = getContextualShaper(EASTERN_ARABIC);
+        checkResult("EASTERN_ARABIC", ns, given, expected_EASTERN_ARABIC);
+
+        ns = getContextualShaper(EnumSet.of(Range.EASTERN_ARABIC));
+        checkResult("Range.EASTERN_ARABIC", ns, given, expected_EASTERN_ARABIC);
+
+        ns = getContextualShaper(ARABIC | EASTERN_ARABIC);
+        checkResult("ARABIC | EASTERN_ARABIC", ns, given, expected_EASTERN_ARABIC);
+
+        ns = getContextualShaper(EnumSet.of(Range.ARABIC, Range.EASTERN_ARABIC));
+        checkResult("Range.ARABIC, Range.EASTERN_ARABIC", ns, given, expected_EASTERN_ARABIC);
+    }
+
+    private static void test6903266() {
+        NumericShaper ns = getContextualShaper(EnumSet.of(Range.TAI_THAM_HORA));
+        String given = "\u1a20 012";
+        String expected = "\u1a20 \u1a80\u1a81\u1a82";
+        checkResult("Range.TAI_THAM_HORA", ns, given, expected);
+
+        ns = getContextualShaper(EnumSet.of(Range.TAI_THAM_HORA,
+                                            Range.TAI_THAM_THAM));
+        given = "\u1a20 012";
+        expected = "\u1a20 \u1a90\u1a91\u1a92"; // Tham digits are prioritized.
+        checkResult("Range.TAI_THAM_HORA, Range.TAI_THAM_THAM", ns, given, expected);
+
+        ns = getContextualShaper(EnumSet.of(Range.JAVANESE));
+        given = "\ua984 012";
+        expected = "\ua984 \ua9d0\ua9d1\ua9d2";
+        checkResult("Range.JAVANESE", ns, given, expected);
+
+        ns = getContextualShaper(EnumSet.of(Range.TAI_THAM_THAM));
+        given = "\u1a20 012";
+        expected = "\u1a20 \u1a90\u1a91\u1a92";
+        checkResult("Range.TAI_THAM_THAM", ns, given, expected);
+
+        ns = getContextualShaper(EnumSet.of(Range.MEETEI_MAYEK));
+        given = "\uabc0 012";
+        expected = "\uabc0 \uabf0\uabf1\uabf2";
+        checkResult("Range.MEETEI_MAYEK", ns, given, expected);
+    }
+
+    private static void checkResult(String ranges, NumericShaper ns,
+                                    String given, String expected) {
+        char[] text = given.toCharArray();
+        ns.shape(text, 0, text.length);
+        String got = new String(text);
+
+        if (!expected.equals(got)) {
+            err = true;
+            System.err.println("Error with range(s) <" + ranges + ">.");
+            System.err.println("  text     = " + given);
+            System.err.println("  got      = " + got);
+            System.err.println("  expected = " + expected);
+        } else {
+            System.out.println("OK with range(s) <" + ranges + ">.");
+            System.out.println("  text     = " + given);
+            System.out.println("  got      = " + got);
+            System.out.println("  expected = " + expected);
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/lang/Math/DivModTests.java b/ojluni/src/test/java/lang/Math/DivModTests.java
new file mode 100644
index 0000000..7e9fb61
--- /dev/null
+++ b/ojluni/src/test/java/lang/Math/DivModTests.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.lang.Math;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+/**
+ * @test Test Math and StrictMath Floor Div / Modulo operations.
+ * @bug 6282196
+ * @summary Basic tests for Floor division and modulo methods for both Math
+ * and StrictMath for int and long datatypes.
+ */
+public class DivModTests {
+
+    /**
+     * Report a test failure and increment the error count.
+     * @param message the formatting string
+     * @param args the variable number of arguments for the message.
+     */
+    static void fail(String message, Object... args) {
+        final String formattedMessage = String.format(message, args);
+        Assert.fail(formattedMessage);
+    }
+
+    /**
+     * Test the integer floorDiv and floorMod methods.
+     * Math and StrictMath tested and the same results are expected for both.
+     */
+    @Test
+    public void testIntFloorDivMod() {
+        testIntFloorDivMod(4, 0, new ArithmeticException(), new ArithmeticException()); // Should throw ArithmeticException
+        testIntFloorDivMod(4, 3, 1, 1);
+        testIntFloorDivMod(3, 3, 1, 0);
+        testIntFloorDivMod(2, 3, 0, 2);
+        testIntFloorDivMod(1, 3, 0, 1);
+        testIntFloorDivMod(0, 3, 0, 0);
+        testIntFloorDivMod(4, -3, -2, -2);
+        testIntFloorDivMod(3, -3, -1, 0);
+        testIntFloorDivMod(2, -3, -1, -1);
+        testIntFloorDivMod(1, -3, -1, -2);
+        testIntFloorDivMod(0, -3, 0, 0);
+        testIntFloorDivMod(-1, 3, -1, 2);
+        testIntFloorDivMod(-2, 3, -1, 1);
+        testIntFloorDivMod(-3, 3, -1, 0);
+        testIntFloorDivMod(-4, 3, -2, 2);
+        testIntFloorDivMod(-1, -3, 0, -1);
+        testIntFloorDivMod(-2, -3, 0, -2);
+        testIntFloorDivMod(-3, -3, 1, 0);
+        testIntFloorDivMod(-4, -3, 1, -1);
+        testIntFloorDivMod(Integer.MAX_VALUE, 1, Integer.MAX_VALUE, 0);
+        testIntFloorDivMod(Integer.MAX_VALUE, -1, -Integer.MAX_VALUE, 0);
+        testIntFloorDivMod(Integer.MAX_VALUE, 3, 715827882, 1);
+        testIntFloorDivMod(Integer.MAX_VALUE - 1, 3, 715827882, 0);
+        testIntFloorDivMod(Integer.MIN_VALUE, 3, -715827883, 1);
+        testIntFloorDivMod(Integer.MIN_VALUE + 1, 3, -715827883, 2);
+        testIntFloorDivMod(Integer.MIN_VALUE + 1, -1, Integer.MAX_VALUE, 0);
+        // Special case of integer overflow
+        testIntFloorDivMod(Integer.MIN_VALUE, -1, Integer.MIN_VALUE, 0);
+    }
+
+    /**
+     * Test FloorDiv and then FloorMod with int data.
+     */
+    static void testIntFloorDivMod(int x, int y, Object divExpected, Object modExpected) {
+        testIntFloorDiv(x, y, divExpected);
+        testIntFloorMod(x, y, modExpected);
+    }
+
+    /**
+     * Test FloorDiv with int data.
+     */
+    static void testIntFloorDiv(int x, int y, Object expected) {
+        Object result = doFloorDiv(x, y);
+        if (!resultEquals(result, expected)) {
+            fail("FAIL: Math.floorDiv(%d, %d) = %s; expected %s%n", x, y, result, expected);
+        }
+
+        Object strict_result = doStrictFloorDiv(x, y);
+        if (!resultEquals(strict_result, expected)) {
+            fail("FAIL: StrictMath.floorDiv(%d, %d) = %s; expected %s%n", x, y, strict_result, expected);
+        }
+    }
+
+    /**
+     * Test FloorMod with int data.
+     */
+    static void testIntFloorMod(int x, int y, Object expected) {
+        Object result = doFloorMod(x, y);
+        if (!resultEquals(result, expected)) {
+            fail("FAIL: Math.floorMod(%d, %d) = %s; expected %s%n", x, y, result, expected);
+        }
+
+        Object strict_result = doStrictFloorMod(x, y);
+        if (!resultEquals(strict_result, expected)) {
+            fail("FAIL: StrictMath.floorMod(%d, %d) = %s; expected %s%n", x, y, strict_result, expected);
+        }
+
+        try {
+            // Verify result against double precision floor function
+            int tmp = x / y;     // Force ArithmeticException for divide by zero
+            double ff = x - Math.floor((double)x / (double)y) * y;
+            int fr = (int)ff;
+            boolean t = (fr == ((Integer)result));
+            if (!result.equals(fr)) {
+                fail("FAIL: Math.floorMod(%d, %d) = %s differs from Math.floor(x, y): %d%n", x, y, result, fr);
+            }
+        } catch (ArithmeticException ae) {
+            if (y != 0) {
+                fail("FAIL: Math.floorMod(%d, %d); unexpected %s%n", x, y, ae);
+            }
+        }
+    }
+
+    /**
+     * Test the floorDiv and floorMod methods for primitive long.
+     */
+    @Test
+    public void testLongFloorDivMod() {
+        testLongFloorDivMod(4L, 0L, new ArithmeticException(), new ArithmeticException()); // Should throw ArithmeticException
+        testLongFloorDivMod(4L, 3L, 1L, 1L);
+        testLongFloorDivMod(3L, 3L, 1L, 0L);
+        testLongFloorDivMod(2L, 3L, 0L, 2L);
+        testLongFloorDivMod(1L, 3L, 0L, 1L);
+        testLongFloorDivMod(0L, 3L, 0L, 0L);
+        testLongFloorDivMod(4L, -3L, -2L, -2L);
+        testLongFloorDivMod(3L, -3L, -1L, 0l);
+        testLongFloorDivMod(2L, -3L, -1L, -1L);
+        testLongFloorDivMod(1L, -3L, -1L, -2L);
+        testLongFloorDivMod(0L, -3L, 0L, 0L);
+        testLongFloorDivMod(-1L, 3L, -1L, 2L);
+        testLongFloorDivMod(-2L, 3L, -1L, 1L);
+        testLongFloorDivMod(-3L, 3L, -1L, 0L);
+        testLongFloorDivMod(-4L, 3L, -2L, 2L);
+        testLongFloorDivMod(-1L, -3L, 0L, -1L);
+        testLongFloorDivMod(-2L, -3L, 0L, -2L);
+        testLongFloorDivMod(-3L, -3L, 1L, 0L);
+        testLongFloorDivMod(-4L, -3L, 1L, -1L);
+
+        testLongFloorDivMod(Long.MAX_VALUE, 1, Long.MAX_VALUE, 0L);
+        testLongFloorDivMod(Long.MAX_VALUE, -1, -Long.MAX_VALUE, 0L);
+        testLongFloorDivMod(Long.MAX_VALUE, 3L, Long.MAX_VALUE / 3L, 1L);
+        testLongFloorDivMod(Long.MAX_VALUE - 1L, 3L, (Long.MAX_VALUE - 1L) / 3L, 0L);
+        testLongFloorDivMod(Long.MIN_VALUE, 3L, Long.MIN_VALUE / 3L - 1L, 1L);
+        testLongFloorDivMod(Long.MIN_VALUE + 1L, 3L, Long.MIN_VALUE / 3L - 1L, 2L);
+        testLongFloorDivMod(Long.MIN_VALUE + 1, -1, Long.MAX_VALUE, 0L);
+        // Special case of integer overflow
+        testLongFloorDivMod(Long.MIN_VALUE, -1, Long.MIN_VALUE, 0L);
+    }
+
+    /**
+     * Test the long floorDiv and floorMod methods.
+     * Math and StrictMath are tested and the same results are expected for both.
+     */
+    static void testLongFloorDivMod(long x, long y, Object divExpected, Object modExpected) {
+        testLongFloorDiv(x, y, divExpected);
+        testLongFloorMod(x, y, modExpected);
+    }
+
+    /**
+     * Test FloorDiv with long arguments against expected value.
+     * The expected value is usually a Long but in some cases  is
+     * an ArithmeticException.
+     *
+     * @param x dividend
+     * @param y modulus
+     * @param expected expected value,
+     */
+    static void testLongFloorDiv(long x, long y, Object expected) {
+        Object result = doFloorDiv(x, y);
+        if (!resultEquals(result, expected)) {
+            fail("FAIL: long Math.floorDiv(%d, %d) = %s; expected %s%n", x, y, result, expected);
+        }
+
+        Object strict_result = doStrictFloorDiv(x, y);
+        if (!resultEquals(strict_result, expected)) {
+            fail("FAIL: long StrictMath.floorDiv(%d, %d) = %s; expected %s%n", x, y, strict_result, expected);
+        }
+    }
+
+    /**
+     * Test FloorMod of long arguments against expected value.
+     * The expected value is usually a Long but in some cases  is
+     * an ArithmeticException.
+     *
+     * @param x dividend
+     * @param y modulus
+     * @param expected expected value
+     */
+    static void testLongFloorMod(long x, long y, Object expected) {
+        Object result = doFloorMod(x, y);
+        if (!resultEquals(result, expected)) {
+            fail("FAIL: long Math.floorMod(%d, %d) = %s; expected %s%n", x, y, result, expected);
+        }
+
+        Object strict_result = doStrictFloorMod(x, y);
+        if (!resultEquals(strict_result, expected)) {
+            fail("FAIL: long StrictMath.floorMod(%d, %d) = %s; expected %s%n", x, y, strict_result, expected);
+        }
+
+        try {
+            // Verify the result against BigDecimal rounding mode.
+            BigDecimal xD = new BigDecimal(x);
+            BigDecimal yD = new BigDecimal(y);
+            BigDecimal resultD = xD.divide(yD, RoundingMode.FLOOR);
+            resultD = resultD.multiply(yD);
+            resultD = xD.subtract(resultD);
+            long fr = resultD.longValue();
+            if (!result.equals(fr)) {
+                fail("FAIL: Long.floorMod(%d, %d) = %d is different than BigDecimal result: %d%n", x, y, result, fr);
+
+            }
+        } catch (ArithmeticException ae) {
+            if (y != 0) {
+                fail("FAIL: long Math.floorMod(%d, %d); unexpected ArithmeticException from bigdecimal");
+            }
+        }
+    }
+
+    /**
+     * Test the floorDiv and floorMod methods for mixed long and int.
+     */
+    @Test
+    public void testLongIntFloorDivMod() {
+        testLongIntFloorDivMod(4L, 0, new ArithmeticException(), new ArithmeticException()); // Should throw ArithmeticException
+        testLongIntFloorDivMod(4L, 3, 1L, 1);
+        testLongIntFloorDivMod(3L, 3, 1L, 0);
+        testLongIntFloorDivMod(2L, 3, 0L, 2);
+        testLongIntFloorDivMod(1L, 3, 0L, 1);
+        testLongIntFloorDivMod(0L, 3, 0L, 0);
+        testLongIntFloorDivMod(4L, -3, -2L, -2);
+        testLongIntFloorDivMod(3L, -3, -1L, 0);
+        testLongIntFloorDivMod(2L, -3, -1L, -1);
+        testLongIntFloorDivMod(1L, -3, -1L, -2);
+        testLongIntFloorDivMod(0L, -3, 0L, 0);
+        testLongIntFloorDivMod(-1L, 3, -1L, 2);
+        testLongIntFloorDivMod(-2L, 3, -1L, 1);
+        testLongIntFloorDivMod(-3L, 3, -1L, 0);
+        testLongIntFloorDivMod(-4L, 3, -2L, 2);
+        testLongIntFloorDivMod(-1L, -3, 0L, -1);
+        testLongIntFloorDivMod(-2L, -3, 0L, -2);
+        testLongIntFloorDivMod(-3L, -3, 1L, 0);
+        testLongIntFloorDivMod(-4L, -3, 1L, -1);
+
+        testLongIntFloorDivMod(Long.MAX_VALUE, 1, Long.MAX_VALUE, 0);
+        testLongIntFloorDivMod(Long.MAX_VALUE, -1, -Long.MAX_VALUE, 0);
+        testLongIntFloorDivMod(Long.MAX_VALUE, 3, Long.MAX_VALUE / 3L, 1);
+        testLongIntFloorDivMod(Long.MAX_VALUE - 1L, 3, (Long.MAX_VALUE - 1L) / 3L, 0);
+        testLongIntFloorDivMod(Long.MIN_VALUE, 3, Long.MIN_VALUE / 3L - 1L, 1);
+        testLongIntFloorDivMod(Long.MIN_VALUE + 1L, 3, Long.MIN_VALUE / 3L - 1L, 2);
+        testLongIntFloorDivMod(Long.MIN_VALUE + 1, -1, Long.MAX_VALUE, 0);
+        // Special case of integer overflow
+        testLongIntFloorDivMod(Long.MIN_VALUE, -1, Long.MIN_VALUE, 0);
+    }
+
+    /**
+     * Test the integer floorDiv and floorMod methods.
+     * Math and StrictMath are tested and the same results are expected for both.
+     */
+    static void testLongIntFloorDivMod(long x, int y, Object divExpected, Object modExpected) {
+        testLongIntFloorDiv(x, y, divExpected);
+        testLongIntFloorMod(x, y, modExpected);
+    }
+
+    /**
+     * Test FloorDiv with long arguments against expected value.
+     * The expected value is usually a Long but in some cases  is
+     * an ArithmeticException.
+     *
+     * @param x dividend
+     * @param y modulus
+     * @param expected expected value,
+     */
+    static void testLongIntFloorDiv(long x, int y, Object expected) {
+        Object result = doFloorDiv(x, y);
+        if (!resultEquals(result, expected)) {
+            fail("FAIL: long Math.floorDiv(%d, %d) = %s; expected %s%n", x, y, result, expected);
+        }
+
+        Object strict_result = doStrictFloorDiv(x, y);
+        if (!resultEquals(strict_result, expected)) {
+            fail("FAIL: long StrictMath.floorDiv(%d, %d) = %s; expected %s%n", x, y, strict_result, expected);
+        }
+    }
+
+    /**
+     * Test FloorMod of long arguments against expected value.
+     * The expected value is usually a Long but in some cases  is
+     * an ArithmeticException.
+     *
+     * @param x dividend
+     * @param y modulus
+     * @param expected expected value
+     */
+    static void testLongIntFloorMod(long x, int y, Object expected) {
+        Object result = doFloorMod(x, y);
+        if (!resultEquals(result, expected)) {
+            fail("FAIL: long Math.floorMod(%d, %d) = %s; expected %s%n", x, y, result, expected);
+        }
+
+        Object strict_result = doStrictFloorMod(x, y);
+        if (!resultEquals(strict_result, expected)) {
+            fail("FAIL: long StrictMath.floorMod(%d, %d) = %s; expected %s%n", x, y, strict_result, expected);
+        }
+
+        try {
+            // Verify the result against BigDecimal rounding mode.
+            BigDecimal xD = new BigDecimal(x);
+            BigDecimal yD = new BigDecimal(y);
+            BigDecimal resultD = xD.divide(yD, RoundingMode.FLOOR);
+            resultD = resultD.multiply(yD);
+            resultD = xD.subtract(resultD);
+            // Android-changed: compare with int value for int Math.floorMod(long, int)
+            // long fr = resultD.longValue();
+            int fr = resultD.intValue();
+            if (!result.equals(fr)) {
+                fail("FAIL: Long.floorMod(%d, %d) = %d is different than BigDecimal result: %d%n", x, y, result, fr);
+            }
+        } catch (ArithmeticException ae) {
+            if (y != 0) {
+                fail("FAIL: long Math.floorMod(%d, %d); unexpected ArithmeticException from bigdecimal");
+            }
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doFloorDiv(int x, int y) {
+        try {
+            return Math.floorDiv(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doFloorDiv(long x, int y) {
+        try {
+            return Math.floorDiv(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doFloorDiv(long x, long y) {
+        try {
+            return Math.floorDiv(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doFloorMod(int x, int y) {
+        try {
+            return Math.floorMod(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doFloorMod(long x, int y) {
+        try {
+            return Math.floorMod(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doFloorMod(long x, long y) {
+        try {
+            return Math.floorMod(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doStrictFloorDiv(int x, int y) {
+        try {
+            return StrictMath.floorDiv(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doStrictFloorDiv(long x, int y) {
+        try {
+            return StrictMath.floorDiv(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doStrictFloorDiv(long x, long y) {
+        try {
+            return StrictMath.floorDiv(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doStrictFloorMod(int x, int y) {
+        try {
+            return StrictMath.floorMod(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doStrictFloorMod(long x, int y) {
+        try {
+            return StrictMath.floorMod(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Invoke floorDiv and return the result or any exception.
+     * @param x the x value
+     * @param y the y value
+     * @return the result Integer or an exception.
+     */
+    static Object doStrictFloorMod(long x, long y) {
+        try {
+            return StrictMath.floorMod(x, y);
+        } catch (ArithmeticException ae) {
+            return ae;
+        }
+    }
+
+    /**
+     * Returns a boolean by comparing the result and the expected value.
+     * The equals method is not defined for ArithmeticException but it is
+     * desirable to have equals return true if the expected and the result
+     * both threw the same exception (class and message.)
+     *
+     * @param result the result from testing the method
+     * @param expected the expected value
+     * @return true if the result is equal to the expected values; false otherwise.
+     */
+    static boolean resultEquals(Object result, Object expected) {
+        if (result.getClass() != expected.getClass()) {
+            fail("FAIL: Result type mismatch, %s; expected: %s%n",
+                    result.getClass().getName(), expected.getClass().getName());
+            return false;
+        }
+
+        if (result.equals(expected)) {
+            return true;
+        }
+        // Handle special case to compare ArithmeticExceptions
+        if (result instanceof ArithmeticException && expected instanceof ArithmeticException) {
+            return true;
+        }
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/lang/Math/ExactArithTests.java b/ojluni/src/test/java/lang/Math/ExactArithTests.java
new file mode 100644
index 0000000..3109d41
--- /dev/null
+++ b/ojluni/src/test/java/lang/Math/ExactArithTests.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.lang.Math;
+
+import java.math.BigInteger;
+
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import static org.testng.Assert.fail;
+
+// Android-changed: Added test annotations, remove main() method.
+/**
+ * @test Test for Math.*Exact integer and long methods.
+ * @bug 6708398
+ * @summary Basic tests for Math exact arithmetic operations.
+ *
+ * @author Roger Riggs
+ */
+public class ExactArithTests {
+
+    /**
+     * Test Math.addExact, multiplyExact, subtractExact, toIntValue methods
+     * with {@code int} arguments.
+     */
+    @Test
+    public void testIntegerExact() {
+        testIntegerExact(0, 0);
+        testIntegerExact(1, 1);
+        testIntegerExact(1, -1);
+        testIntegerExact(-1, 1);
+        testIntegerExact(1000, 2000);
+
+        testIntegerExact(Integer.MIN_VALUE, Integer.MIN_VALUE);
+        testIntegerExact(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testIntegerExact(Integer.MIN_VALUE, 1);
+        testIntegerExact(Integer.MAX_VALUE, 1);
+        testIntegerExact(Integer.MIN_VALUE, 2);
+        testIntegerExact(Integer.MAX_VALUE, 2);
+        testIntegerExact(Integer.MIN_VALUE, -1);
+        testIntegerExact(Integer.MAX_VALUE, -1);
+        testIntegerExact(Integer.MIN_VALUE, -2);
+        testIntegerExact(Integer.MAX_VALUE, -2);
+
+    }
+
+    /**
+     * Test exact arithmetic by comparing with the same operations using long
+     * and checking that the result is the same as the integer truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testIntegerExact(int x, int y) {
+        try {
+            // Test addExact
+            int sum = Math.addExact(x, y);
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 != sum2) {
+                fail("FAIL: int Math.addExact(" + x + " + " + y + ") = " + sum + "; expected Arithmetic exception");
+            } else if (sum != sum2) {
+                fail("FAIL: long Math.addExact(" + x + " + " + y + ") = " + sum + "; expected: " + sum2);
+            }
+        } catch (ArithmeticException ex) {
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 == sum2) {
+                fail("FAIL: int Math.addExact(" + x + " + " + y + ")" + "; Unexpected exception: " + ex);
+
+            }
+        }
+
+        try {
+            // Test subtractExact
+            int diff = Math.subtractExact(x, y);
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 != diff2) {
+                fail("FAIL: int Math.subtractExact(" + x + " - " + y + ") = " + diff + "; expected: " + diff2);
+            }
+
+        } catch (ArithmeticException ex) {
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 == diff2) {
+                fail("FAIL: int Math.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            int product = Math.multiplyExact(x, y);
+            long m2 = (long) x * (long) y;
+            if ((int) m2 != m2) {
+                fail("FAIL: int Math.multiplyExact(" + x + " * " + y + ") = " + product + "; expected: " + m2);
+            }
+        } catch (ArithmeticException ex) {
+            long m2 = (long) x * (long) y;
+            if ((int) m2 == m2) {
+                fail("FAIL: int Math.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test incrementExact
+            int inc = Math.incrementExact(x);
+            long inc2 = (long) x + 1L;
+            if ((int) inc2 != inc2) {
+                fail("FAIL: int Math.incrementExact(" + x + ") = " + inc + "; expected Arithmetic exception");
+            } else if (inc != inc2) {
+                fail("FAIL: long Math.incrementExact(" + x + ") = " + inc + "; expected: " + inc2);
+            }
+        } catch (ArithmeticException ex) {
+            long inc2 = (long) x + 1L;
+            if ((int) inc2 == inc2) {
+                fail("FAIL: int Math.incrementExact(" + x + ")" + "; Unexpected exception: " + ex);
+
+            }
+        }
+
+        try {
+            // Test decrementExact
+            int dec = Math.decrementExact(x);
+            long dec2 = (long) x - 1L;
+            if ((int) dec2 != dec2) {
+                fail("FAIL: int Math.decrementExact(" + x + ") = " + dec + "; expected Arithmetic exception");
+            } else if (dec != dec2) {
+                fail("FAIL: long Math.decrementExact(" + x + ") = " + dec + "; expected: " + dec2);
+            }
+        } catch (ArithmeticException ex) {
+            long dec2 = (long) x - 1L;
+            if ((int) dec2 == dec2) {
+                fail("FAIL: int Math.decrementExact(" + x + ")" + "; Unexpected exception: " + ex);
+
+            }
+        }
+
+        try {
+            // Test negateExact
+            int neg = Math.negateExact(x);
+            long neg2 = -((long)x) ;
+            if ((int) neg2 != neg2) {
+                fail("FAIL: int Math.negateExact(" + x + ") = " + neg + "; expected Arithmetic exception");
+            } else if (neg != neg2) {
+                fail("FAIL: long Math.negateExact(" + x + ") = " + neg + "; expected: " + neg2);
+            }
+        } catch (ArithmeticException ex) {
+            long neg2 = (long) x - 1L;
+            if ((int) neg2 == neg2) {
+                fail("FAIL: int Math.negateExact(" + x + ")" + "; Unexpected exception: " + ex);
+
+            }
+        }
+    }
+
+    /**
+     * Test Math.addExact, multiplyExact, subtractExact, toIntExact methods
+     * with {@code long} arguments.
+     */
+    @Test
+    static void testLongExact() {
+        testLongExactTwice(0, 0);
+        testLongExactTwice(1, 1);
+        testLongExactTwice(1, -1);
+        testLongExactTwice(1000, 2000);
+
+        testLongExactTwice(Long.MIN_VALUE, Long.MIN_VALUE);
+        testLongExactTwice(Long.MAX_VALUE, Long.MAX_VALUE);
+        testLongExactTwice(Long.MIN_VALUE, 1);
+        testLongExactTwice(Long.MAX_VALUE, 1);
+        testLongExactTwice(Long.MIN_VALUE, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Long.MIN_VALUE, -1);
+        testLongExactTwice(Long.MAX_VALUE, -1);
+        testLongExactTwice(Long.MIN_VALUE, -2);
+        testLongExactTwice(Long.MAX_VALUE, -2);
+        testLongExactTwice(Long.MIN_VALUE/2, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE, -Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE+1, Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MAX_VALUE+1, -Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MIN_VALUE-1, Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE-1, -Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE/2, 2);
+
+    }
+
+    /**
+     * Test each of the exact operations with the arguments and
+     * with the arguments reversed.
+     * @param x
+     * @param y
+     */
+    static void testLongExactTwice(long x, long y) {
+        testLongExact(x, y);
+        testLongExact(y, x);
+    }
+
+
+    /**
+     * Test long exact arithmetic by comparing with the same operations using BigInteger
+     * and checking that the result is the same as the long truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testLongExact(long x, long y) {
+        BigInteger resultBig = null;
+        final BigInteger xBig = BigInteger.valueOf(x);
+        final BigInteger yBig = BigInteger.valueOf(y);
+        try {
+            // Test addExact
+            resultBig = xBig.add(yBig);
+            long sum = Math.addExact(x, y);
+            checkResult("long Math.addExact", x, y, sum, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.addExact(" + x + " + " + y + "); Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test subtractExact
+            resultBig = xBig.subtract(yBig);
+            long diff = Math.subtractExact(x, y);
+            checkResult("long Math.subtractExact", x, y, diff, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            resultBig = xBig.multiply(yBig);
+            long product = Math.multiplyExact(x, y);
+            checkResult("long Math.multiplyExact", x, y, product, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test incrementExact
+            resultBig = xBig.add(BigInteger.ONE);
+            long inc = Math.incrementExact(x);
+            checkResult("long Math.incrementExact", x, 1L, inc, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.incrementExact(" + x + "); Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test decrementExact
+            resultBig = xBig.subtract(BigInteger.ONE);
+            long dec = Math.decrementExact(x);
+            checkResult("long Math.decrementExact", x, 1L, dec, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.decrementExact(" + x + "); Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test negateExact
+            resultBig = xBig.negate();
+            long dec = Math.negateExact(x);
+            checkResult("long Math.negateExact", x, 0L, dec, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.negateExact(" + x + "); Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test toIntExact
+            int value = Math.toIntExact(x);
+            if ((long)value != x) {
+                fail("FAIL: " + "long Math.toIntExact" + "(" + x + ") = " + value + "; expected an arithmetic exception: ");
+            }
+        } catch (ArithmeticException ex) {
+            if (resultBig.bitLength() <= 32) {
+                fail("FAIL: long Math.toIntExact(" + x + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+    }
+
+    /**
+     * Compare the expected and actual results.
+     * @param message message for the error
+     * @param x first argument
+     * @param y second argument
+     * @param result actual result value
+     * @param expected expected result value
+     */
+    static void checkResult(String message, long x, long y, long result, BigInteger expected) {
+        BigInteger resultBig = BigInteger.valueOf(result);
+        if (!inLongRange(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected an arithmetic exception: ");
+        } else if (!resultBig.equals(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected " + expected);
+        }
+    }
+
+    /**
+     * Check if the value fits in 64 bits (a long).
+     * @param value
+     * @return true if the value fits in 64 bits (including the sign).
+     */
+    static boolean inLongRange(BigInteger value) {
+        return value.bitLength() <= 63;
+    }
+
+    /**
+     * Test Math.multiplyExact method with {@code long} and {@code int}
+     * arguments.
+     */
+    @Test
+    static void testLongIntExact() {
+        testLongIntExact(0, 0);
+        testLongIntExact(1, 1);
+        testLongIntExact(1, -1);
+        testLongIntExact(1000, 2000);
+
+        testLongIntExact(Long.MIN_VALUE, Integer.MIN_VALUE);
+        testLongIntExact(Long.MAX_VALUE, Integer.MAX_VALUE);
+        testLongIntExact(Long.MIN_VALUE, 1);
+        testLongIntExact(Long.MAX_VALUE, 1);
+        testLongIntExact(Long.MIN_VALUE, 2);
+        testLongIntExact(Long.MAX_VALUE, 2);
+        testLongIntExact(Long.MIN_VALUE, -1);
+        testLongIntExact(Long.MAX_VALUE, -1);
+        testLongIntExact(Long.MIN_VALUE, -2);
+        testLongIntExact(Long.MAX_VALUE, -2);
+        testLongIntExact(Long.MIN_VALUE/2, 2);
+        testLongIntExact(Long.MAX_VALUE, 2);
+        testLongIntExact(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testLongIntExact(Integer.MAX_VALUE, -Integer.MAX_VALUE);
+        testLongIntExact((long)Integer.MAX_VALUE+1L, Integer.MAX_VALUE);
+        testLongIntExact((long)Integer.MAX_VALUE+1L, -Integer.MAX_VALUE+1);
+        testLongIntExact((long)Integer.MIN_VALUE-1L, Integer.MIN_VALUE);
+        testLongIntExact((long)Integer.MIN_VALUE-1, Integer.MAX_VALUE);
+        testLongIntExact(Integer.MIN_VALUE/2, 2);
+    }
+
+    /**
+     * Test long-int exact arithmetic by comparing with the same operations using BigInteger
+     * and checking that the result is the same as the long truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testLongIntExact(long x, int y) {
+        BigInteger resultBig = null;
+        final BigInteger xBig = BigInteger.valueOf(x);
+        final BigInteger yBig = BigInteger.valueOf(y);
+
+        try {
+            // Test multiplyExact
+            resultBig = xBig.multiply(yBig);
+            long product = Math.multiplyExact(x, y);
+            checkResult("long Math.multiplyExact", x, y, product, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long Math.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/lang/Math/MultiplicationTests.java b/ojluni/src/test/java/lang/Math/MultiplicationTests.java
new file mode 100644
index 0000000..5bd17a6
--- /dev/null
+++ b/ojluni/src/test/java/lang/Math/MultiplicationTests.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.lang.Math;
+
+import java.math.BigInteger;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+// Android-removed: Private constructor, main() method, randomness tests.
+public class MultiplicationTests {
+
+    // Calculate high 64 bits of 128 product using BigInteger.
+    private static long multiplyHighBigInt(long x, long y) {
+        return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y))
+            .shiftRight(64).longValue();
+    }
+
+    @Test
+    public void testMultiplyHigh() {
+        // check some boundary cases
+        long[][] v = new long[][]{
+            {0L, 0L},
+            {-1L, 0L},
+            {0L, -1L},
+            {1L, 0L},
+            {0L, 1L},
+            {-1L, -1L},
+            {-1L, 1L},
+            {1L, -1L},
+            {1L, 1L},
+            {Long.MAX_VALUE, Long.MAX_VALUE},
+            {Long.MAX_VALUE, -Long.MAX_VALUE},
+            {-Long.MAX_VALUE, Long.MAX_VALUE},
+            {-Long.MAX_VALUE, -Long.MAX_VALUE},
+            {Long.MAX_VALUE, Long.MIN_VALUE},
+            {Long.MIN_VALUE, Long.MAX_VALUE},
+            {Long.MIN_VALUE, Long.MIN_VALUE}
+        };
+
+        for (long[] xy : v) {
+            long x = xy[0];
+            long y = xy[1];
+            long p1 = multiplyHighBigInt(x, y);
+            long p2 = Math.multiplyHigh(x, y);
+            assertEquals(p1, p2);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/lang/StrictMath/ExactArithTests.java b/ojluni/src/test/java/lang/StrictMath/ExactArithTests.java
new file mode 100644
index 0000000..0ce1306
--- /dev/null
+++ b/ojluni/src/test/java/lang/StrictMath/ExactArithTests.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.lang.StrictMath;
+
+import java.math.BigInteger;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.fail;
+
+/**
+ * @test Test for StrictMath.*Exact integer and long methods.
+ * @bug 6708398
+ * @summary Basic tests for StrictMath exact arithmetic operations.
+ *
+ * @author Roger Riggs
+ */
+public class ExactArithTests {
+
+    // BEGIN Android-removed: main(), error counter, replace fail() with testng.Assert.fail().
+    /**
+     * The count of test errors.
+     *
+    private static int errors = 0;
+
+    /**
+     * @param args the command line arguments
+     *
+    public static void main(String[] args) {
+        testIntegerExact();
+        testLongExact();
+
+        if (errors > 0) {
+            throw new RuntimeException(errors + " errors found in ExactArithTests.");
+        }
+    }
+
+    static void fail(String message) {
+        errors++;
+        System.err.println(message);
+    }
+    */
+    // END Android-removed: main(), error counter.
+
+    /**
+     * Test StrictMath.addExact, multiplyExact, subtractExact, toIntValue methods
+     * with {@code int} arguments.
+     */
+    // Android-added: @Test annotation.
+    @Test
+    static void testIntegerExact() {
+        testIntegerExact(0, 0);
+        testIntegerExact(1, 1);
+        testIntegerExact(1, -1);
+        testIntegerExact(-1, 1);
+        testIntegerExact(1000, 2000);
+
+        testIntegerExact(Integer.MIN_VALUE, Integer.MIN_VALUE);
+        testIntegerExact(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testIntegerExact(Integer.MIN_VALUE, 1);
+        testIntegerExact(Integer.MAX_VALUE, 1);
+        testIntegerExact(Integer.MIN_VALUE, 2);
+        testIntegerExact(Integer.MAX_VALUE, 2);
+        testIntegerExact(Integer.MIN_VALUE, -1);
+        testIntegerExact(Integer.MAX_VALUE, -1);
+        testIntegerExact(Integer.MIN_VALUE, -2);
+        testIntegerExact(Integer.MAX_VALUE, -2);
+
+    }
+
+    /**
+     * Test exact arithmetic by comparing with the same operations using long
+     * and checking that the result is the same as the integer truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testIntegerExact(int x, int y) {
+        try {
+            // Test addExact
+            int sum = StrictMath.addExact(x, y);
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 != sum2) {
+                fail("FAIL: int StrictMath.addExact(" + x + " + " + y + ") = " + sum + "; expected Arithmetic exception");
+            } else if (sum != sum2) {
+                fail("FAIL: long StrictMath.addExact(" + x + " + " + y + ") = " + sum + "; expected: " + sum2);
+            }
+        } catch (ArithmeticException ex) {
+            long sum2 = (long) x + (long) y;
+            if ((int) sum2 == sum2) {
+                fail("FAIL: int StrictMath.addExact(" + x + " + " + y + ")" + "; Unexpected exception: " + ex);
+
+            }
+        }
+
+        try {
+            // Test subtractExact
+            int diff = StrictMath.subtractExact(x, y);
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 != diff2) {
+                fail("FAIL: int StrictMath.subtractExact(" + x + " - " + y + ") = " + diff + "; expected: " + diff2);
+            }
+
+        } catch (ArithmeticException ex) {
+            long diff2 = (long) x - (long) y;
+            if ((int) diff2 == diff2) {
+                fail("FAIL: int StrictMath.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            int product = StrictMath.multiplyExact(x, y);
+            long m2 = (long) x * (long) y;
+            if ((int) m2 != m2) {
+                fail("FAIL: int StrictMath.multiplyExact(" + x + " * " + y + ") = " + product + "; expected: " + m2);
+            }
+        } catch (ArithmeticException ex) {
+            long m2 = (long) x * (long) y;
+            if ((int) m2 == m2) {
+                fail("FAIL: int StrictMath.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+    }
+
+    /**
+     * Test StrictMath.addExact, multiplyExact, subtractExact, toIntExact methods
+     * with {@code long} arguments.
+     */
+    // Android-added: @Test annotation.
+    @Test
+    static void testLongExact() {
+        testLongExactTwice(0, 0);
+        testLongExactTwice(1, 1);
+        testLongExactTwice(1, -1);
+        testLongExactTwice(1000, 2000);
+
+        testLongExactTwice(Long.MIN_VALUE, Long.MIN_VALUE);
+        testLongExactTwice(Long.MAX_VALUE, Long.MAX_VALUE);
+        testLongExactTwice(Long.MIN_VALUE, 1);
+        testLongExactTwice(Long.MAX_VALUE, 1);
+        testLongExactTwice(Long.MIN_VALUE, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Long.MIN_VALUE, -1);
+        testLongExactTwice(Long.MAX_VALUE, -1);
+        testLongExactTwice(Long.MIN_VALUE, -2);
+        testLongExactTwice(Long.MAX_VALUE, -2);
+        testLongExactTwice(Long.MIN_VALUE/2, 2);
+        testLongExactTwice(Long.MAX_VALUE, 2);
+        testLongExactTwice(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE, -Integer.MAX_VALUE);
+        testLongExactTwice(Integer.MAX_VALUE+1, Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MAX_VALUE+1, -Integer.MAX_VALUE+1);
+        testLongExactTwice(Integer.MIN_VALUE-1, Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE-1, -Integer.MIN_VALUE-1);
+        testLongExactTwice(Integer.MIN_VALUE/2, 2);
+
+    }
+
+    /**
+     * Test each of the exact operations with the arguments and
+     * with the arguments reversed.
+     * @param x
+     * @param y
+     */
+    static void testLongExactTwice(long x, long y) {
+        testLongExact(x, y);
+        testLongExact(y, x);
+    }
+
+
+    /**
+     * Test long exact arithmetic by comparing with the same operations using BigInteger
+     * and checking that the result is the same as the long truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testLongExact(long x, long y) {
+        BigInteger resultBig = null;
+        final BigInteger xBig = BigInteger.valueOf(x);
+        final BigInteger yBig = BigInteger.valueOf(y);
+        try {
+            // Test addExact
+            resultBig = xBig.add(yBig);
+            long sum = StrictMath.addExact(x, y);
+            checkResult("long StrictMath.addExact", x, y, sum, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long StrictMath.addExact(" + x + " + " + y + "); Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test subtractExact
+            resultBig = xBig.subtract(yBig);
+            long diff = StrictMath.subtractExact(x, y);
+            checkResult("long StrictMath.subtractExact", x, y, diff, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long StrictMath.subtractExact(" + x + " - " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test multiplyExact
+            resultBig = xBig.multiply(yBig);
+            long product = StrictMath.multiplyExact(x, y);
+            checkResult("long StrictMath.multiplyExact", x, y, product, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long StrictMath.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+        try {
+            // Test toIntExact
+            int value = StrictMath.toIntExact(x);
+            if ((long)value != x) {
+                fail("FAIL: " + "long StrictMath.toIntExact" + "(" + x + ") = " + value + "; expected an arithmetic exception: ");
+            }
+        } catch (ArithmeticException ex) {
+            if (resultBig.bitLength() <= 32) {
+                fail("FAIL: long StrictMath.toIntExact(" + x + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+
+    }
+
+    /**
+     * Compare the expected and actual results.
+     * @param message message for the error
+     * @param x first argument
+     * @param y second argument
+     * @param result actual result value
+     * @param expected expected result value
+     */
+    static void checkResult(String message, long x, long y, long result, BigInteger expected) {
+        BigInteger resultBig = BigInteger.valueOf(result);
+        if (!inLongRange(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected an arithmetic exception: ");
+        } else if (!resultBig.equals(expected)) {
+            fail("FAIL: " + message + "(" + x + ", " + y + ") = " + result + "; expected " + expected);
+        }
+    }
+
+    /**
+     * Check if the value fits in 64 bits (a long).
+     * @param value
+     * @return true if the value fits in 64 bits (including the sign).
+     */
+    static boolean inLongRange(BigInteger value) {
+        return value.bitLength() <= 63;
+    }
+
+    // BEGIN Android-added: add multiplyExact(long, int) based on Math.ExactArithTests.
+    /**
+     * Test StrictMath.multiplyExact method with {@code long} and {@code int}
+     * arguments.
+     */
+    @Test
+    static void testLongIntExact() {
+        testLongIntExact(0, 0);
+        testLongIntExact(1, 1);
+        testLongIntExact(1, -1);
+        testLongIntExact(1000, 2000);
+
+        testLongIntExact(Long.MIN_VALUE, Integer.MIN_VALUE);
+        testLongIntExact(Long.MAX_VALUE, Integer.MAX_VALUE);
+        testLongIntExact(Long.MIN_VALUE, 1);
+        testLongIntExact(Long.MAX_VALUE, 1);
+        testLongIntExact(Long.MIN_VALUE, 2);
+        testLongIntExact(Long.MAX_VALUE, 2);
+        testLongIntExact(Long.MIN_VALUE, -1);
+        testLongIntExact(Long.MAX_VALUE, -1);
+        testLongIntExact(Long.MIN_VALUE, -2);
+        testLongIntExact(Long.MAX_VALUE, -2);
+        testLongIntExact(Long.MIN_VALUE/2, 2);
+        testLongIntExact(Long.MAX_VALUE, 2);
+        testLongIntExact(Integer.MAX_VALUE, Integer.MAX_VALUE);
+        testLongIntExact(Integer.MAX_VALUE, -Integer.MAX_VALUE);
+        testLongIntExact((long)Integer.MAX_VALUE+1L, Integer.MAX_VALUE);
+        testLongIntExact((long)Integer.MAX_VALUE+1L, -Integer.MAX_VALUE+1);
+        testLongIntExact((long)Integer.MIN_VALUE-1L, Integer.MIN_VALUE);
+        testLongIntExact((long)Integer.MIN_VALUE-1, Integer.MAX_VALUE);
+        testLongIntExact(Integer.MIN_VALUE/2, 2);
+    }
+
+    /**
+     * Test long-int exact arithmetic by comparing with the same operations using BigInteger
+     * and checking that the result is the same as the long truncation.
+     * Errors are reported with {@link fail}.
+     *
+     * @param x first parameter
+     * @param y second parameter
+     */
+    static void testLongIntExact(long x, int y) {
+        BigInteger resultBig = null;
+        final BigInteger xBig = BigInteger.valueOf(x);
+        final BigInteger yBig = BigInteger.valueOf(y);
+
+        try {
+            // Test multiplyExact
+            resultBig = xBig.multiply(yBig);
+            long product = StrictMath.multiplyExact(x, y);
+            checkResult("long StrictMath.multiplyExact", x, y, product, resultBig);
+        } catch (ArithmeticException ex) {
+            if (inLongRange(resultBig)) {
+                fail("FAIL: long StrictMath.multiplyExact(" + x + " * " + y + ")" + "; Unexpected exception: " + ex);
+            }
+        }
+    }
+    // END Android-added: add multiplyExact(long, int) based on Math.ExactArithTests.
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/lang/StrictMath/MultiplicationTests.java b/ojluni/src/test/java/lang/StrictMath/MultiplicationTests.java
new file mode 100644
index 0000000..3489eee
--- /dev/null
+++ b/ojluni/src/test/java/lang/StrictMath/MultiplicationTests.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run main MultiplicationTests
+ * @bug 5100935
+ * @summary Tests for multiplication methods (use -Dseed=X to set PRNG seed)
+ * @key randomness
+ */
+package test.java.lang.StrictMath;
+
+import java.math.BigInteger;
+// Android-removed: main(), randomness
+// import jdk.test.lib.RandomFactory;
+
+// Android-added: testng.
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import static org.testng.Assert.fail;
+
+public class MultiplicationTests {
+    private MultiplicationTests(){}
+
+    // BEGIN Android-removed: randomness, main().
+    /*
+    // Number of random products to test.
+    private static final int COUNT = 1 << 16;
+
+    // Initialize shared random number generator
+    private static java.util.Random rnd = RandomFactory.getRandom();
+    */
+    // END Android-removed: randomness, main().
+
+    // Calculate high 64 bits of 128 product using BigInteger.
+    private static long multiplyHighBigInt(long x, long y) {
+        return BigInteger.valueOf(x).multiply(BigInteger.valueOf(y))
+                .shiftRight(64).longValue();
+    }
+
+    // Check Math.multiplyHigh(x,y) against multiplyHighBigInt(x,y)
+    private static boolean check(long x, long y) {
+        long p1 = multiplyHighBigInt(x, y);
+        long p2 = StrictMath.multiplyHigh(x, y);
+        if (p1 != p2) {
+            // Android-changed: use testng fail().
+            //System.err.printf("Error - x:%d y:%d p1:%d p2:%d\n", x, y, p1, p2);
+            fail(String.format("Error - x:%d y:%d p1:%d p2:%d\n", x, y, p1, p2));
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    // Android-changed: testng, return void instead of int.
+    @Test
+    public static void testMultiplyHigh() {
+        int failures = 0;
+
+        // check some boundary cases
+        long[][] v = new long[][]{
+                {0L, 0L},
+                {-1L, 0L},
+                {0L, -1L},
+                {1L, 0L},
+                {0L, 1L},
+                {-1L, -1L},
+                {-1L, 1L},
+                {1L, -1L},
+                {1L, 1L},
+                {Long.MAX_VALUE, Long.MAX_VALUE},
+                {Long.MAX_VALUE, -Long.MAX_VALUE},
+                {-Long.MAX_VALUE, Long.MAX_VALUE},
+                {Long.MAX_VALUE, Long.MIN_VALUE},
+                {Long.MIN_VALUE, Long.MAX_VALUE},
+                {Long.MIN_VALUE, Long.MIN_VALUE}
+        };
+
+        for (long[] xy : v) {
+            if(!check(xy[0], xy[1])) {
+                failures++;
+            }
+        }
+
+        // Android-added: assert error counter.
+        Assert.assertEquals(failures, 0);
+
+        // BEGIN Android-removed: error counter, randomness, main().
+        /*
+        // check some random values
+        for (int i = 0; i < COUNT; i++) {
+            if (!check(rnd.nextLong(), rnd.nextLong())) {
+                failures++;
+            }
+        }
+
+        return failures;
+        */
+        // END Android-removed: error counter, randomness, main().
+    }
+
+    // BEGIN Android-removed: error counter, randomness, main().
+    /*
+    public static void main(String argv[]) {
+        int failures = testMultiplyHigh();
+
+        if (failures > 0) {
+            System.err.println("Multiplication testing encountered "
+                    + failures + " failures.");
+            throw new RuntimeException();
+        } else {
+            System.out.println("MultiplicationTests succeeded");
+        }
+    }
+    */
+}
\ No newline at end of file
diff --git a/ojluni/src/test/java/time/tck/java/time/TCKDuration.java b/ojluni/src/test/java/time/tck/java/time/TCKDuration.java
index c12e914..fb11a30 100644
--- a/ojluni/src/test/java/time/tck/java/time/TCKDuration.java
+++ b/ojluni/src/test/java/time/tck/java/time/TCKDuration.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -65,9 +65,11 @@
 import static java.time.temporal.ChronoUnit.MICROS;
 import static java.time.temporal.ChronoUnit.MILLIS;
 import static java.time.temporal.ChronoUnit.MINUTES;
+import static java.time.temporal.ChronoUnit.MONTHS;
 import static java.time.temporal.ChronoUnit.NANOS;
 import static java.time.temporal.ChronoUnit.SECONDS;
 import static java.time.temporal.ChronoUnit.WEEKS;
+import static java.time.temporal.ChronoUnit.YEARS;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
@@ -2266,6 +2268,133 @@
     }
 
     //-----------------------------------------------------------------------
+    //  truncated(TemporalUnit)
+    //-----------------------------------------------------------------------
+    TemporalUnit NINETY_MINS = new TemporalUnit() {
+        @Override
+        public Duration getDuration() {
+            return Duration.ofMinutes(90);
+        }
+        @Override
+        public boolean isDurationEstimated() {
+            return false;
+        }
+        @Override
+        public boolean isDateBased() {
+            return false;
+        }
+        @Override
+        public boolean isTimeBased() {
+            return true;
+        }
+        @Override
+        public boolean isSupportedBy(Temporal temporal) {
+            return false;
+        }
+        @Override
+        public <R extends Temporal> R addTo(R temporal, long amount) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public long between(Temporal temporal1, Temporal temporal2) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public String toString() {
+            return "NinetyMins";
+        }
+    };
+
+    TemporalUnit NINETY_FIVE_MINS = new TemporalUnit() {
+        @Override
+        public Duration getDuration() {
+            return Duration.ofMinutes(95);
+        }
+        @Override
+        public boolean isDurationEstimated() {
+            return false;
+        }
+        @Override
+        public boolean isDateBased() {
+            return false;
+        }
+        @Override
+        public boolean isTimeBased() {
+            return false;
+        }
+        @Override
+        public boolean isSupportedBy(Temporal temporal) {
+            return false;
+        }
+        @Override
+        public <R extends Temporal> R addTo(R temporal, long amount) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public long between(Temporal temporal1, Temporal temporal2) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public String toString() {
+            return "NinetyFiveMins";
+        }
+    };
+
+    @DataProvider(name="truncatedToValid")
+    Object[][] data_truncatedToValid() {
+        return new Object[][] {
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), NANOS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789)},
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), MICROS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_000)},
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), MILLIS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 1230_00_000)},
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), SECONDS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 0)},
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), MINUTES, Duration.ofSeconds(86400 + 3600 + 60, 0)},
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), HOURS, Duration.ofSeconds(86400 + 3600, 0)},
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), DAYS, Duration.ofSeconds(86400, 0)},
+
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 123_456_789), NINETY_MINS, Duration.ofSeconds(86400 + 0, 0)},
+                {Duration.ofSeconds(86400 + 7200 + 60 + 1, 123_456_789), NINETY_MINS, Duration.ofSeconds(86400 + 5400, 0)},
+                {Duration.ofSeconds(86400 + 10800 + 60 + 1, 123_456_789), NINETY_MINS, Duration.ofSeconds(86400 + 10800, 0)},
+
+                {Duration.ofSeconds(-86400 - 3600 - 60 - 1, 123_456_789), MINUTES, Duration.ofSeconds(-86400 - 3600 - 60, 0 )},
+                {Duration.ofSeconds(-86400 - 3600 - 60 - 1, 123_456_789), MICROS, Duration.ofSeconds(-86400 - 3600 - 60 - 1, 123_457_000)},
+
+                {Duration.ofSeconds(86400 + 3600 + 60 + 1, 0), SECONDS, Duration.ofSeconds(86400 + 3600 + 60 + 1, 0)},
+                {Duration.ofSeconds(-86400 - 3600 - 120, 0), MINUTES, Duration.ofSeconds(-86400 - 3600 - 120, 0)},
+
+                {Duration.ofSeconds(-1, 0), SECONDS, Duration.ofSeconds(-1, 0)},
+                {Duration.ofSeconds(-1, 123_456_789), SECONDS, Duration.ofSeconds(0, 0)},
+                {Duration.ofSeconds(-1, 123_456_789), NANOS, Duration.ofSeconds(0, -876_543_211)},
+                {Duration.ofSeconds(0, 123_456_789), SECONDS, Duration.ofSeconds(0, 0)},
+                {Duration.ofSeconds(0, 123_456_789), NANOS, Duration.ofSeconds(0, 123_456_789)},
+        };
+    }
+
+    @Test(dataProvider="truncatedToValid")
+    public void test_truncatedTo_valid(Duration input, TemporalUnit unit, Duration expected) {
+        assertEquals(input.truncatedTo(unit), expected);
+    }
+
+    @DataProvider(name="truncatedToInvalid")
+    Object[][] data_truncatedToInvalid() {
+        return new Object[][] {
+                {Duration.ofSeconds(1, 123_456_789), NINETY_FIVE_MINS},
+                {Duration.ofSeconds(1, 123_456_789), WEEKS},
+                {Duration.ofSeconds(1, 123_456_789), MONTHS},
+                {Duration.ofSeconds(1, 123_456_789), YEARS},
+        };
+    }
+
+    @Test(dataProvider="truncatedToInvalid", expectedExceptions=DateTimeException.class)
+    public void test_truncatedTo_invalid(Duration input, TemporalUnit unit) {
+        input.truncatedTo(unit);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void test_truncatedTo_null() {
+        Duration.ofSeconds(1234).truncatedTo(null);
+    }
+
+    //-----------------------------------------------------------------------
     // dividedBy()
     //-----------------------------------------------------------------------
     @DataProvider(name="DividedBy")
@@ -2372,6 +2501,65 @@
     }
 
     //-----------------------------------------------------------------------
+    // dividedbyDur()
+    //-----------------------------------------------------------------------
+
+    @DataProvider(name="dividedByDur_provider")
+    Object[][] provider_dividedByDur() {
+        return new Object[][] {
+            {Duration.ofSeconds(0, 0), Duration.ofSeconds(1, 0), 0},
+            {Duration.ofSeconds(1, 0), Duration.ofSeconds(1, 0), 1},
+            {Duration.ofSeconds(6, 0), Duration.ofSeconds(3, 0), 2},
+            {Duration.ofSeconds(3, 0), Duration.ofSeconds(6, 0), 0},
+            {Duration.ofSeconds(7, 0), Duration.ofSeconds(3, 0), 2},
+
+            {Duration.ofSeconds(0, 333_333_333), Duration.ofSeconds(0, 333_333_333), 1},
+            {Duration.ofSeconds(0, 666_666_666), Duration.ofSeconds(0, 333_333_333), 2},
+            {Duration.ofSeconds(0, 333_333_333), Duration.ofSeconds(0, 666_666_666), 0},
+            {Duration.ofSeconds(0, 777_777_777), Duration.ofSeconds(0, 333_333_333), 2},
+
+            {Duration.ofSeconds(-7, 0), Duration.ofSeconds(3, 0), -2},
+            {Duration.ofSeconds(0, 7), Duration.ofSeconds(0, -3), -2},
+            {Duration.ofSeconds(0, -777_777_777), Duration.ofSeconds(0, 333_333_333), -2},
+
+            {Duration.ofSeconds(432000L, -777_777_777L), Duration.ofSeconds(14400L, 333_333_333L), 29},
+            {Duration.ofSeconds(-432000L, 777_777_777L), Duration.ofSeconds(14400L, 333_333_333L), -29},
+            {Duration.ofSeconds(-432000L, -777_777_777L), Duration.ofSeconds(14400L, 333_333_333L), -29},
+            {Duration.ofSeconds(-432000L, -777_777_777L), Duration.ofSeconds(14400L, -333_333_333L), -30},
+            {Duration.ofSeconds(432000L, -777_777_777L), Duration.ofSeconds(-14400L, 333_333_333L), -30},
+            {Duration.ofSeconds(432000L, -777_777_777L), Duration.ofSeconds(-14400L, -333_333_333L), -29},
+            {Duration.ofSeconds(-432000L, -777_777_777L), Duration.ofSeconds(-14400L, -333_333_333L), 29},
+
+            {Duration.ofSeconds(Long.MAX_VALUE, 0), Duration.ofSeconds(1, 0), Long.MAX_VALUE},
+            {Duration.ofSeconds(Long.MAX_VALUE, 0), Duration.ofSeconds(Long.MAX_VALUE, 0), 1},
+        };
+    }
+
+    @Test(dataProvider="dividedByDur_provider")
+    public void test_dividedByDur(Duration dividend, Duration divisor, long expected) {
+        assertEquals(dividend.dividedBy(divisor), expected);
+    }
+
+    @Test(expectedExceptions=ArithmeticException.class)
+    public void test_dividedByDur_zero() {
+       Duration t = Duration.ofSeconds(1, 0);
+       t.dividedBy(Duration.ZERO);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void test_dividedByDur_null() {
+       Duration t = Duration.ofSeconds(1, 0);
+       t.dividedBy(null);
+    }
+
+    @Test(expectedExceptions=ArithmeticException.class)
+    public void test_dividedByDur_overflow() {
+       Duration dur1 = Duration.ofSeconds(Long.MAX_VALUE, 0);
+       Duration dur2 = Duration.ofNanos(1);
+       dur1.dividedBy(dur2);
+    }
+
+    //-----------------------------------------------------------------------
     // negated()
     //-----------------------------------------------------------------------
     @Test
@@ -2454,6 +2642,147 @@
     }
 
     //-----------------------------------------------------------------------
+    // toSeconds()
+    //-----------------------------------------------------------------------
+    @DataProvider(name="toSeconds_provider")
+    Object[][] provider_toSeconds() {
+        return new Object[][] {
+            {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 31556926L},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -31556927L},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, 123_456_789), -31556926L},
+            {Duration.ofSeconds(0), 0L},
+            {Duration.ofSeconds(0, 123_456_789), 0L},
+            {Duration.ofSeconds(0, -123_456_789), -1L},
+            {Duration.ofSeconds(Long.MAX_VALUE), 9223372036854775807L},
+            {Duration.ofSeconds(Long.MIN_VALUE), -9223372036854775808L},
+        };
+    }
+
+    @Test(dataProvider="toSeconds_provider")
+    public void test_toSeconds(Duration dur, long seconds) {
+        assertEquals(dur.toSeconds(), seconds);
+    }
+
+    //-----------------------------------------------------------------------
+    // toDaysPart()
+    //-----------------------------------------------------------------------
+    @DataProvider(name="toDaysPart_provider")
+    Object[][] provider_toDaysPart() {
+        return new Object[][] {
+            {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 365L},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -365L},
+            {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 123_456_789), 0L},
+            {Duration.ofDays(365), 365L},
+            {Duration.ofHours(2), 0L},
+            {Duration.ofHours(-2), 0L},
+        };
+    }
+
+    @Test(dataProvider="toDaysPart_provider")
+    public void test_toDaysPart(Duration dur, long days) {
+        assertEquals(dur.toDaysPart(), days);
+    }
+
+    //-----------------------------------------------------------------------
+    // toHoursPart()
+    //-----------------------------------------------------------------------
+    @DataProvider(name="toHoursPart_provider")
+    Object[][] provider_toHoursPart() {
+        return new Object[][] {
+            {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 5},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -5},
+            {Duration.ofSeconds(48 * 60 + 46, 123_456_789), 0},
+            {Duration.ofHours(2), 2},
+            {Duration.ofHours(-2), -2},
+        };
+    }
+
+    @Test(dataProvider="toHoursPart_provider")
+    public void test_toHoursPart(Duration dur, int hours) {
+        assertEquals(dur.toHoursPart(), hours);
+    }
+
+    //-----------------------------------------------------------------------
+    // toMinutesPart()
+    //-----------------------------------------------------------------------
+    @DataProvider(name="toMinutesPart_provider")
+    Object[][] provider_toMinutesPart() {
+        return new Object[][] {
+            {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 48},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -48},
+            {Duration.ofSeconds(46, 123_456_789),0},
+            {Duration.ofMinutes(48), 48},
+            {Duration.ofHours(2), 0},
+            {Duration.ofHours(-2),0},
+        };
+    }
+
+    @Test(dataProvider="toMinutesPart_provider")
+    public void test_toMinutesPart(Duration dur, int minutes) {
+        assertEquals(dur.toMinutesPart(), minutes);
+    }
+
+    //-----------------------------------------------------------------------
+    // toSecondsPart()
+    //-----------------------------------------------------------------------
+    @DataProvider(name="toSecondsPart_provider")
+    Object[][] provider_toSecondsPart() {
+        return new Object[][] {
+            {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 46},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -47},
+            {Duration.ofSeconds(0, 123_456_789), 0},
+            {Duration.ofSeconds(46), 46},
+            {Duration.ofHours(2), 0},
+            {Duration.ofHours(-2), 0},
+        };
+    }
+
+    @Test(dataProvider="toSecondsPart_provider")
+    public void test_toSecondsPart(Duration dur, int seconds) {
+        assertEquals(dur.toSecondsPart(), seconds);
+    }
+
+    //-----------------------------------------------------------------------
+    // toMillisPart()
+    //-----------------------------------------------------------------------
+    @DataProvider(name="toMillisPart_provider")
+    Object[][] provider_toMillisPart() {
+        return new Object[][] {
+            {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 123},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), 876},
+            {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 0), 0},
+            {Duration.ofMillis(123), 123},
+            {Duration.ofHours(2), 0},
+            {Duration.ofHours(-2), 0},
+        };
+    }
+
+    @Test(dataProvider="toMillisPart_provider")
+    public void test_toMillisPart(Duration dur, int millis) {
+        assertEquals(dur.toMillisPart(), millis);
+    }
+
+    //-----------------------------------------------------------------------
+    // toNanosPart()
+    //-----------------------------------------------------------------------
+    @DataProvider(name="toNanosPart_provider")
+    Object[][] provider_toNanosPart() {
+        return new Object[][] {
+            {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 123_456_789},
+            {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), 876_543_211},
+            {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 0), 0},
+            {Duration.ofNanos(123_456_789), 123_456_789},
+            {Duration.ofHours(2), 0},
+            {Duration.ofHours(-2), 0},
+        };
+    }
+
+    @Test(dataProvider="toNanosPart_provider")
+    public void test_toNanosPart(Duration dur, int nanos) {
+        assertEquals(dur.toNanosPart(), nanos);
+    }
+
+    //-----------------------------------------------------------------------
     // compareTo()
     //-----------------------------------------------------------------------
     @Test
diff --git a/ojluni/src/test/java/time/tck/java/time/TCKLocalTime.java b/ojluni/src/test/java/time/tck/java/time/TCKLocalTime.java
index 9297510..f669a7c 100644
--- a/ojluni/src/test/java/time/tck/java/time/TCKLocalTime.java
+++ b/ojluni/src/test/java/time/tck/java/time/TCKLocalTime.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -102,6 +102,7 @@
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
 import java.time.Period;
+import java.time.Year;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
@@ -137,6 +138,7 @@
 public class TCKLocalTime extends AbstractDateTimeTest {
 
     private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2);
+    private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2);
     private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris");
 
     // Android-changed: This was originally non-static and initialized in @BeforeMethod,
@@ -422,6 +424,38 @@
         LocalTime.of(0, 0, 0, 1000000000);
     }
 
+     //-----------------------------------------------------------------------
+     // ofInstant()
+     //-----------------------------------------------------------------------
+     @DataProvider(name="instantFactory")
+     Object[][] data_instantFactory() {
+         return new Object[][] {
+                 {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalTime.of(2, 2, 4, 500)},
+                 {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalTime.of(23, 2, 4, 500)},
+                 {Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalTime.of(2, 0, 4, 500)},
+                 {OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(),
+                         ZoneOffset.UTC, LocalTime.MIN},
+                 {OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(),
+                         ZoneOffset.UTC, LocalTime.MAX},
+         };
+     }
+
+     @Test(dataProvider="instantFactory")
+     public void factory_ofInstant(Instant instant, ZoneId zone, LocalTime expected) {
+         LocalTime test = LocalTime.ofInstant(instant, zone);
+         assertEquals(test, expected);
+     }
+
+     @Test(expectedExceptions=NullPointerException.class)
+     public void factory_ofInstant_nullInstant() {
+         LocalTime.ofInstant((Instant) null, ZONE_PARIS);
+     }
+
+     @Test(expectedExceptions=NullPointerException.class)
+     public void factory_ofInstant_nullZone() {
+         LocalTime.ofInstant(Instant.EPOCH, (ZoneId) null);
+     }
+
     //-----------------------------------------------------------------------
     // ofSecondOfDay(long)
     //-----------------------------------------------------------------------
@@ -2401,6 +2435,32 @@
         }
     }
 
+    //-----------------------------------------------------------------------
+    // toEpochSecond()
+    //--------------------------------------------------------------------------
+    @DataProvider(name="epochSecond")
+    Object[][] provider__toEpochSecond() {
+        return new Object[][] {
+        {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), -7200L},
+        {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1965, 12, 31), OFFSET_PTWO), -126282600L},
+        {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1995, 5, 3), OFFSET_MTWO), 799507800L},
+        {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO),
+                Instant.ofEpochSecond(-7200).getEpochSecond()},
+        {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1969, 12, 31), OFFSET_MTWO),
+                Instant.ofEpochSecond(-37800L).getEpochSecond()},
+        {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO),
+                LocalDateTime.of(1970, 1, 1, 11, 30).toEpochSecond(OFFSET_PTWO)},
+        };
+    }
+
+    @Test(dataProvider="epochSecond")
+    public void test_toEpochSecond(long actual, long expected) {
+        assertEquals(actual, expected);
+    }
+
+    //-----------------------------------------------------------------------
+    // toSecondOfDay_fromNanoOfDay_symmetry()
+    //-----------------------------------------------------------------------
     @Test
     public void test_toSecondOfDay_fromNanoOfDay_symmetry() {
         LocalTime t = LocalTime.of(0, 0);
diff --git a/ojluni/src/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java b/ojluni/src/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java
index a13630a..326aed9 100644
--- a/ojluni/src/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java
+++ b/ojluni/src/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java
@@ -921,7 +921,9 @@
             // French Locale and ISO Chronology
             {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM y 'à' HH:mm:ss zzzz"},
             {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM y 'à' HH:mm:ss z"},
-            {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y 'à' HH:mm:ss"},
+            // Android-changed: Since ICU 68, medium format uses ',' instead of 'à', to separate the date and time in French.
+            // {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y 'à' HH:mm:ss"},
+            {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y, HH:mm:ss"},
             {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/y HH:mm"},
             {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM y"},
             {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM y"},
@@ -950,11 +952,15 @@
             {FormatStyle.FULL, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE zzzz ah:mm:ss"},
             {FormatStyle.LONG, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 z ah:mm:ss"},
             {FormatStyle.MEDIUM, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 ah:mm:ss"},
-            {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d ah:mm"},
+            // Android-changed: Since ICU 68, use single 'y' to represent year in short form like other format styles
+            // {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d ah:mm"},
+            {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy/M/d ah:mm"},
             {FormatStyle.FULL, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE"},
             {FormatStyle.LONG, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"},
             {FormatStyle.MEDIUM, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"},
-            {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d"},
+            // Android-changed: Since ICU 68, use single 'y' to represent year in short form like other format styles
+            // {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d"},
+            {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy/M/d"},
             {null, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "zzzz ah:mm:ss"},
             {null, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "z ah:mm:ss"},
             {null, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm:ss"},
diff --git a/ojluni/src/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java b/ojluni/src/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java
index bc0944f..619889b 100644
--- a/ojluni/src/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java
+++ b/ojluni/src/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java
@@ -46,7 +46,6 @@
 
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
-import android.icu.impl.ZoneMeta;
 
 /*
  * @test
@@ -91,7 +90,7 @@
                     }
                     // Android-changed (http://b/33197219): TimeZone.getDisplayName() for
                     // non-canonical time zones are not correct.
-                    if (!zid.equals(ZoneMeta.getCanonicalCLDRID(zid))) {
+                    if (!zid.equals(getSystemCanonicalID(zid))) {
                         continue;
                     }
                     zdt = zdt.withZoneSameLocal(ZoneId.of(zid));
@@ -110,6 +109,20 @@
         }
     }
 
+    // BEGIN Android-added: Get non-custom system canonical time zone Id from ICU.
+    private static String getSystemCanonicalID(String zid) {
+        if (android.icu.util.TimeZone.UNKNOWN_ZONE_ID.equals(zid)) {
+            return zid;
+        }
+        boolean[] isSystemID = { false };
+        String canonicalID = android.icu.util.TimeZone.getCanonicalID(zid, isSystemID);
+        if (canonicalID == null || !isSystemID[0]) {
+            return null;
+        }
+        return canonicalID;
+    }
+    // END Android-added: Get non-custom system canonical time zone Id from ICU.
+
     private void printText(Locale locale, ZonedDateTime zdt, TextStyle style, TimeZone zone, String expected) {
         String result = getFormatter(locale, style).format(zdt);
         // Android-changed: TimeZone.getDisplayName() will never return "GMT".
diff --git a/ojluni/src/test/java/util/Collection/SetFactories.java b/ojluni/src/test/java/util/Collection/SetFactories.java
new file mode 100644
index 0000000..653f419
--- /dev/null
+++ b/ojluni/src/test/java/util/Collection/SetFactories.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.util.Collection;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
+
+/*
+ * @test
+ * @bug 8048330
+ * @summary Test convenience static factory methods on Set.
+ * @run testng SetFactories
+ */
+
+@Test
+public class SetFactories {
+
+    Set<Integer> genSet() {
+        return new HashSet<>(Arrays.asList(1, 2, 3));
+    }
+
+    @Test
+    public void copyOfResultsEqual() {
+        Set<Integer> orig = genSet();
+        Set<Integer> copy = Set.copyOf(orig);
+
+        assertEquals(orig, copy);
+        assertEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfModifiedUnequal() {
+        Set<Integer> orig = genSet();
+        Set<Integer> copy = Set.copyOf(orig);
+        orig.add(4);
+
+        assertNotEquals(orig, copy);
+        assertNotEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfIdentity() {
+        Set<Integer> orig = genSet();
+        Set<Integer> copy1 = Set.copyOf(orig);
+        Set<Integer> copy2 = Set.copyOf(copy1);
+
+        assertNotSame(orig, copy1);
+        assertSame(copy1, copy2);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullCollection() {
+        Set<Integer> set = Set.copyOf(null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullElements() {
+        Set<Integer> set = Set.copyOf(Arrays.asList(1, null, 3));
+    }
+
+    @Test
+    public void copyOfAcceptsDuplicates() {
+        Set<Integer> set = Set.copyOf(Arrays.asList(1, 1, 2, 3, 3, 3));
+        assertEquals(set, Set.of(1, 2, 3));
+    }
+}
diff --git a/ojluni/src/test/java/util/List/ListFactories.java b/ojluni/src/test/java/util/List/ListFactories.java
new file mode 100644
index 0000000..3aee752
--- /dev/null
+++ b/ojluni/src/test/java/util/List/ListFactories.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.util.List;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static java.util.Arrays.asList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+/*
+ * @test
+ * @bug 8048330 8203184
+ * @summary Test convenience static factory methods on List.
+ * @run testng ListFactories
+ */
+@Test
+public class ListFactories {
+
+    static final int NUM_STRINGS = 20; // should be larger than the largest fixed-arg overload
+    static final String[] stringArray;
+    static {
+        String[] sa = new String[NUM_STRINGS];
+        for (int i = 0; i < NUM_STRINGS; i++) {
+            sa[i] = String.valueOf((char)('a' + i));
+        }
+        stringArray = sa;
+    }
+
+    // returns array of [actual, expected]
+    static Object[] a(List<String> act, List<String> exp) {
+        return new Object[] { act, exp };
+    }
+
+    @DataProvider(name="empty")
+    public Iterator<Object[]> empty() {
+        return Collections.singletonList(
+            a(List.of(), asList())
+        ).iterator();
+    }
+
+    @DataProvider(name="nonempty")
+    public Iterator<Object[]> nonempty() {
+        return asList(
+            a(List.of("a"),
+               asList("a")),
+            a(List.of("a", "b"),
+               asList("a", "b")),
+            a(List.of("a", "b", "c"),
+               asList("a", "b", "c")),
+            a(List.of("a", "b", "c", "d"),
+               asList("a", "b", "c", "d")),
+            a(List.of("a", "b", "c", "d", "e"),
+               asList("a", "b", "c", "d", "e")),
+            a(List.of("a", "b", "c", "d", "e", "f"),
+               asList("a", "b", "c", "d", "e", "f")),
+            a(List.of("a", "b", "c", "d", "e", "f", "g"),
+               asList("a", "b", "c", "d", "e", "f", "g")),
+            a(List.of("a", "b", "c", "d", "e", "f", "g", "h"),
+               asList("a", "b", "c", "d", "e", "f", "g", "h")),
+            a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i"),
+               asList("a", "b", "c", "d", "e", "f", "g", "h", "i")),
+            a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"),
+               asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")),
+            a(List.of(stringArray),
+               asList(stringArray))
+        ).iterator();
+    }
+
+    @DataProvider(name="sublists")
+    public Iterator<Object[]> sublists() {
+        return asList(
+            a(List.<String>of().subList(0,0),
+               asList()),
+            a(List.of("a").subList(0,0),
+               asList("a").subList(0,0)),
+            a(List.of("a", "b").subList(0,1),
+               asList("a", "b").subList(0,1)),
+            a(List.of("a", "b", "c").subList(1,3),
+               asList("a", "b", "c").subList(1,3)),
+            a(List.of("a", "b", "c", "d").subList(0,4),
+               asList("a", "b", "c", "d").subList(0,4)),
+            a(List.of("a", "b", "c", "d", "e").subList(0,3),
+               asList("a", "b", "c", "d", "e").subList(0,3)),
+            a(List.of("a", "b", "c", "d", "e", "f").subList(3, 5),
+               asList("a", "b", "c", "d", "e", "f").subList(3, 5)),
+            a(List.of("a", "b", "c", "d", "e", "f", "g").subList(0, 7),
+               asList("a", "b", "c", "d", "e", "f", "g").subList(0, 7)),
+            a(List.of("a", "b", "c", "d", "e", "f", "g", "h").subList(0, 0),
+               asList("a", "b", "c", "d", "e", "f", "g", "h").subList(0, 0)),
+            a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i").subList(4, 5),
+               asList("a", "b", "c", "d", "e", "f", "g", "h", "i").subList(4, 5)),
+            a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j").subList(1,10),
+               asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j").subList(1,10)),
+            a(List.of(stringArray).subList(5, NUM_STRINGS),
+               asList(Arrays.copyOfRange(stringArray, 5, NUM_STRINGS)))
+                ).iterator();
+    }
+
+    @DataProvider(name="all")
+    public Iterator<Object[]> all() {
+        List<Object[]> all = new ArrayList<>();
+        empty().forEachRemaining(all::add);
+        nonempty().forEachRemaining(all::add);
+        sublists().forEachRemaining(all::add);
+        return all.iterator();
+    }
+
+    @DataProvider(name="nonsublists")
+    public Iterator<Object[]> nonsublists() {
+        List<Object[]> all = new ArrayList<>();
+        empty().forEachRemaining(all::add);
+        nonempty().forEachRemaining(all::add);
+        return all.iterator();
+    }
+
+    @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class)
+    public void cannotAddLast(List<String> act, List<String> exp) {
+        act.add("x");
+    }
+
+    @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class)
+    public void cannotAddFirst(List<String> act, List<String> exp) {
+        act.add(0, "x");
+    }
+
+    @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class)
+    public void cannotRemove(List<String> act, List<String> exp) {
+        act.remove(0);
+    }
+
+    @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class)
+    public void cannotSet(List<String> act, List<String> exp) {
+        act.set(0, "x");
+    }
+
+    @Test(dataProvider="all")
+    public void contentsMatch(List<String> act, List<String> exp) {
+        assertEquals(act, exp);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed1() {
+        List.of((Object)null); // force one-arg overload
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed2a() {
+        List.of("a", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed2b() {
+        List.of(null, "b");
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed3() {
+        List.of("a", "b", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed4() {
+        List.of("a", "b", "c", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed5() {
+        List.of("a", "b", "c", "d", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed6() {
+        List.of("a", "b", "c", "d", "e", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed7() {
+        List.of("a", "b", "c", "d", "e", "f", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed8() {
+        List.of("a", "b", "c", "d", "e", "f", "g", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed9() {
+        List.of("a", "b", "c", "d", "e", "f", "g", "h", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowed10() {
+        List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullDisallowedN() {
+        String[] array = stringArray.clone();
+        array[0] = null;
+        List.of(array);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullArrayDisallowed() {
+        List.of((Object[])null);
+    }
+
+    @Test
+    public void ensureArrayCannotModifyList() {
+        String[] array = stringArray.clone();
+        List<String> list = List.of(array);
+        array[0] = "xyzzy";
+        assertEquals(list, Arrays.asList(stringArray));
+    }
+
+    @Test(dataProvider="all", expectedExceptions=NullPointerException.class)
+    public void containsNullShouldThrowNPE(List<String> act, List<String> exp) {
+        act.contains(null);
+    }
+
+    @Test(dataProvider="all", expectedExceptions=NullPointerException.class)
+    public void indexOfNullShouldThrowNPE(List<String> act, List<String> exp) {
+        act.indexOf(null);
+    }
+
+    @Test(dataProvider="all", expectedExceptions=NullPointerException.class)
+    public void lastIndexOfNullShouldThrowNPE(List<String> act, List<String> exp) {
+        act.lastIndexOf(null);
+    }
+
+    // List.of().subList views should not be Serializable
+    @Test(dataProvider="sublists")
+    public void isNotSerializable(List<String> act, List<String> exp) {
+        assertFalse(act instanceof Serializable);
+    }
+
+    // ... but List.of() should be
+    @Test(dataProvider="nonsublists")
+    public void serialEquality(List<String> act, List<String> exp) {
+        // assume that act.equals(exp) tested elsewhere
+        List<String> copy = serialClone(act);
+        assertEquals(act, copy);
+        assertEquals(copy, exp);
+    }
+
+    @SuppressWarnings("unchecked")
+    static <T> T serialClone(T obj) {
+        try {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+                oos.writeObject(obj);
+            }
+            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+            ObjectInputStream ois = new ObjectInputStream(bais);
+            return (T) ois.readObject();
+        } catch (IOException | ClassNotFoundException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+    List<Integer> genList() {
+        return new ArrayList<>(Arrays.asList(1, 2, 3));
+    }
+
+    @Test
+    public void copyOfResultsEqual() {
+        List<Integer> orig = genList();
+        List<Integer> copy = List.copyOf(orig);
+
+        assertEquals(orig, copy);
+        assertEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfModifiedUnequal() {
+        List<Integer> orig = genList();
+        List<Integer> copy = List.copyOf(orig);
+        orig.add(4);
+
+        assertNotEquals(orig, copy);
+        assertNotEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfIdentity() {
+        List<Integer> orig = genList();
+        List<Integer> copy1 = List.copyOf(orig);
+        List<Integer> copy2 = List.copyOf(copy1);
+
+        assertNotSame(orig, copy1);
+        assertSame(copy1, copy2);
+    }
+
+    @Test
+    public void copyOfSubList() {
+        List<Integer> orig = List.of(0, 1, 2, 3);
+        List<Integer> sub = orig.subList(0, 3);
+        List<Integer> copy = List.copyOf(sub);
+
+        assertNotSame(sub, copy);
+    }
+
+    @Test
+    public void copyOfSubSubList() {
+        List<Integer> orig = List.of(0, 1, 2, 3);
+        List<Integer> sub = orig.subList(0, 3).subList(0, 2);
+        List<Integer> copy = List.copyOf(sub);
+
+        assertNotSame(sub, copy);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullCollection() {
+        List<Integer> list = List.copyOf(null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullElements() {
+        List<Integer> list = List.copyOf(Arrays.asList(1, null, 3));
+    }
+
+    @Test
+    public void iteratorShouldNotBeListIterator() {
+        List<Integer> list = List.of(1, 2, 3, 4, 5);
+        Iterator<Integer> it = list.iterator();
+        it.next();
+        try {
+            ((ListIterator<Integer>) it).previous();
+            fail("ListIterator operation succeeded on Iterator");
+        } catch (ClassCastException|UnsupportedOperationException ignore) { }
+    }
+}
diff --git a/ojluni/src/test/java/util/Map/MapFactories.java b/ojluni/src/test/java/util/Map/MapFactories.java
new file mode 100644
index 0000000..7fe14e4
--- /dev/null
+++ b/ojluni/src/test/java/util/Map/MapFactories.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.util.Map;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
+
+/*
+ * @test
+ * @bug 8048330
+ * @summary Test convenience static factory methods on Map.
+ * @run testng MapFactories
+ */
+
+public class MapFactories {
+
+    Map<Integer, String> genMap() {
+        Map<Integer, String> map = new HashMap<>();
+        map.put(1, "a");
+        map.put(2, "b");
+        map.put(3, "c");
+        return map;
+    }
+
+    @Test
+    public void copyOfResultsEqual() {
+        Map<Integer, String> orig = genMap();
+        Map<Integer, String> copy = Map.copyOf(orig);
+
+        assertEquals(orig, copy);
+        assertEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfModifiedUnequal() {
+        Map<Integer, String> orig = genMap();
+        Map<Integer, String> copy = Map.copyOf(orig);
+        orig.put(4, "d");
+
+        assertNotEquals(orig, copy);
+        assertNotEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfIdentity() {
+        Map<Integer, String> orig = genMap();
+        Map<Integer, String> copy1 = Map.copyOf(orig);
+        Map<Integer, String> copy2 = Map.copyOf(copy1);
+
+        assertNotSame(orig, copy1);
+        assertSame(copy1, copy2);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullMap() {
+        Map<Integer, String> map = Map.copyOf(null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullKey() {
+        Map<Integer, String> map = genMap();
+        map.put(null, "x");
+        Map<Integer, String> copy = Map.copyOf(map);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullValue() {
+        Map<Integer, String> map = genMap();
+        map.put(-1, null);
+        Map<Integer, String> copy = Map.copyOf(map);
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java b/ojluni/src/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java
new file mode 100644
index 0000000..7074509
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AbstractExecutorServiceTest.java
@@ -0,0 +1,636 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractExecutorServiceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractExecutorServiceTest.class);
+    }
+
+    /**
+     * A no-frills implementation of AbstractExecutorService, designed
+     * to test the submit methods only.
+     */
+    static class DirectExecutorService extends AbstractExecutorService {
+        public void execute(Runnable r) { r.run(); }
+        public void shutdown() { shutdown = true; }
+        public List<Runnable> shutdownNow() {
+            shutdown = true;
+            return Collections.EMPTY_LIST;
+        }
+        public boolean isShutdown() { return shutdown; }
+        public boolean isTerminated() { return isShutdown(); }
+        public boolean awaitTermination(long timeout, TimeUnit unit) {
+            return isShutdown();
+        }
+        private volatile boolean shutdown = false;
+    }
+
+    /**
+     * execute(runnable) runs it to completion
+     */
+    public void testExecuteRunnable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        final AtomicBoolean done = new AtomicBoolean(false);
+        Future<?> future = e.submit(new CheckedRunnable() {
+            public void realRun() {
+                done.set(true);
+            }});
+        assertNull(future.get());
+        assertNull(future.get(0, MILLISECONDS));
+        assertTrue(done.get());
+        assertTrue(future.isDone());
+        assertFalse(future.isCancelled());
+    }
+
+    /**
+     * Completed submit(callable) returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<String> future = e.submit(new StringTask());
+        String result = future.get();
+        assertSame(TEST_STRING, result);
+    }
+
+    /**
+     * Completed submit(runnable) returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<?> future = e.submit(new NoOpRunnable());
+        future.get();
+        assertTrue(future.isDone());
+    }
+
+    /**
+     * Completed submit(runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        ExecutorService e = new DirectExecutorService();
+        Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+        String result = future.get();
+        assertSame(TEST_STRING, result);
+    }
+
+    /**
+     * A submitted privileged action runs to completion
+     */
+    public void testSubmitPrivilegedAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedAction() {
+                    public Object run() {
+                        return TEST_STRING;
+                    }}));
+
+                assertSame(TEST_STRING, future.get());
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"),
+                           new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted privileged exception action runs to completion
+     */
+    public void testSubmitPrivilegedExceptionAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() {
+                    public Object run() {
+                        return TEST_STRING;
+                    }}));
+
+                assertSame(TEST_STRING, future.get());
+            }};
+
+        runWithPermissions(r);
+    }
+
+    /**
+     * A submitted failed privileged exception action reports exception
+     */
+    public void testSubmitFailedPrivilegedExceptionAction() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                ExecutorService e = new DirectExecutorService();
+                Future future = e.submit(Executors.callable(new PrivilegedExceptionAction() {
+                    public Object run() throws Exception {
+                        throw new IndexOutOfBoundsException();
+                    }}));
+
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    assertTrue(success.getCause() instanceof IndexOutOfBoundsException);
+                }}};
+
+        runWithPermissions(r);
+    }
+
+    /**
+     * execute(null runnable) throws NPE
+     */
+    public void testExecuteNullRunnable() {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.submit((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * submit(null callable) throws NPE
+     */
+    public void testSubmitNullCallable() {
+        ExecutorService e = new DirectExecutorService();
+        try {
+            e.submit((Callable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * submit(callable).get() throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch submitted    = new CountDownLatch(1);
+        final CountDownLatch quittingTime = new CountDownLatch(1);
+        final Callable<Void> awaiter = new CheckedCallable<Void>() {
+            public Void realCall() throws InterruptedException {
+                assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS));
+                return null;
+            }};
+        final ExecutorService p
+            = new ThreadPoolExecutor(1,1,60, TimeUnit.SECONDS,
+                                     new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, quittingTime)) {
+            Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Future<Void> future = p.submit(awaiter);
+                    submitted.countDown();
+                    future.get();
+                }});
+
+            await(submitted);
+            t.interrupt();
+            awaitTermination(t);
+        }
+    }
+
+    /**
+     * get of submit(callable) throws ExecutionException if callable
+     * throws exception
+     */
+    public void testSubmitEE() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   60, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            Callable c = new Callable() {
+                public Object call() { throw new ArithmeticException(); }};
+            try {
+                p.submit(c).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof ArithmeticException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<Long>> l = new ArrayList<>();
+            l.add(new Callable<Long>() {
+                      public Long call() { throw new ArithmeticException(); }});
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task in c completes
+     */
+    public void testInvokeAny4() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task in c if at least one completes
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(null time unit) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<Long>> l = new ArrayList<>();
+            l.add(new Callable<Long>() {
+                      public Long call() { throw new ArithmeticException(); }});
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task in c
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(null time unit) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws InterruptedException {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        final ExecutorService e = new DirectExecutorService();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            for (long timeout = timeoutMillis();;) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(Executors.callable(possiblyInterruptedRunnable(timeout),
+                                             TEST_STRING));
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    e.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals(TEST_STRING, futures.get(1).get());
+                } catch (CancellationException retryWithLongerTimeout) {
+                    // unusual delay before starting second task
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                    continue;
+                }
+                assertTrue(futures.get(2).isCancelled());
+                break;
+            }
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AbstractQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/AbstractQueueTest.java
new file mode 100644
index 0000000..c7bb03c
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AbstractQueueTest.java
@@ -0,0 +1,206 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.AbstractQueue;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractQueueTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractQueueTest.class);
+    }
+
+    static class Succeed extends AbstractQueue<Integer> {
+        public boolean offer(Integer x) {
+            if (x == null) throw new NullPointerException();
+            return true;
+        }
+        public Integer peek() { return one; }
+        public Integer poll() { return one; }
+        public int size() { return 0; }
+        public Iterator iterator() { return null; } // not needed
+    }
+
+    static class Fail extends AbstractQueue<Integer> {
+        public boolean offer(Integer x) {
+            if (x == null) throw new NullPointerException();
+            return false;
+        }
+        public Integer peek() { return null; }
+        public Integer poll() { return null; }
+        public int size() { return 0; }
+        public Iterator iterator() { return null; } // not needed
+    }
+
+    /**
+     * add returns true if offer succeeds
+     */
+    public void testAddS() {
+        Succeed q = new Succeed();
+        assertTrue(q.add(two));
+    }
+
+    /**
+     * add throws ISE true if offer fails
+     */
+    public void testAddF() {
+        Fail q = new Fail();
+        try {
+            q.add(one);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * add throws NPE if offer does
+     */
+    public void testAddNPE() {
+        Succeed q = new Succeed();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove returns normally if poll succeeds
+     */
+    public void testRemoveS() {
+        Succeed q = new Succeed();
+        q.remove();
+    }
+
+    /**
+     * remove throws NSEE if poll returns null
+     */
+    public void testRemoveF() {
+        Fail q = new Fail();
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * element returns normally if peek succeeds
+     */
+    public void testElementS() {
+        Succeed q = new Succeed();
+        q.element();
+    }
+
+    /**
+     * element throws NSEE if peek returns null
+     */
+    public void testElementF() {
+        Fail q = new Fail();
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        Succeed q = new Succeed();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        Succeed q = new Succeed();
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        Succeed q = new Succeed();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        Succeed q = new Succeed();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws ISE if an add fails
+     */
+    public void testAddAll4() {
+        Fail q = new Fail();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java b/ojluni/src/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java
new file mode 100644
index 0000000..438ea56
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AbstractQueuedLongSynchronizerTest.java
@@ -0,0 +1,1286 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
+import java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractQueuedLongSynchronizerTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractQueuedLongSynchronizerTest.class);
+    }
+
+    /**
+     * A simple mutex class, adapted from the class javadoc.  Exclusive
+     * acquire tests exercise this as a sample user extension.
+     */
+    static class Mutex extends AbstractQueuedLongSynchronizer {
+        /** An eccentric value > 32 bits for locked synchronizer state. */
+        static final long LOCKED = (1L << 63) | (1L << 15);
+
+        static final long UNLOCKED = 0;
+
+        public boolean isHeldExclusively() {
+            long state = getState();
+            assertTrue(state == UNLOCKED || state == LOCKED);
+            return state == LOCKED;
+        }
+
+        public boolean tryAcquire(long acquires) {
+            assertEquals(LOCKED, acquires);
+            return compareAndSetState(UNLOCKED, LOCKED);
+        }
+
+        public boolean tryRelease(long releases) {
+            if (getState() != LOCKED) throw new IllegalMonitorStateException();
+            setState(UNLOCKED);
+            return true;
+        }
+
+        public boolean tryAcquireNanos(long nanos) throws InterruptedException {
+            return tryAcquireNanos(LOCKED, nanos);
+        }
+
+        public boolean tryAcquire() {
+            return tryAcquire(LOCKED);
+        }
+
+        public boolean tryRelease() {
+            return tryRelease(LOCKED);
+        }
+
+        public void acquire() {
+            acquire(LOCKED);
+        }
+
+        public void acquireInterruptibly() throws InterruptedException {
+            acquireInterruptibly(LOCKED);
+        }
+
+        public void release() {
+            release(LOCKED);
+        }
+
+        public ConditionObject newCondition() {
+            return new ConditionObject();
+        }
+    }
+
+    /**
+     * A simple latch class, to test shared mode.
+     */
+    static class BooleanLatch extends AbstractQueuedLongSynchronizer {
+        public boolean isSignalled() { return getState() != 0; }
+
+        public long tryAcquireShared(long ignore) {
+            return isSignalled() ? 1 : -1;
+        }
+
+        public boolean tryReleaseShared(long ignore) {
+            setState(1L << 62);
+            return true;
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that does not expect to
+     * be interrupted.
+     */
+    class InterruptibleSyncRunnable extends CheckedRunnable {
+        final Mutex sync;
+        InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that expects to be
+     * interrupted.
+     */
+    class InterruptedSyncRunnable extends CheckedInterruptedRunnable {
+        final Mutex sync;
+        InterruptedSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /** A constant to clarify calls to checking methods below. */
+    static final Thread[] NO_THREADS = new Thread[0];
+
+    /**
+     * Spin-waits until sync.isQueued(t) becomes true.
+     */
+    void waitForQueuedThread(AbstractQueuedLongSynchronizer sync,
+                             Thread t) {
+        long startTime = System.nanoTime();
+        while (!sync.isQueued(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Checks that sync has exactly the given queued threads.
+     */
+    void assertHasQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                Thread... expected) {
+        Collection<Thread> actual = sync.getQueuedThreads();
+        assertEquals(expected.length > 0, sync.hasQueuedThreads());
+        assertEquals(expected.length, sync.getQueueLength());
+        assertEquals(expected.length, actual.size());
+        assertEquals(expected.length == 0, actual.isEmpty());
+        assertEquals(new HashSet<Thread>(actual),
+                     new HashSet<Thread>(Arrays.asList(expected)));
+        // Android-added: Since we check queued threads, also check queued predecessors
+        final boolean expectedQueuedPredecessors =
+            sync.getFirstQueuedThread() != Thread.currentThread() &&
+            sync.hasQueuedThreads();
+        assertEquals(expectedQueuedPredecessors, sync.hasQueuedPredecessors());
+    }
+
+    /**
+     * Checks that sync has exactly the given (exclusive) queued threads.
+     */
+    void assertHasExclusiveQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                         Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getExclusiveQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getSharedQueuedThreads().size());
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that sync has exactly the given (shared) queued threads.
+     */
+    void assertHasSharedQueuedThreads(AbstractQueuedLongSynchronizer sync,
+                                      Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getSharedQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getExclusiveQueuedThreads().size());
+        assertTrue(sync.getExclusiveQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads,
+     * after acquiring mutex.
+     */
+    void assertHasWaitersUnlocked(Mutex sync, ConditionObject c,
+                                 Thread... threads) {
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, threads);
+        sync.release();
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaitersLocked(Mutex sync, ConditionObject c,
+                                Thread... threads) {
+        assertEquals(threads.length > 0, sync.hasWaiters(c));
+        assertEquals(threads.length, sync.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, sync.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(sync.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(ConditionObject c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(nanosTimeout);
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Checks that awaiting the given condition times out (using the
+     * default timeout duration).
+     */
+    void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
+        final long timeoutMillis = timeoutMillis();
+        final long startTime;
+        try {
+            switch (awaitMethod) {
+            case awaitTimed:
+                startTime = System.nanoTime();
+                assertFalse(c.await(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitNanos:
+                startTime = System.nanoTime();
+                long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+                long nanosRemaining = c.awaitNanos(nanosTimeout);
+                assertTrue(nanosRemaining <= 0);
+                assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitUntil:
+                // We shouldn't assume that nanoTime and currentTimeMillis
+                // use the same time source, so don't use nanoTime here.
+                java.util.Date delayedDate = delayedDate(timeoutMillis);
+                assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+                assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+                break;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        } catch (InterruptedException ie) { threadUnexpectedException(ie); }
+    }
+
+    /**
+     * isHeldExclusively is false upon construction
+     */
+    public void testIsHeldExclusively() {
+        Mutex sync = new Mutex();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquiring released sync succeeds
+     */
+    public void testAcquire() {
+        Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * tryAcquire on a released sync succeeds
+     */
+    public void testTryAcquire() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.tryAcquire());
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasQueuedThreads());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasQueuedThreads());
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.hasQueuedThreads());
+    }
+
+    /**
+     * isQueued(null) throws NullPointerException
+     */
+    public void testIsQueuedNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.isQueued(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isQueued reports whether a thread is queued
+     */
+    public void testIsQueued() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        sync.acquire();
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+    }
+
+    /**
+     * getFirstQueuedThread returns first waiting thread or null if none
+     */
+    public void testGetFirstQueuedThread() {
+        final Mutex sync = new Mutex();
+        assertNull(sync.getFirstQueuedThread());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(t2, sync.getFirstQueuedThread());
+        sync.release();
+        awaitTermination(t2);
+        assertNull(sync.getFirstQueuedThread());
+    }
+
+    /**
+     * hasContended reports false if no thread has ever blocked, else true
+     */
+    public void testHasContended() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasContended());
+        sync.acquire();
+        assertFalse(sync.hasContended());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasContended());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasContended());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasContended());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.hasContended());
+    }
+
+    /**
+     * getQueuedThreads returns all waiting threads
+     */
+    public void testGetQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertFalse(sync.getQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertTrue(sync.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getExclusiveQueuedThreads returns all exclusive waiting threads
+     */
+    public void testGetExclusiveQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertFalse(sync.getExclusiveQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getSharedQueuedThreads does not include exclusively waiting threads
+     */
+    public void testGetSharedQueuedThreads_Exclusive() {
+        final Mutex sync = new Mutex();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.acquire();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * getSharedQueuedThreads returns all shared waiting threads
+     */
+    public void testGetSharedQueuedThreads_Shared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t1);
+        assertHasSharedQueuedThreads(l, t1);
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t2);
+        assertHasSharedQueuedThreads(l, t1, t2);
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasSharedQueuedThreads(l, t2);
+        assertTrue(l.releaseShared(0));
+        awaitTermination(t2);
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+    }
+
+    /**
+     * tryAcquireNanos is interruptible
+     */
+    public void testTryAcquireNanos_Interruptible() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
+            }});
+
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquire on exclusively held sync fails
+     */
+    public void testTryAcquireWhenSynced() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(sync.tryAcquire());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * tryAcquireNanos on an exclusively held sync times out
+     */
+    public void testAcquireNanos_Timeout() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long nanos = MILLISECONDS.toNanos(timeoutMillis());
+                assertFalse(sync.tryAcquireNanos(nanos));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * getState is true when acquired and false when not
+     */
+    public void testGetState() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+
+        final BooleanLatch acquired = new BooleanLatch();
+        final BooleanLatch done = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                done.acquireShared(0);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        assertTrue(sync.isHeldExclusively());
+        assertTrue(done.releaseShared(0));
+        awaitTermination(t);
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquireInterruptibly succeeds when released, else is interruptible
+     */
+    public void testAcquireInterruptibly() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final BooleanLatch threadStarted = new BooleanLatch();
+        sync.acquireInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(threadStarted.releaseShared(0));
+                sync.acquireInterruptibly();
+            }});
+
+        threadStarted.acquireShared(0);
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertTrue(sync.isHeldExclusively());
+    }
+
+    /**
+     * owns is true for a condition created by sync else false
+     */
+    public void testOwns() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        assertTrue(sync.owns(c));
+        assertFalse(sync2.owns(c));
+    }
+
+    /**
+     * Calling await without holding sync throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * Calling signalAll without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil without a signal times out
+     */
+    public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); }
+    public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); }
+    public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); }
+    public void testAwait_Timeout(AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertAwaitTimesOut(c, awaitMethod);
+        sync.release();
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil returns when signalled
+     */
+    public void testSignal_await()      { testSignal(AwaitMethod.await); }
+    public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); }
+    public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); }
+    public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); }
+    public void testSignal(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters(null) throws NullPointerException
+     */
+    public void testHasWaitersNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength(null) throws NullPointerException
+     */
+    public void testGetWaitQueueLengthNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not synced
+     */
+    public void testHasWaitersIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitQueueLengthIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitingThreadsIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertFalse(sync.hasWaiters(c));
+                assertTrue(acquired.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.hasWaiters(c));
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertFalse(sync.hasWaiters(c));
+        sync.release();
+
+        awaitTermination(t);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertEquals(0, sync.getWaitQueueLength(c));
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertEquals(1, sync.getWaitQueueLength(c));
+        sync.release();
+
+        final Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertEquals(1, sync.getWaitQueueLength(c));
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertEquals(2, sync.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertEquals(0, sync.getWaitQueueLength(c));
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertTrue(sync.getWaitingThreads(c).isEmpty());
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        final Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertTrue(sync.getWaitingThreads(c).contains(t1));
+                assertFalse(sync.getWaitingThreads(c).isEmpty());
+                assertEquals(1, sync.getWaitingThreads(c).size());
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t1.start();
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(1, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t2.start();
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertTrue(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(2, sync.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                sync.release();
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        sync.release();
+        t.interrupt();
+        assertHasWaitersUnlocked(sync, c, t);
+        assertThreadStaysAlive(t);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()      { testInterruptible(AwaitMethod.await); }
+    public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); }
+    public void testInterruptible(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                await(c, awaitMethod);
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()      { testSignalAll(AwaitMethod.await); }
+    public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); }
+    public void testSignalAll(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired1.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired2.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired1.acquireShared(0);
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        sync.release();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current state
+     */
+    public void testToString() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED));
+        sync.acquire();
+        assertTrue(sync.toString().contains("State = " + Mutex.LOCKED));
+    }
+
+    /**
+     * A serialized AQS deserializes with current state, but no queued threads
+     */
+    public void testSerialization() {
+        Mutex sync = new Mutex();
+        assertFalse(serialClone(sync).isHeldExclusively());
+        sync.acquire();
+        Thread t = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t);
+        assertTrue(sync.isHeldExclusively());
+
+        Mutex clone = serialClone(sync);
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+        t.interrupt();
+        awaitTermination(t);
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+    }
+
+    /**
+     * tryReleaseShared setting state changes getState
+     */
+    public void testGetStateWithReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * releaseShared has no effect when already signalled
+     */
+    public void testReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * acquireSharedInterruptibly returns after release, but not before
+     */
+    public void testAcquireSharedInterruptibly() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertHasSharedQueuedThreads(l, t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquireSharedNanos returns after release, but not before
+     */
+    public void testTryAcquireSharedNanos() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireSharedInterruptibly is interruptible
+     */
+    public void testAcquireSharedInterruptibly_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos is interruptible
+     */
+    public void testTryAcquireSharedNanos_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                l.tryAcquireSharedNanos(0, nanos);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos times out if not released before timeout
+     */
+    public void testTryAcquireSharedNanos_Timeout() {
+        final BooleanLatch l = new BooleanLatch();
+        final BooleanLatch observedQueued = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                for (long millis = timeoutMillis();
+                     !observedQueued.isSignalled();
+                     millis *= 2) {
+                    long nanos = MILLISECONDS.toNanos(millis);
+                    long startTime = System.nanoTime();
+                    assertFalse(l.tryAcquireSharedNanos(0, nanos));
+                    assertTrue(millisElapsedSince(startTime) >= millis);
+                }
+                assertFalse(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        observedQueued.releaseShared(0);
+        assertFalse(l.isSignalled());
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * awaitNanos/timed await with 0 wait times out immediately
+     */
+    public void testAwait_Zero() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(0L) <= 0);
+        assertFalse(c.await(0L, NANOSECONDS));
+        sync.release();
+    }
+
+    /**
+     * awaitNanos/timed await with maximum negative wait times does not underflow
+     */
+    public void testAwait_NegativeInfinity() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(Long.MIN_VALUE) <= 0);
+        assertFalse(c.await(Long.MIN_VALUE, NANOSECONDS));
+        sync.release();
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java b/ojluni/src/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java
new file mode 100644
index 0000000..a1f36ba
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java
@@ -0,0 +1,1289 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AbstractQueuedSynchronizerTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AbstractQueuedSynchronizerTest.class);
+    }
+
+    /**
+     * A simple mutex class, adapted from the class javadoc.  Exclusive
+     * acquire tests exercise this as a sample user extension.  Other
+     * methods/features of AbstractQueuedSynchronizer are tested via
+     * other test classes, including those for ReentrantLock,
+     * ReentrantReadWriteLock, and Semaphore.
+     */
+    static class Mutex extends AbstractQueuedSynchronizer {
+        /** An eccentric value for locked synchronizer state. */
+        static final int LOCKED = (1 << 31) | (1 << 15);
+
+        static final int UNLOCKED = 0;
+
+        @Override public boolean isHeldExclusively() {
+            int state = getState();
+            assertTrue(state == UNLOCKED || state == LOCKED);
+            return state == LOCKED;
+        }
+
+        @Override public boolean tryAcquire(int acquires) {
+            assertEquals(LOCKED, acquires);
+            return compareAndSetState(UNLOCKED, LOCKED);
+        }
+
+        @Override public boolean tryRelease(int releases) {
+            if (getState() != LOCKED) throw new IllegalMonitorStateException();
+            assertEquals(LOCKED, releases);
+            setState(UNLOCKED);
+            return true;
+        }
+
+        public boolean tryAcquireNanos(long nanos) throws InterruptedException {
+            return tryAcquireNanos(LOCKED, nanos);
+        }
+
+        public boolean tryAcquire() {
+            return tryAcquire(LOCKED);
+        }
+
+        public boolean tryRelease() {
+            return tryRelease(LOCKED);
+        }
+
+        public void acquire() {
+            acquire(LOCKED);
+        }
+
+        public void acquireInterruptibly() throws InterruptedException {
+            acquireInterruptibly(LOCKED);
+        }
+
+        public void release() {
+            release(LOCKED);
+        }
+
+        public ConditionObject newCondition() {
+            return new ConditionObject();
+        }
+    }
+
+    /**
+     * A simple latch class, to test shared mode.
+     */
+    static class BooleanLatch extends AbstractQueuedSynchronizer {
+        public boolean isSignalled() { return getState() != 0; }
+
+        public int tryAcquireShared(int ignore) {
+            return isSignalled() ? 1 : -1;
+        }
+
+        public boolean tryReleaseShared(int ignore) {
+            setState(1);
+            return true;
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that does not expect to
+     * be interrupted.
+     */
+    class InterruptibleSyncRunnable extends CheckedRunnable {
+        final Mutex sync;
+        InterruptibleSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling acquireInterruptibly that expects to be
+     * interrupted.
+     */
+    class InterruptedSyncRunnable extends CheckedInterruptedRunnable {
+        final Mutex sync;
+        InterruptedSyncRunnable(Mutex sync) { this.sync = sync; }
+        public void realRun() throws InterruptedException {
+            sync.acquireInterruptibly();
+        }
+    }
+
+    /** A constant to clarify calls to checking methods below. */
+    static final Thread[] NO_THREADS = new Thread[0];
+
+    /**
+     * Spin-waits until sync.isQueued(t) becomes true.
+     */
+    void waitForQueuedThread(AbstractQueuedSynchronizer sync, Thread t) {
+        long startTime = System.nanoTime();
+        while (!sync.isQueued(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Checks that sync has exactly the given queued threads.
+     */
+    void assertHasQueuedThreads(AbstractQueuedSynchronizer sync,
+                                Thread... expected) {
+        Collection<Thread> actual = sync.getQueuedThreads();
+        assertEquals(expected.length > 0, sync.hasQueuedThreads());
+        assertEquals(expected.length, sync.getQueueLength());
+        assertEquals(expected.length, actual.size());
+        assertEquals(expected.length == 0, actual.isEmpty());
+        assertEquals(new HashSet<Thread>(actual),
+                     new HashSet<Thread>(Arrays.asList(expected)));
+        // Android-added: Since we check queued threads, also check queued predecessors
+        final boolean expectedQueuedPredecessors =
+            sync.getFirstQueuedThread() != Thread.currentThread() &&
+            sync.hasQueuedThreads();
+        assertEquals(expectedQueuedPredecessors, sync.hasQueuedPredecessors());
+    }
+
+    /**
+     * Checks that sync has exactly the given (exclusive) queued threads.
+     */
+    void assertHasExclusiveQueuedThreads(AbstractQueuedSynchronizer sync,
+                                         Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getExclusiveQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getSharedQueuedThreads().size());
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that sync has exactly the given (shared) queued threads.
+     */
+    void assertHasSharedQueuedThreads(AbstractQueuedSynchronizer sync,
+                                      Thread... expected) {
+        assertHasQueuedThreads(sync, expected);
+        assertEquals(new HashSet<Thread>(sync.getSharedQueuedThreads()),
+                     new HashSet<Thread>(sync.getQueuedThreads()));
+        assertEquals(0, sync.getExclusiveQueuedThreads().size());
+        assertTrue(sync.getExclusiveQueuedThreads().isEmpty());
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads,
+     * after acquiring mutex.
+     */
+    void assertHasWaitersUnlocked(Mutex sync, ConditionObject c,
+                                 Thread... threads) {
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, threads);
+        sync.release();
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaitersLocked(Mutex sync, ConditionObject c,
+                                Thread... threads) {
+        assertEquals(threads.length > 0, sync.hasWaiters(c));
+        assertEquals(threads.length, sync.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, sync.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, sync.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(sync.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition using the specified AwaitMethod.
+     */
+    void await(ConditionObject c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(nanosTimeout);
+            assertTrue(nanosRemaining > 0);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Checks that awaiting the given condition times out (using the
+     * default timeout duration).
+     */
+    void assertAwaitTimesOut(ConditionObject c, AwaitMethod awaitMethod) {
+        final long timeoutMillis = timeoutMillis();
+        final long startTime;
+        try {
+            switch (awaitMethod) {
+            case awaitTimed:
+                startTime = System.nanoTime();
+                assertFalse(c.await(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitNanos:
+                startTime = System.nanoTime();
+                long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis);
+                long nanosRemaining = c.awaitNanos(nanosTimeout);
+                assertTrue(nanosRemaining <= 0);
+                assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+                break;
+            case awaitUntil:
+                // We shouldn't assume that nanoTime and currentTimeMillis
+                // use the same time source, so don't use nanoTime here.
+                java.util.Date delayedDate = delayedDate(timeoutMillis);
+                assertFalse(c.awaitUntil(delayedDate(timeoutMillis)));
+                assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+                break;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        } catch (InterruptedException ie) { threadUnexpectedException(ie); }
+    }
+
+    /**
+     * isHeldExclusively is false upon construction
+     */
+    public void testIsHeldExclusively() {
+        Mutex sync = new Mutex();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquiring released sync succeeds
+     */
+    public void testAcquire() {
+        Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * tryAcquire on a released sync succeeds
+     */
+    public void testTryAcquire() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.tryAcquire());
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasQueuedThreads());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasQueuedThreads());
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.hasQueuedThreads());
+    }
+
+    /**
+     * isQueued(null) throws NullPointerException
+     */
+    public void testIsQueuedNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.isQueued(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isQueued reports whether a thread is queued
+     */
+    public void testIsQueued() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        sync.acquire();
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(sync.isQueued(t1));
+        assertTrue(sync.isQueued(t2));
+        sync.release();
+        awaitTermination(t2);
+        assertFalse(sync.isQueued(t1));
+        assertFalse(sync.isQueued(t2));
+    }
+
+    /**
+     * getFirstQueuedThread returns first waiting thread or null if none
+     */
+    public void testGetFirstQueuedThread() {
+        final Mutex sync = new Mutex();
+        assertNull(sync.getFirstQueuedThread());
+        sync.acquire();
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertEquals(t1, sync.getFirstQueuedThread());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(t2, sync.getFirstQueuedThread());
+        sync.release();
+        awaitTermination(t2);
+        assertNull(sync.getFirstQueuedThread());
+    }
+
+    /**
+     * hasContended reports false if no thread has ever blocked, else true
+     */
+    public void testHasContended() {
+        final Mutex sync = new Mutex();
+        assertFalse(sync.hasContended());
+        sync.acquire();
+        assertFalse(sync.hasContended());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.hasContended());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.hasContended());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.hasContended());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.hasContended());
+    }
+
+    /**
+     * getQueuedThreads returns all waiting threads
+     */
+    public void testGetQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertFalse(sync.getQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getQueuedThreads().contains(t1));
+        assertTrue(sync.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getExclusiveQueuedThreads returns all exclusive waiting threads
+     */
+    public void testGetExclusiveQueuedThreads() {
+        final Mutex sync = new Mutex();
+        Thread t1 = new Thread(new InterruptedSyncRunnable(sync));
+        Thread t2 = new Thread(new InterruptibleSyncRunnable(sync));
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        sync.acquire();
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        t1.start();
+        waitForQueuedThread(sync, t1);
+        assertHasExclusiveQueuedThreads(sync, t1);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertFalse(sync.getExclusiveQueuedThreads().contains(t2));
+        t2.start();
+        waitForQueuedThread(sync, t2);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t1));
+        assertTrue(sync.getExclusiveQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasExclusiveQueuedThreads(sync, t2);
+        sync.release();
+        awaitTermination(t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+    }
+
+    /**
+     * getSharedQueuedThreads does not include exclusively waiting threads
+     */
+    public void testGetSharedQueuedThreads_Exclusive() {
+        final Mutex sync = new Mutex();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.acquire();
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        Thread t2 = newStartedThread(new InterruptibleSyncRunnable(sync));
+        waitForQueuedThread(sync, t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+        sync.release();
+        awaitTermination(t2);
+        assertTrue(sync.getSharedQueuedThreads().isEmpty());
+    }
+
+    /**
+     * getSharedQueuedThreads returns all shared waiting threads
+     */
+    public void testGetSharedQueuedThreads_Shared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t1);
+        assertHasSharedQueuedThreads(l, t1);
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                l.acquireSharedInterruptibly(0);
+            }});
+        waitForQueuedThread(l, t2);
+        assertHasSharedQueuedThreads(l, t1, t2);
+        t1.interrupt();
+        awaitTermination(t1);
+        assertHasSharedQueuedThreads(l, t2);
+        assertTrue(l.releaseShared(0));
+        awaitTermination(t2);
+        assertHasSharedQueuedThreads(l, NO_THREADS);
+    }
+
+    /**
+     * tryAcquireNanos is interruptible
+     */
+    public void testTryAcquireNanos_Interruptible() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.tryAcquireNanos(MILLISECONDS.toNanos(2 * LONG_DELAY_MS));
+            }});
+
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquire on exclusively held sync fails
+     */
+    public void testTryAcquireWhenSynced() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(sync.tryAcquire());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * tryAcquireNanos on an exclusively held sync times out
+     */
+    public void testAcquireNanos_Timeout() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long nanos = MILLISECONDS.toNanos(timeoutMillis());
+                assertFalse(sync.tryAcquireNanos(nanos));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+        sync.release();
+    }
+
+    /**
+     * getState is true when acquired and false when not
+     */
+    public void testGetState() {
+        final Mutex sync = new Mutex();
+        sync.acquire();
+        assertTrue(sync.isHeldExclusively());
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+
+        final BooleanLatch acquired = new BooleanLatch();
+        final BooleanLatch done = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                done.acquireShared(0);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        assertTrue(sync.isHeldExclusively());
+        assertTrue(done.releaseShared(0));
+        awaitTermination(t);
+        assertFalse(sync.isHeldExclusively());
+    }
+
+    /**
+     * acquireInterruptibly succeeds when released, else is interruptible
+     */
+    public void testAcquireInterruptibly() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final BooleanLatch threadStarted = new BooleanLatch();
+        sync.acquireInterruptibly();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(threadStarted.releaseShared(0));
+                sync.acquireInterruptibly();
+            }});
+
+        threadStarted.acquireShared(0);
+        waitForQueuedThread(sync, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertTrue(sync.isHeldExclusively());
+    }
+
+    /**
+     * owns is true for a condition created by sync else false
+     */
+    public void testOwns() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        assertTrue(sync.owns(c));
+        assertFalse(sync2.owns(c));
+    }
+
+    /**
+     * Calling await without holding sync throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * Calling signalAll without holding sync throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil without a signal times out
+     */
+    public void testAwaitTimed_Timeout() { testAwait_Timeout(AwaitMethod.awaitTimed); }
+    public void testAwaitNanos_Timeout() { testAwait_Timeout(AwaitMethod.awaitNanos); }
+    public void testAwaitUntil_Timeout() { testAwait_Timeout(AwaitMethod.awaitUntil); }
+    public void testAwait_Timeout(AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertAwaitTimesOut(c, awaitMethod);
+        sync.release();
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil returns when signalled
+     */
+    public void testSignal_await()      { testSignal(AwaitMethod.await); }
+    public void testSignal_awaitTimed() { testSignal(AwaitMethod.awaitTimed); }
+    public void testSignal_awaitNanos() { testSignal(AwaitMethod.awaitNanos); }
+    public void testSignal_awaitUntil() { testSignal(AwaitMethod.awaitUntil); }
+    public void testSignal(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(acquired.releaseShared(0));
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters(null) throws NullPointerException
+     */
+    public void testHasWaitersNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength(null) throws NullPointerException
+     */
+    public void testGetWaitQueueLengthNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads(null) throws NullPointerException
+     */
+    public void testGetWaitingThreadsNPE() {
+        final Mutex sync = new Mutex();
+        try {
+            sync.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not synced
+     */
+    public void testHasWaitersIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitQueueLengthIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final Mutex sync2 = new Mutex();
+        try {
+            sync2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not synced
+     */
+    public void testGetWaitingThreadsIMSE() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        try {
+            sync.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertFalse(sync.hasWaiters(c));
+                assertTrue(acquired.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        acquired.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.hasWaiters(c));
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertFalse(sync.hasWaiters(c));
+        sync.release();
+
+        awaitTermination(t);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertEquals(0, sync.getWaitQueueLength(c));
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertEquals(1, sync.getWaitQueueLength(c));
+        sync.release();
+
+        final Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertEquals(1, sync.getWaitQueueLength(c));
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertEquals(2, sync.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertEquals(0, sync.getWaitQueueLength(c));
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        final Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                assertTrue(sync.getWaitingThreads(c).isEmpty());
+                assertTrue(acquired1.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        final Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertHasWaitersLocked(sync, c, t1);
+                assertTrue(sync.getWaitingThreads(c).contains(t1));
+                assertFalse(sync.getWaitingThreads(c).isEmpty());
+                assertEquals(1, sync.getWaitingThreads(c).size());
+                assertTrue(acquired2.releaseShared(0));
+                c.await();
+                sync.release();
+            }});
+
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t1.start();
+        acquired1.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(1, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        t2.start();
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertTrue(sync.getWaitingThreads(c).contains(t1));
+        assertTrue(sync.getWaitingThreads(c).contains(t2));
+        assertFalse(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(2, sync.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        assertFalse(sync.getWaitingThreads(c).contains(t1));
+        assertFalse(sync.getWaitingThreads(c).contains(t2));
+        assertTrue(sync.getWaitingThreads(c).isEmpty());
+        assertEquals(0, sync.getWaitingThreads(c).size());
+        sync.release();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertHasWaitersUnlocked(sync, c, NO_THREADS);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly() {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                assertHasWaitersLocked(sync, c, NO_THREADS);
+                sync.release();
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        sync.release();
+        t.interrupt();
+        assertHasWaitersUnlocked(sync, c, t);
+        assertThreadStaysAlive(t);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signal();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t);
+        sync.release();
+        awaitTermination(t);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()      { testInterruptible(AwaitMethod.await); }
+    public void testInterruptible_awaitTimed() { testInterruptible(AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos() { testInterruptible(AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil() { testInterruptible(AwaitMethod.awaitUntil); }
+    public void testInterruptible(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch pleaseInterrupt = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                assertTrue(pleaseInterrupt.releaseShared(0));
+                await(c, awaitMethod);
+            }});
+
+        pleaseInterrupt.acquireShared(0);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()      { testSignalAll(AwaitMethod.await); }
+    public void testSignalAll_awaitTimed() { testSignalAll(AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos() { testSignalAll(AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil() { testSignalAll(AwaitMethod.awaitUntil); }
+    public void testSignalAll(final AwaitMethod awaitMethod) {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        final BooleanLatch acquired1 = new BooleanLatch();
+        final BooleanLatch acquired2 = new BooleanLatch();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired1.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                sync.acquire();
+                acquired2.releaseShared(0);
+                await(c, awaitMethod);
+                sync.release();
+            }});
+
+        acquired1.acquireShared(0);
+        acquired2.acquireShared(0);
+        sync.acquire();
+        assertHasWaitersLocked(sync, c, t1, t2);
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        c.signalAll();
+        assertHasWaitersLocked(sync, c, NO_THREADS);
+        assertHasExclusiveQueuedThreads(sync, t1, t2);
+        sync.release();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current state
+     */
+    public void testToString() {
+        Mutex sync = new Mutex();
+        assertTrue(sync.toString().contains("State = " + Mutex.UNLOCKED));
+        sync.acquire();
+        assertTrue(sync.toString().contains("State = " + Mutex.LOCKED));
+    }
+
+    /**
+     * A serialized AQS deserializes with current state, but no queued threads
+     */
+    public void testSerialization() {
+        Mutex sync = new Mutex();
+        assertFalse(serialClone(sync).isHeldExclusively());
+        sync.acquire();
+        Thread t = newStartedThread(new InterruptedSyncRunnable(sync));
+        waitForQueuedThread(sync, t);
+        assertTrue(sync.isHeldExclusively());
+
+        Mutex clone = serialClone(sync);
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, t);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+        t.interrupt();
+        awaitTermination(t);
+        sync.release();
+        assertFalse(sync.isHeldExclusively());
+        assertTrue(clone.isHeldExclusively());
+        assertHasExclusiveQueuedThreads(sync, NO_THREADS);
+        assertHasExclusiveQueuedThreads(clone, NO_THREADS);
+    }
+
+    /**
+     * tryReleaseShared setting state changes getState
+     */
+    public void testGetStateWithReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * releaseShared has no effect when already signalled
+     */
+    public void testReleaseShared() {
+        final BooleanLatch l = new BooleanLatch();
+        assertFalse(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+    }
+
+    /**
+     * acquireSharedInterruptibly returns after release, but not before
+     */
+    public void testAcquireSharedInterruptibly() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertHasSharedQueuedThreads(l, t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * tryAcquireSharedNanos returns after release, but not before
+     */
+    public void testTryAcquireSharedNanos() {
+        final BooleanLatch l = new BooleanLatch();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+                assertTrue(l.tryAcquireSharedNanos(0, nanos));
+                assertTrue(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        assertThreadStaysAlive(t);
+        assertTrue(l.releaseShared(0));
+        assertTrue(l.isSignalled());
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireSharedInterruptibly is interruptible
+     */
+    public void testAcquireSharedInterruptibly_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                l.acquireSharedInterruptibly(0);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos is interruptible
+     */
+    public void testTryAcquireSharedNanos_Interruptible() {
+        final BooleanLatch l = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                long nanos = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+                l.tryAcquireSharedNanos(0, nanos);
+            }});
+
+        waitForQueuedThread(l, t);
+        assertFalse(l.isSignalled());
+        t.interrupt();
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * tryAcquireSharedNanos times out if not released before timeout
+     */
+    public void testTryAcquireSharedNanos_Timeout() {
+        final BooleanLatch l = new BooleanLatch();
+        final BooleanLatch observedQueued = new BooleanLatch();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertFalse(l.isSignalled());
+                for (long millis = timeoutMillis();
+                     !observedQueued.isSignalled();
+                     millis *= 2) {
+                    long nanos = MILLISECONDS.toNanos(millis);
+                    long startTime = System.nanoTime();
+                    assertFalse(l.tryAcquireSharedNanos(0, nanos));
+                    assertTrue(millisElapsedSince(startTime) >= millis);
+                }
+                assertFalse(l.isSignalled());
+            }});
+
+        waitForQueuedThread(l, t);
+        observedQueued.releaseShared(0);
+        assertFalse(l.isSignalled());
+        awaitTermination(t);
+        assertFalse(l.isSignalled());
+    }
+
+    /**
+     * awaitNanos/timed await with 0 wait times out immediately
+     */
+    public void testAwait_Zero() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(0L) <= 0);
+        assertFalse(c.await(0L, NANOSECONDS));
+        sync.release();
+    }
+
+    /**
+     * awaitNanos/timed await with maximum negative wait times does not underflow
+     */
+    public void testAwait_NegativeInfinity() throws InterruptedException {
+        final Mutex sync = new Mutex();
+        final ConditionObject c = sync.newCondition();
+        sync.acquire();
+        assertTrue(c.awaitNanos(Long.MIN_VALUE) <= 0);
+        assertFalse(c.await(Long.MIN_VALUE, NANOSECONDS));
+        sync.release();
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java
new file mode 100644
index 0000000..23b11d7
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ArrayBlockingQueueTest.java
@@ -0,0 +1,979 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadLocalRandom;
+
+import junit.framework.Test;
+
+public class ArrayBlockingQueueTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return ArrayBlockingQueue.class; }
+            public Collection emptyCollection() {
+                boolean fair = ThreadLocalRandom.current().nextBoolean();
+                return populatedQueue(0, SIZE, 2 * SIZE, fair);
+            }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+
+        return newTestSuite(
+            ArrayBlockingQueueTest.class,
+            new Fair().testSuite(),
+            new NonFair().testSuite(),
+            CollectionTest.testSuite(new Implementation()));
+    }
+
+    public static class Fair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return populatedQueue(0, SIZE, 2 * SIZE, true);
+        }
+    }
+
+    public static class NonFair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return populatedQueue(0, SIZE, 2 * SIZE, false);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    static ArrayBlockingQueue<Integer> populatedQueue(int n) {
+        return populatedQueue(n, n, n, false);
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n - 1, with given capacity range and fairness.
+     */
+    static ArrayBlockingQueue<Integer> populatedQueue(
+        int size, int minCapacity, int maxCapacity, boolean fair) {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        int capacity = rnd.nextInt(minCapacity, maxCapacity + 1);
+        ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<>(capacity);
+        assertTrue(q.isEmpty());
+        // shuffle circular array elements so they wrap
+        {
+            int n = rnd.nextInt(capacity);
+            for (int i = 0; i < n; i++) q.add(42);
+            for (int i = 0; i < n; i++) q.remove();
+        }
+        for (int i = 0; i < size; i++)
+            assertTrue(q.offer((Integer) i));
+        assertEquals(size == 0, q.isEmpty());
+        assertEquals(capacity - size, q.remainingCapacity());
+        assertEquals(size, q.size());
+        if (size > 0)
+            assertEquals((Integer) 0, q.peek());
+        return q;
+    }
+
+    /**
+     * A new queue has the indicated capacity
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new ArrayBlockingQueue(SIZE).remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor_nonPositiveCapacity() {
+        for (int i : new int[] { 0, -1, Integer.MIN_VALUE }) {
+            try {
+                new ArrayBlockingQueue(i);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+            for (boolean fair : new boolean[] { true, false }) {
+                try {
+                    new ArrayBlockingQueue(i, fair);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor_nullCollection() {
+        try {
+            new ArrayBlockingQueue(1, true, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new ArrayBlockingQueue(SIZE, false, elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new ArrayBlockingQueue(SIZE, false, elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from too large collection throws IAE
+     */
+    public void testConstructor_collectionTooLarge() {
+        // just barely fits - succeeds
+        new ArrayBlockingQueue(SIZE, false,
+                               Collections.nCopies(SIZE, ""));
+        try {
+            new ArrayBlockingQueue(SIZE - 1, false,
+                                   Collections.nCopies(SIZE, ""));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor7() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE, true, elements);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Queue transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        BlockingQueue q = populatedQueue(0, 2, 2, false);
+        assertTrue(q.isEmpty());
+        assertEquals(2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.offer(two));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        int size = ThreadLocalRandom.current().nextInt(1, SIZE);
+        BlockingQueue q = populatedQueue(size, size, 2 * size, false);
+        int spare = q.remainingCapacity();
+        int capacity = spare + size;
+        for (int i = 0; i < size; i++) {
+            assertEquals(spare + i, q.remainingCapacity());
+            assertEquals(capacity, q.size() + q.remainingCapacity());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < size; i++) {
+            assertEquals(capacity - i, q.remainingCapacity());
+            assertEquals(capacity, q.size() + q.remainingCapacity());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws ISE if full
+     */
+    public void testAdd() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; i++) assertTrue(q.add((Integer) i));
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add((Integer) SIZE);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws ISE if not enough room
+     */
+    public void testAddAll_insufficientSpace() {
+        int size = ThreadLocalRandom.current().nextInt(1, SIZE);
+        ArrayBlockingQueue q = populatedQueue(0, size, size, false);
+        // Just fits:
+        q.addAll(populatedQueue(size, size, 2 * size, false));
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(size, q.size());
+        assertEquals(0, q.peek());
+        try {
+            q = populatedQueue(0, size, size, false);
+            q.addAll(Collections.nCopies(size + 1, 42));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final ArrayBlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        await(aboutToWait);
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        int size = ThreadLocalRandom.current().nextInt(1, SIZE);
+        ArrayBlockingQueue q = populatedQueue(size, size, 2 * size, false);
+        assertFalse(q.contains(null));
+        for (int i = 0; i < size; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            assertEquals(i, q.poll());
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        int size = ThreadLocalRandom.current().nextInt(1, 5);
+        ArrayBlockingQueue q = populatedQueue(size, size, 2 * size, false);
+        int capacity = size + q.remainingCapacity();
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(capacity, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayBlockingQueue p = new ArrayBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ArrayBlockingQueue q = populatedQueue(SIZE);
+            ArrayBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    void checkToArray(ArrayBlockingQueue<Integer> q) {
+        int size = q.size();
+        Object[] a1 = q.toArray();
+        assertEquals(size, a1.length);
+        Integer[] a2 = q.toArray(new Integer[0]);
+        assertEquals(size, a2.length);
+        Integer[] a3 = q.toArray(new Integer[Math.max(0, size - 1)]);
+        assertEquals(size, a3.length);
+        Integer[] a4 = new Integer[size];
+        assertSame(a4, q.toArray(a4));
+        Integer[] a5 = new Integer[size + 1];
+        Arrays.fill(a5, 42);
+        assertSame(a5, q.toArray(a5));
+        Integer[] a6 = new Integer[size + 2];
+        Arrays.fill(a6, 42);
+        assertSame(a6, q.toArray(a6));
+        Object[][] as = { a1, a2, a3, a4, a5, a6 };
+        for (Object[] a : as) {
+            if (a.length > size) assertNull(a[size]);
+            if (a.length > size + 1) assertEquals(42, a[size + 1]);
+        }
+        Iterator it = q.iterator();
+        Integer s = q.peek();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertEquals(s + i, (int) x);
+            for (Object[] a : as)
+                assertSame(a1[i], x);
+        }
+    }
+
+    /**
+     * toArray() and toArray(a) contain all elements in FIFO order
+     */
+    public void testToArray() {
+        final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        final int size = rnd.nextInt(6);
+        final int capacity = Math.max(1, size + rnd.nextInt(size + 1));
+        ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<>(capacity);
+        for (int i = 0; i < size; i++) {
+            checkToArray(q);
+            q.add(i);
+        }
+        // Provoke wraparound
+        int added = size * 2;
+        for (int i = 0; i < added; i++) {
+            checkToArray(q);
+            assertEquals((Integer) i, q.poll());
+            q.add(size + i);
+        }
+        for (int i = 0; i < size; i++) {
+            checkToArray(q);
+            assertEquals((Integer) (added + i), q.poll());
+        }
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray_incompatibleArrayType() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+        try {
+            q.toArray(new String[0]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new ArrayBlockingQueue(SIZE).iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        assertEquals("queue should be full", 0, q.remainingCapacity());
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        q.add(one);
+        q.add(two);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(three));
+                    threadsStarted.await();
+                    assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertEquals(0, q.remainingCapacity());
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final ArrayBlockingQueue q = new ArrayBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        ArrayBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final ArrayBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE * 2);
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            populatedQueue(0, 1, 10, false),
+            populatedQueue(2, 2, 10, true),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ArrayDeque8Test.java b/ojluni/src/test/java/util/concurrent/tck/ArrayDeque8Test.java
new file mode 100644
index 0000000..a25d036
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ArrayDeque8Test.java
@@ -0,0 +1,126 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Spliterator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ArrayDeque8Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(ArrayDeque8Test.class);
+    }
+
+    /**
+     * Spliterator.getComparator always throws IllegalStateException
+     */
+    public void testSpliterator_getComparator() {
+        assertThrows(IllegalStateException.class,
+                     () -> new ArrayDeque().spliterator().getComparator());
+    }
+
+    /**
+     * Spliterator characteristics are as advertised
+     */
+    public void testSpliterator_characteristics() {
+        ArrayDeque q = new ArrayDeque();
+        Spliterator s = q.spliterator();
+        int characteristics = s.characteristics();
+        int required = Spliterator.NONNULL
+            | Spliterator.ORDERED
+            | Spliterator.SIZED
+            | Spliterator.SUBSIZED;
+        assertEquals(required, characteristics & required);
+        assertTrue(s.hasCharacteristics(required));
+        assertEquals(0, characteristics
+                     & (Spliterator.CONCURRENT
+                        | Spliterator.DISTINCT
+                        | Spliterator.IMMUTABLE
+                        | Spliterator.SORTED));
+    }
+
+    /**
+     * Handle capacities near Integer.MAX_VALUE.
+     * ant -Dvmoptions='-Xms28g -Xmx28g' -Djsr166.expensiveTests=true -Djsr166.tckTestClass=ArrayDeque8Test -Djsr166.methodFilter=testHugeCapacity tck
+     */
+    public void testHugeCapacity() {
+        if (! (testImplementationDetails
+               && expensiveTests
+               && Runtime.getRuntime().maxMemory() > 24L * (1 << 30)))
+            return;
+
+        final Integer e = 42;
+        final int maxArraySize = Integer.MAX_VALUE - 8;
+
+        assertThrows(OutOfMemoryError.class,
+                     () -> new ArrayDeque(Integer.MAX_VALUE));
+
+        {
+            ArrayDeque q = new ArrayDeque(maxArraySize - 1);
+            assertEquals(0, q.size());
+            assertTrue(q.isEmpty());
+            q = null;
+        }
+
+        {
+            ArrayDeque q = new ArrayDeque();
+            assertTrue(q.addAll(Collections.nCopies(maxArraySize - 3, e)));
+            assertEquals(e, q.peekFirst());
+            assertEquals(e, q.peekLast());
+            assertEquals(maxArraySize - 3, q.size());
+            q.addFirst((Integer) 0);
+            q.addLast((Integer) 1);
+            assertEquals((Integer) 0, q.peekFirst());
+            assertEquals((Integer) 1, q.peekLast());
+            assertEquals(maxArraySize - 1, q.size());
+
+            ArrayDeque qq = q;
+            ArrayDeque smallish = new ArrayDeque(
+                Collections.nCopies(Integer.MAX_VALUE - q.size() + 1, e));
+            assertThrows(
+                IllegalStateException.class,
+                () -> qq.addAll(qq),
+                () -> qq.addAll(smallish),
+                () -> smallish.addAll(qq));
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ArrayDequeTest.java b/ojluni/src/test/java/util/concurrent/tck/ArrayDequeTest.java
new file mode 100644
index 0000000..1cff7ff
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ArrayDequeTest.java
@@ -0,0 +1,998 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ArrayDequeTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return ArrayDeque.class; }
+            public Collection emptyCollection() { return populatedDeque(0); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return false; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(ArrayDequeTest.class,
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private static ArrayDeque<Integer> populatedDeque(int n) {
+        // Randomize various aspects of memory layout, including
+        // capacity slop and wraparound.
+        final ArrayDeque<Integer> q;
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        switch (rnd.nextInt(6)) {
+        case 0: q = new ArrayDeque<Integer>();      break;
+        case 1: q = new ArrayDeque<Integer>(0);     break;
+        case 2: q = new ArrayDeque<Integer>(1);     break;
+        case 3: q = new ArrayDeque<Integer>(Math.max(0, n - 1)); break;
+        case 4: q = new ArrayDeque<Integer>(n);     break;
+        case 5: q = new ArrayDeque<Integer>(n + 1); break;
+        default: throw new AssertionError();
+        }
+        switch (rnd.nextInt(3)) {
+        case 0:
+            q.addFirst(42);
+            assertEquals((Integer) 42, q.removeLast());
+            break;
+        case 1:
+            q.addLast(42);
+            assertEquals((Integer) 42, q.removeFirst());
+            break;
+        case 2: /* do nothing */ break;
+        default: throw new AssertionError();
+        }
+        assertTrue(q.isEmpty());
+        if (rnd.nextBoolean())
+            for (int i = 0; i < n; i++)
+                assertTrue(q.offerLast((Integer) i));
+        else
+            for (int i = n; --i >= 0; )
+                q.addFirst((Integer) i);
+        assertEquals(n, q.size());
+        if (n > 0) {
+            assertFalse(q.isEmpty());
+            assertEquals((Integer) 0, q.peekFirst());
+            assertEquals((Integer) (n - 1), q.peekLast());
+        }
+        return q;
+    }
+
+    /**
+     * new deque is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ArrayDeque().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ArrayDeque((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ArrayDeque(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ArrayDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayDeque q = new ArrayDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.removeFirst();
+        q.removeFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.removeFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        ArrayDeque q = new ArrayDeque(1);
+        try {
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * peekFirst() returns element inserted with push
+     */
+    public void testPush() {
+        ArrayDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop() removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerFirst(null) throws NPE
+     */
+    public void testOfferFirstNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NPE
+     */
+    public void testOfferLastNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offer(x) succeeds
+     */
+    public void testOffer() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * offerFirst(x) succeeds
+     */
+    public void testOfferFirst() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offerFirst(zero));
+        assertTrue(q.offerFirst(one));
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * offerLast(x) succeeds
+     */
+    public void testOfferLast() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.offerLast(zero));
+        assertTrue(q.offerLast(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addFirst(null) throws NPE
+     */
+    public void testAddFirstNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addLast(null) throws NPE
+     */
+    public void testAddLastNull() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(x) succeeds
+     */
+    public void testAdd() {
+        ArrayDeque q = new ArrayDeque();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addFirst(x) succeeds
+     */
+    public void testAddFirst() {
+        ArrayDeque q = new ArrayDeque();
+        q.addFirst(zero);
+        q.addFirst(one);
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * addLast(x) succeeds
+     */
+    public void testAddLast() {
+        ArrayDeque q = new ArrayDeque();
+        q.addLast(zero);
+        q.addLast(one);
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ArrayDeque q = new ArrayDeque();
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ArrayDeque q = new ArrayDeque();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ArrayDeque q = new ArrayDeque();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * pollFirst() succeeds unless empty
+     */
+    public void testPollFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast() succeeds unless empty
+     */
+    public void testPollLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * poll() succeeds unless empty
+     */
+    public void testPoll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * remove() removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst() returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peek() returns next element, or null if empty
+     */
+    public void testPeek() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * peekLast() returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * element() returns first element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        Deque<Integer> q = populatedDeque(SIZE);
+        assertFalse(q.removeFirstOccurrence(null));
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(i));
+            assertFalse(q.contains(i));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(i));
+            assertFalse(q.removeFirstOccurrence(i + 1));
+            assertFalse(q.contains(i));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+        assertFalse(q.removeFirstOccurrence(null));
+        assertFalse(q.removeFirstOccurrence(42));
+        q = new ArrayDeque();
+        assertFalse(q.removeFirstOccurrence(null));
+        assertFalse(q.removeFirstOccurrence(42));
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        Deque<Integer> q = populatedDeque(SIZE);
+        assertFalse(q.removeLastOccurrence(null));
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(i));
+            assertFalse(q.contains(i));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(i));
+            assertFalse(q.removeLastOccurrence(i + 1));
+            assertFalse(q.contains(i));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+        assertFalse(q.removeLastOccurrence(null));
+        assertFalse(q.removeLastOccurrence(42));
+        q = new ArrayDeque();
+        assertFalse(q.removeLastOccurrence(null));
+        assertFalse(q.removeLastOccurrence(42));
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ArrayDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            assertEquals(i, q.pollFirst());
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ArrayDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        ArrayDeque p = new ArrayDeque();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            assertTrue(p.add(new Integer(i)));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ArrayDeque q = populatedDeque(SIZE);
+        ArrayDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            assertEquals(changed, (i > 0));
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.removeFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ArrayDeque q = populatedDeque(SIZE);
+            ArrayDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                assertFalse(q.contains(p.removeFirst()));
+            }
+        }
+    }
+
+    void checkToArray(ArrayDeque<Integer> q) {
+        int size = q.size();
+        Object[] a1 = q.toArray();
+        assertEquals(size, a1.length);
+        Integer[] a2 = q.toArray(new Integer[0]);
+        assertEquals(size, a2.length);
+        Integer[] a3 = q.toArray(new Integer[Math.max(0, size - 1)]);
+        assertEquals(size, a3.length);
+        Integer[] a4 = new Integer[size];
+        assertSame(a4, q.toArray(a4));
+        Integer[] a5 = new Integer[size + 1];
+        Arrays.fill(a5, 42);
+        assertSame(a5, q.toArray(a5));
+        Integer[] a6 = new Integer[size + 2];
+        Arrays.fill(a6, 42);
+        assertSame(a6, q.toArray(a6));
+        Object[][] as = { a1, a2, a3, a4, a5, a6 };
+        for (Object[] a : as) {
+            if (a.length > size) assertNull(a[size]);
+            if (a.length > size + 1) assertEquals(42, a[size + 1]);
+        }
+        Iterator it = q.iterator();
+        Integer s = q.peekFirst();
+        for (int i = 0; i < size; i++) {
+            Integer x = (Integer) it.next();
+            assertEquals(s + i, (int) x);
+            for (Object[] a : as)
+                assertSame(a1[i], x);
+        }
+    }
+
+    /**
+     * toArray() and toArray(a) contain all elements in FIFO order
+     */
+    public void testToArray() {
+        final int size = ThreadLocalRandom.current().nextInt(10);
+        ArrayDeque<Integer> q = new ArrayDeque<>(size);
+        for (int i = 0; i < size; i++) {
+            checkToArray(q);
+            q.addLast(i);
+        }
+        // Provoke wraparound
+        int added = size * 2;
+        for (int i = 0; i < added; i++) {
+            checkToArray(q);
+            assertEquals((Integer) i, q.poll());
+            q.addLast(size + i);
+        }
+        for (int i = 0; i < size; i++) {
+            checkToArray(q);
+            assertEquals((Integer) (added + i), q.poll());
+        }
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ArrayDeque l = new ArrayDeque();
+        l.add(new Object());
+        try {
+            l.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray_incompatibleArrayType() {
+        ArrayDeque l = new ArrayDeque();
+        l.add(new Integer(5));
+        try {
+            l.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+        try {
+            l.toArray(new String[0]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * Iterator iterates through all elements
+     */
+    public void testIterator() {
+        ArrayDeque q = populatedDeque(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Deque c = new ArrayDeque();
+        assertIteratorExhausted(c.iterator());
+        assertIteratorExhausted(c.descendingIterator());
+    }
+
+    /**
+     * Iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ArrayDeque q = new ArrayDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final ArrayDeque q = new ArrayDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = 1; j <= max; ++j)
+                q.add(new Integer(j));
+            Iterator it = q.iterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.iterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        ArrayDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final ArrayDeque q = new ArrayDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove() removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final ArrayDeque q = new ArrayDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = max; j >= 1; --j)
+                q.add(new Integer(j));
+            Iterator it = q.descendingIterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.descendingIterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * toString() contains toStrings of elements
+     */
+    public void testToString() {
+        ArrayDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(Arrays.toString(x.toArray()), Arrays.toString(y.toArray()));
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * A cloned deque has same elements in same order
+     */
+    public void testClone() throws Exception {
+        ArrayDeque<Integer> x = populatedDeque(SIZE);
+        ArrayDeque<Integer> y = x.clone();
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Deque<?>[] qs = {
+            new ArrayDeque<Object>(),
+            populatedDeque(2),
+        };
+
+        for (Deque<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+            assertFalse(q.removeFirstOccurrence(null));
+            assertFalse(q.removeLastOccurrence(null));
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ArrayListTest.java b/ojluni/src/test/java/util/concurrent/tck/ArrayListTest.java
new file mode 100644
index 0000000..b168d50
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ArrayListTest.java
@@ -0,0 +1,67 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ArrayListTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return ArrayList.class; }
+            public List emptyCollection() { return new ArrayList(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return false; }
+            public boolean permitsNulls() { return true; }
+        }
+        class SubListImplementation extends Implementation {
+            public List emptyCollection() {
+                return super.emptyCollection().subList(0, 0);
+            }
+        }
+        return newTestSuite(
+                // ArrayListTest.class,
+                CollectionTest.testSuite(new Implementation()),
+                CollectionTest.testSuite(new SubListImplementation()));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/Atomic8Test.java b/ojluni/src/test/java/util/concurrent/tck/Atomic8Test.java
new file mode 100644
index 0000000..dc3af4c
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/Atomic8Test.java
@@ -0,0 +1,628 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Tests of atomic class methods accepting lambdas introduced in JDK8.
+ */
+public class Atomic8Test extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(Atomic8Test.class);
+    }
+
+    static long addLong17(long x) { return x + 17; }
+    static int addInt17(int x) { return x + 17; }
+    static Integer addInteger17(Integer x) {
+        return x.intValue() + 17;
+    }
+    static Integer sumInteger(Integer x, Integer y) {
+        return x.intValue() + y.intValue();
+    }
+
+    volatile long aLongField;
+    volatile int anIntField;
+    volatile Integer anIntegerField;
+
+    AtomicLongFieldUpdater<Atomic8Test> aLongFieldUpdater() {
+        return AtomicLongFieldUpdater.newUpdater
+            (Atomic8Test.class, "aLongField");
+    }
+
+    AtomicIntegerFieldUpdater<Atomic8Test> anIntFieldUpdater() {
+        return AtomicIntegerFieldUpdater.newUpdater
+            (Atomic8Test.class, "anIntField");
+    }
+
+    AtomicReferenceFieldUpdater<Atomic8Test,Integer> anIntegerFieldUpdater() {
+        return AtomicReferenceFieldUpdater.newUpdater
+            (Atomic8Test.class, Integer.class, "anIntegerField");
+    }
+
+    /**
+     * AtomicLong getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testLongGetAndUpdate() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(1L, a.getAndUpdate(Atomic8Test::addLong17));
+        assertEquals(18L, a.getAndUpdate(Atomic8Test::addLong17));
+        assertEquals(35L, a.get());
+    }
+
+    /**
+     * AtomicLong updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongUpdateAndGet() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(18L, a.updateAndGet(Atomic8Test::addLong17));
+        assertEquals(35L, a.updateAndGet(Atomic8Test::addLong17));
+    }
+
+    /**
+     * AtomicLong getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testLongGetAndAccumulate() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(1L, a.getAndAccumulate(2L, Long::sum));
+        assertEquals(3L, a.getAndAccumulate(3L, Long::sum));
+        assertEquals(6L, a.get());
+    }
+
+    /**
+     * AtomicLong accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongAccumulateAndGet() {
+        AtomicLong a = new AtomicLong(1L);
+        assertEquals(7L, a.accumulateAndGet(6L, Long::sum));
+        assertEquals(10L, a.accumulateAndGet(3L, Long::sum));
+        assertEquals(10L, a.get());
+    }
+
+    /**
+     * AtomicInteger getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testIntGetAndUpdate() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(1, a.getAndUpdate(Atomic8Test::addInt17));
+        assertEquals(18, a.getAndUpdate(Atomic8Test::addInt17));
+        assertEquals(35, a.get());
+    }
+
+    /**
+     * AtomicInteger updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntUpdateAndGet() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(18, a.updateAndGet(Atomic8Test::addInt17));
+        assertEquals(35, a.updateAndGet(Atomic8Test::addInt17));
+        assertEquals(35, a.get());
+    }
+
+    /**
+     * AtomicInteger getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testIntGetAndAccumulate() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(1, a.getAndAccumulate(2, Integer::sum));
+        assertEquals(3, a.getAndAccumulate(3, Integer::sum));
+        assertEquals(6, a.get());
+    }
+
+    /**
+     * AtomicInteger accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntAccumulateAndGet() {
+        AtomicInteger a = new AtomicInteger(1);
+        assertEquals(7, a.accumulateAndGet(6, Integer::sum));
+        assertEquals(10, a.accumulateAndGet(3, Integer::sum));
+        assertEquals(10, a.get());
+    }
+
+    /**
+     * AtomicReference getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testReferenceGetAndUpdate() {
+        AtomicReference<Integer> a = new AtomicReference<>(one);
+        assertEquals((Integer) 1, a.getAndUpdate(Atomic8Test::addInteger17));
+        assertEquals((Integer) 18, a.getAndUpdate(Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.get());
+    }
+
+    /**
+     * AtomicReference updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceUpdateAndGet() {
+        AtomicReference<Integer> a = new AtomicReference<>(one);
+        assertEquals((Integer) 18, a.updateAndGet(Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.updateAndGet(Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.get());
+    }
+
+    /**
+     * AtomicReference getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testReferenceGetAndAccumulate() {
+        AtomicReference<Integer> a = new AtomicReference<>(one);
+        assertEquals((Integer) 1, a.getAndAccumulate(2, Atomic8Test::sumInteger));
+        assertEquals((Integer) 3, a.getAndAccumulate(3, Atomic8Test::sumInteger));
+        assertEquals((Integer) 6, a.get());
+    }
+
+    /**
+     * AtomicReference accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceAccumulateAndGet() {
+        AtomicReference<Integer> a = new AtomicReference<>(one);
+        assertEquals((Integer) 7, a.accumulateAndGet(6, Atomic8Test::sumInteger));
+        assertEquals((Integer) 10, a.accumulateAndGet(3, Atomic8Test::sumInteger));
+        assertEquals((Integer) 10, a.get());
+    }
+
+    /**
+     * AtomicLongArray getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testLongArrayGetAndUpdate() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(1L, a.getAndUpdate(0, Atomic8Test::addLong17));
+        assertEquals(18L, a.getAndUpdate(0, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(0));
+    }
+
+    /**
+     * AtomicLongArray updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongArrayUpdateAndGet() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(18L, a.updateAndGet(0, Atomic8Test::addLong17));
+        assertEquals(35L, a.updateAndGet(0, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(0));
+    }
+
+    /**
+     * AtomicLongArray getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testLongArrayGetAndAccumulate() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(1L, a.getAndAccumulate(0, 2L, Long::sum));
+        assertEquals(3L, a.getAndAccumulate(0, 3L, Long::sum));
+        assertEquals(6L, a.get(0));
+    }
+
+    /**
+     * AtomicLongArray accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongArrayAccumulateAndGet() {
+        AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        assertEquals(7L, a.accumulateAndGet(0, 6L, Long::sum));
+        assertEquals(10L, a.accumulateAndGet(0, 3L, Long::sum));
+        assertEquals(10L, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testIntArrayGetAndUpdate() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(1, a.getAndUpdate(0, Atomic8Test::addInt17));
+        assertEquals(18, a.getAndUpdate(0, Atomic8Test::addInt17));
+        assertEquals(35, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntArrayUpdateAndGet() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(18, a.updateAndGet(0, Atomic8Test::addInt17));
+        assertEquals(35, a.updateAndGet(0, Atomic8Test::addInt17));
+        assertEquals(35, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testIntArrayGetAndAccumulate() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(1, a.getAndAccumulate(0, 2, Integer::sum));
+        assertEquals(3, a.getAndAccumulate(0, 3, Integer::sum));
+        assertEquals(6, a.get(0));
+    }
+
+    /**
+     * AtomicIntegerArray accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntArrayAccumulateAndGet() {
+        AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        assertEquals(7, a.accumulateAndGet(0, 6, Integer::sum));
+        assertEquals(10, a.accumulateAndGet(0, 3, Integer::sum));
+    }
+
+    /**
+     * AtomicReferenceArray getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testReferenceArrayGetAndUpdate() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<>(1);
+        a.set(0, one);
+        assertEquals((Integer) 1, a.getAndUpdate(0, Atomic8Test::addInteger17));
+        assertEquals((Integer) 18, a.getAndUpdate(0, Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.get(0));
+    }
+
+    /**
+     * AtomicReferenceArray updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceArrayUpdateAndGet() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<>(1);
+        a.set(0, one);
+        assertEquals((Integer) 18, a.updateAndGet(0, Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.updateAndGet(0, Atomic8Test::addInteger17));
+    }
+
+    /**
+     * AtomicReferenceArray getAndAccumulate returns previous value and updates
+     * with supplied function.
+     */
+    public void testReferenceArrayGetAndAccumulate() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<>(1);
+        a.set(0, one);
+        assertEquals((Integer) 1, a.getAndAccumulate(0, 2, Atomic8Test::sumInteger));
+        assertEquals((Integer) 3, a.getAndAccumulate(0, 3, Atomic8Test::sumInteger));
+        assertEquals((Integer) 6, a.get(0));
+    }
+
+    /**
+     * AtomicReferenceArray accumulateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testReferenceArrayAccumulateAndGet() {
+        AtomicReferenceArray<Integer> a = new AtomicReferenceArray<>(1);
+        a.set(0, one);
+        assertEquals((Integer) 7, a.accumulateAndGet(0, 6, Atomic8Test::sumInteger));
+        assertEquals((Integer) 10, a.accumulateAndGet(0, 3, Atomic8Test::sumInteger));
+    }
+
+    /**
+     * AtomicLongFieldUpdater getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testLongFieldUpdaterGetAndUpdate() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1L, a.getAndUpdate(this, Atomic8Test::addLong17));
+        assertEquals(18L, a.getAndUpdate(this, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(this));
+        assertEquals(35L, aLongField);
+    }
+
+    /**
+     * AtomicLongFieldUpdater updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testLongFieldUpdaterUpdateAndGet() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(18L, a.updateAndGet(this, Atomic8Test::addLong17));
+        assertEquals(35L, a.updateAndGet(this, Atomic8Test::addLong17));
+        assertEquals(35L, a.get(this));
+        assertEquals(35L, aLongField);
+    }
+
+    /**
+     * AtomicLongFieldUpdater getAndAccumulate returns previous value
+     * and updates with supplied function.
+     */
+    public void testLongFieldUpdaterGetAndAccumulate() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1L, a.getAndAccumulate(this, 2L, Long::sum));
+        assertEquals(3L, a.getAndAccumulate(this, 3L, Long::sum));
+        assertEquals(6L, a.get(this));
+        assertEquals(6L, aLongField);
+    }
+
+    /**
+     * AtomicLongFieldUpdater accumulateAndGet updates with supplied
+     * function and returns result.
+     */
+    public void testLongFieldUpdaterAccumulateAndGet() {
+        AtomicLongFieldUpdater a = aLongFieldUpdater();
+        a.set(this, 1);
+        assertEquals(7L, a.accumulateAndGet(this, 6L, Long::sum));
+        assertEquals(10L, a.accumulateAndGet(this, 3L, Long::sum));
+        assertEquals(10L, a.get(this));
+        assertEquals(10L, aLongField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater getAndUpdate returns previous value and updates
+     * result of supplied function
+     */
+    public void testIntegerFieldUpdaterGetAndUpdate() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1, a.getAndUpdate(this, Atomic8Test::addInt17));
+        assertEquals(18, a.getAndUpdate(this, Atomic8Test::addInt17));
+        assertEquals(35, a.get(this));
+        assertEquals(35, anIntField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater updateAndGet updates with supplied function and
+     * returns result.
+     */
+    public void testIntegerFieldUpdaterUpdateAndGet() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(18, a.updateAndGet(this, Atomic8Test::addInt17));
+        assertEquals(35, a.updateAndGet(this, Atomic8Test::addInt17));
+        assertEquals(35, a.get(this));
+        assertEquals(35, anIntField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater getAndAccumulate returns previous value
+     * and updates with supplied function.
+     */
+    public void testIntegerFieldUpdaterGetAndAccumulate() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(1, a.getAndAccumulate(this, 2, Integer::sum));
+        assertEquals(3, a.getAndAccumulate(this, 3, Integer::sum));
+        assertEquals(6, a.get(this));
+        assertEquals(6, anIntField);
+    }
+
+    /**
+     * AtomicIntegerFieldUpdater accumulateAndGet updates with supplied
+     * function and returns result.
+     */
+    public void testIntegerFieldUpdaterAccumulateAndGet() {
+        AtomicIntegerFieldUpdater a = anIntFieldUpdater();
+        a.set(this, 1);
+        assertEquals(7, a.accumulateAndGet(this, 6, Integer::sum));
+        assertEquals(10, a.accumulateAndGet(this, 3, Integer::sum));
+        assertEquals(10, a.get(this));
+        assertEquals(10, anIntField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater getAndUpdate returns previous value
+     * and updates result of supplied function
+     */
+    public void testReferenceFieldUpdaterGetAndUpdate() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals((Integer) 1, a.getAndUpdate(this, Atomic8Test::addInteger17));
+        assertEquals((Integer) 18, a.getAndUpdate(this, Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.get(this));
+        assertEquals((Integer) 35, anIntegerField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater updateAndGet updates with supplied
+     * function and returns result.
+     */
+    public void testReferenceFieldUpdaterUpdateAndGet() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals((Integer) 18, a.updateAndGet(this, Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.updateAndGet(this, Atomic8Test::addInteger17));
+        assertEquals((Integer) 35, a.get(this));
+        assertEquals((Integer) 35, anIntegerField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater returns previous value and updates
+     * with supplied function.
+     */
+    public void testReferenceFieldUpdaterGetAndAccumulate() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals((Integer) 1, a.getAndAccumulate(this, 2, Atomic8Test::sumInteger));
+        assertEquals((Integer) 3, a.getAndAccumulate(this, 3, Atomic8Test::sumInteger));
+        assertEquals((Integer) 6, a.get(this));
+        assertEquals((Integer) 6, anIntegerField);
+    }
+
+    /**
+     * AtomicReferenceFieldUpdater accumulateAndGet updates with
+     * supplied function and returns result.
+     */
+    public void testReferenceFieldUpdaterAccumulateAndGet() {
+        AtomicReferenceFieldUpdater<Atomic8Test,Integer> a = anIntegerFieldUpdater();
+        a.set(this, one);
+        assertEquals((Integer) 7, a.accumulateAndGet(this, 6, Atomic8Test::sumInteger));
+        assertEquals((Integer) 10, a.accumulateAndGet(this, 3, Atomic8Test::sumInteger));
+        assertEquals((Integer) 10, a.get(this));
+        assertEquals((Integer) 10, anIntegerField);
+    }
+
+    /**
+     * All Atomic getAndUpdate methods throw NullPointerException on
+     * null function argument
+     */
+    public void testGetAndUpdateNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().getAndUpdate(null),
+            () -> new AtomicInteger().getAndUpdate(null),
+            () -> new AtomicReference().getAndUpdate(null),
+            () -> new AtomicLongArray(1).getAndUpdate(0, null),
+            () -> new AtomicIntegerArray(1).getAndUpdate(0, null),
+            () -> new AtomicReferenceArray(1).getAndUpdate(0, null),
+            () -> aLongFieldUpdater().getAndUpdate(this, null),
+            () -> anIntFieldUpdater().getAndUpdate(this, null),
+            () -> anIntegerFieldUpdater().getAndUpdate(this, null),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+    /**
+     * All Atomic updateAndGet methods throw NullPointerException on null function argument
+     */
+    public void testUpdateAndGetNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().updateAndGet(null),
+            () -> new AtomicInteger().updateAndGet(null),
+            () -> new AtomicReference().updateAndGet(null),
+            () -> new AtomicLongArray(1).updateAndGet(0, null),
+            () -> new AtomicIntegerArray(1).updateAndGet(0, null),
+            () -> new AtomicReferenceArray(1).updateAndGet(0, null),
+            () -> aLongFieldUpdater().updateAndGet(this, null),
+            () -> anIntFieldUpdater().updateAndGet(this, null),
+            () -> anIntegerFieldUpdater().updateAndGet(this, null),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+    /**
+     * All Atomic getAndAccumulate methods throw NullPointerException
+     * on null function argument
+     */
+    public void testGetAndAccumulateNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().getAndAccumulate(1L, null),
+            () -> new AtomicInteger().getAndAccumulate(1, null),
+            () -> new AtomicReference().getAndAccumulate(one, null),
+            () -> new AtomicLongArray(1).getAndAccumulate(0, 1L, null),
+            () -> new AtomicIntegerArray(1).getAndAccumulate(0, 1, null),
+            () -> new AtomicReferenceArray(1).getAndAccumulate(0, one, null),
+            () -> aLongFieldUpdater().getAndAccumulate(this, 1L, null),
+            () -> anIntFieldUpdater().getAndAccumulate(this, 1, null),
+            () -> anIntegerFieldUpdater().getAndAccumulate(this, one, null),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+    /**
+     * All Atomic accumulateAndGet methods throw NullPointerException
+     * on null function argument
+     */
+    public void testAccumulateAndGetNPE() {
+        Runnable[] throwingActions = {
+            () -> new AtomicLong().accumulateAndGet(1L, null),
+            () -> new AtomicInteger().accumulateAndGet(1, null),
+            () -> new AtomicReference().accumulateAndGet(one, null),
+            () -> new AtomicLongArray(1).accumulateAndGet(0, 1L, null),
+            () -> new AtomicIntegerArray(1).accumulateAndGet(0, 1, null),
+            () -> new AtomicReferenceArray(1).accumulateAndGet(0, one, null),
+            () -> aLongFieldUpdater().accumulateAndGet(this, 1L, null),
+            () -> anIntFieldUpdater().accumulateAndGet(this, 1, null),
+            () -> anIntegerFieldUpdater().accumulateAndGet(this, one, null),
+        };
+        assertThrows(NullPointerException.class, throwingActions);
+    }
+
+    /**
+     * Object arguments for parameters of type T that are not
+     * instances of the class passed to the newUpdater call will
+     * result in a ClassCastException being thrown.
+     */
+    public void testFieldUpdaters_ClassCastException() {
+        // Use raw types to allow passing wrong object type, provoking CCE
+        final AtomicLongFieldUpdater longUpdater = aLongFieldUpdater();
+        final AtomicIntegerFieldUpdater intUpdater = anIntFieldUpdater();
+        final AtomicReferenceFieldUpdater refUpdater = anIntegerFieldUpdater();
+        final Object obj = new Object();
+        for (Object x : new Object[]{ new Object(), null }) {
+            Runnable[] throwingActions = {
+                () -> longUpdater.get(x),
+                () -> intUpdater.get(x),
+                () -> refUpdater.get(x),
+
+                () -> longUpdater.set(x, 17L),
+                () -> intUpdater.set(x, 17),
+                () -> refUpdater.set(x, (Integer) 17),
+
+                () -> longUpdater.addAndGet(x, 17L),
+                () -> intUpdater.addAndGet(x, 17),
+
+                () -> longUpdater.getAndUpdate(x, y -> y),
+                () -> intUpdater.getAndUpdate(x, y -> y),
+                () -> refUpdater.getAndUpdate(x, y -> y),
+
+                () -> longUpdater.compareAndSet(x, 17L, 42L),
+                () -> intUpdater.compareAndSet(x, 17, 42),
+                () -> refUpdater.compareAndSet(x, (Integer) 17, (Integer) 42),
+            };
+            assertThrows(ClassCastException.class, throwingActions);
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicBooleanTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicBooleanTest.java
new file mode 100644
index 0000000..4d666da
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicBooleanTest.java
@@ -0,0 +1,173 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicBooleanTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicBooleanTest.class);
+    }
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        assertTrue(new AtomicBoolean(true).get());
+        assertFalse(new AtomicBoolean(false).get());
+    }
+
+    /**
+     * default constructed initializes to false
+     */
+    public void testConstructor2() {
+        AtomicBoolean ai = new AtomicBoolean();
+        assertFalse(ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.get());
+        ai.set(false);
+        assertFalse(ai.get());
+        ai.set(true);
+        assertTrue(ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.get());
+        ai.lazySet(false);
+        assertFalse(ai.get());
+        ai.lazySet(true);
+        assertTrue(ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        assertTrue(ai.compareAndSet(true, false));
+        assertFalse(ai.get());
+        assertTrue(ai.compareAndSet(false, false));
+        assertFalse(ai.get());
+        assertFalse(ai.compareAndSet(true, false));
+        assertFalse(ai.get());
+        assertTrue(ai.compareAndSet(false, true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicBoolean ai = new AtomicBoolean(true);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(false, true)) Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(true, false));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicBoolean ai = new AtomicBoolean(true);
+        do {} while (!ai.weakCompareAndSet(true, false));
+        assertFalse(ai.get());
+        do {} while (!ai.weakCompareAndSet(false, false));
+        assertFalse(ai.get());
+        do {} while (!ai.weakCompareAndSet(false, true));
+        assertTrue(ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicBoolean ai = new AtomicBoolean();
+        boolean[] booleans = { false, true };
+        for (boolean before : booleans)
+            for (boolean after : booleans) {
+                ai.set(before);
+                assertEquals(before, ai.getAndSet(after));
+                assertEquals(after, ai.get());
+            }
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicBoolean x = new AtomicBoolean();
+        AtomicBoolean y = serialClone(x);
+        x.set(true);
+        AtomicBoolean z = serialClone(x);
+        assertTrue(x.get());
+        assertFalse(y.get());
+        assertTrue(z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicBoolean ai = new AtomicBoolean();
+        assertEquals(Boolean.toString(false), ai.toString());
+        ai.set(true);
+        assertEquals(Boolean.toString(true), ai.toString());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java
new file mode 100644
index 0000000..e24d0ea
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerArrayTest.java
@@ -0,0 +1,369 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicIntegerArrayTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicIntegerArrayTest.class);
+    }
+
+    /**
+     * constructor creates array of given size with all elements zero
+     */
+    public void testConstructor() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            assertEquals(0, aa.get(i));
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            int[] a = null;
+            new AtomicIntegerArray(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        int[] a = { 17, 3, -42, 99, -7 };
+        AtomicIntegerArray aa = new AtomicIntegerArray(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.getAndAdd(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.addAndGet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.set(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.set(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.lazySet(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.lazySet(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertTrue(aa.compareAndSet(i, 1, 2));
+            assertTrue(aa.compareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, -5, 7));
+            assertEquals(-4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicIntegerArray a = new AtomicIntegerArray(1);
+        a.set(0, 1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            do {} while (!aa.weakCompareAndSet(i, 1, 2));
+            do {} while (!aa.weakCompareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            do {} while (!aa.weakCompareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndSet(i, 0));
+            assertEquals(0, aa.getAndSet(i, -10));
+            assertEquals(-10, aa.getAndSet(i, 1));
+        }
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndAdd(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(3, aa.getAndAdd(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndDecrement(i));
+            assertEquals(0, aa.getAndDecrement(i));
+            assertEquals(-1, aa.getAndDecrement(i));
+        }
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndIncrement(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-2, aa.getAndIncrement(i));
+            assertEquals(-1, aa.getAndIncrement(i));
+            assertEquals(0, aa.getAndIncrement(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(3, aa.addAndGet(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(-1, aa.addAndGet(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(0, aa.decrementAndGet(i));
+            assertEquals(-1, aa.decrementAndGet(i));
+            assertEquals(-2, aa.decrementAndGet(i));
+            assertEquals(-2, aa.get(i));
+        }
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(2, aa.incrementAndGet(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-1, aa.incrementAndGet(i));
+            assertEquals(0, aa.incrementAndGet(i));
+            assertEquals(1, aa.incrementAndGet(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    class Counter extends CheckedRunnable {
+        final AtomicIntegerArray aa;
+        int decs;
+        Counter(AtomicIntegerArray a) { aa = a; }
+        public void realRun() {
+            for (;;) {
+                boolean done = true;
+                for (int i = 0; i < aa.length(); i++) {
+                    int v = aa.get(i);
+                    assertTrue(v >= 0);
+                    if (v != 0) {
+                        done = false;
+                        if (aa.compareAndSet(i, v, v - 1))
+                            decs++;
+                    }
+                }
+                if (done)
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Multiple threads using same array of counters successfully
+     * update a number of times equal to total count
+     */
+    public void testCountingInMultipleThreads() throws InterruptedException {
+        final AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+        int countdown = 10000;
+        for (int i = 0; i < SIZE; i++)
+            aa.set(i, countdown);
+        Counter c1 = new Counter(aa);
+        Counter c2 = new Counter(aa);
+        Thread t1 = newStartedThread(c1);
+        Thread t2 = newStartedThread(c2);
+        t1.join();
+        t2.join();
+        assertEquals(c1.decs + c2.decs, SIZE * countdown);
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicIntegerArray x = new AtomicIntegerArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            x.set(i, -i);
+        AtomicIntegerArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        int[] a = { 17, 3, -42, 99, -7 };
+        AtomicIntegerArray aa = new AtomicIntegerArray(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java
new file mode 100644
index 0000000..0d6f79a
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerFieldUpdaterTest.java
@@ -0,0 +1,364 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase {
+    volatile int x = 0;
+    protected volatile int protectedField;
+    private volatile int privateField;
+    int w;
+    float z;
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicIntegerFieldUpdaterTest.class);
+    }
+
+    // for testing subclass access
+    static class AtomicIntegerFieldUpdaterTestSubclass extends AtomicIntegerFieldUpdaterTest {
+        public void checkPrivateAccess() {
+            try {
+                AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                    AtomicIntegerFieldUpdater.newUpdater
+                    (AtomicIntegerFieldUpdaterTest.class, "privateField");
+                shouldThrow();
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+
+        public void checkCompareAndSetProtectedSub() {
+            AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                AtomicIntegerFieldUpdater.newUpdater
+                (AtomicIntegerFieldUpdaterTest.class, "protectedField");
+            this.protectedField = 1;
+            assertTrue(a.compareAndSet(this, 1, 2));
+            assertTrue(a.compareAndSet(this, 2, -4));
+            assertEquals(-4, a.get(this));
+            assertFalse(a.compareAndSet(this, -5, 7));
+            assertEquals(-4, a.get(this));
+            assertTrue(a.compareAndSet(this, -4, 7));
+            assertEquals(7, a.get(this));
+        }
+    }
+
+    static class UnrelatedClass {
+        public void checkPackageAccess(AtomicIntegerFieldUpdaterTest obj) {
+            obj.x = 72;
+            AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                AtomicIntegerFieldUpdater.newUpdater
+                (AtomicIntegerFieldUpdaterTest.class, "x");
+            assertEquals(72, a.get(obj));
+            assertTrue(a.compareAndSet(obj, 72, 73));
+            assertEquals(73, a.get(obj));
+        }
+
+        public void checkPrivateAccess(AtomicIntegerFieldUpdaterTest obj) {
+            try {
+                AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a =
+                    AtomicIntegerFieldUpdater.newUpdater
+                    (AtomicIntegerFieldUpdaterTest.class, "privateField");
+                throw new AssertionError("should throw");
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+    }
+
+    AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> updaterFor(String fieldName) {
+        return AtomicIntegerFieldUpdater.newUpdater
+            (AtomicIntegerFieldUpdaterTest.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws IllegalArgumentException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction using private field from subclass throws RuntimeException
+     */
+    public void testPrivateFieldInSubclass() {
+        AtomicIntegerFieldUpdaterTestSubclass s =
+            new AtomicIntegerFieldUpdaterTestSubclass();
+        s.checkPrivateAccess();
+    }
+
+    /**
+     * construction from unrelated class; package access is allowed,
+     * private access is not
+     */
+    public void testUnrelatedClassAccess() {
+        new UnrelatedClass().checkPackageAccess(this);
+        new UnrelatedClass().checkPrivateAccess(this);
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.set(this, 2);
+        assertEquals(2, a.get(this));
+        a.set(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.lazySet(this, 2);
+        assertEquals(2, a.get(this));
+        a.lazySet(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtected() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("protectedField");
+        protectedField = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtectedInSubclass() {
+        AtomicIntegerFieldUpdaterTestSubclass s =
+            new AtomicIntegerFieldUpdaterTestSubclass();
+        s.checkCompareAndSetProtectedSub();
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = 1;
+        final AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicIntegerFieldUpdaterTest.this, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        do {} while (!a.weakCompareAndSet(this, 1, 2));
+        do {} while (!a.weakCompareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        do {} while (!a.weakCompareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndSet(this, 0));
+        assertEquals(0, a.getAndSet(this, -10));
+        assertEquals(-10, a.getAndSet(this, 1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndAdd(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(3, a.getAndAdd(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndDecrement(this));
+        assertEquals(0, a.getAndDecrement(this));
+        assertEquals(-1, a.getAndDecrement(this));
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndIncrement(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-2, a.getAndIncrement(this));
+        assertEquals(-1, a.getAndIncrement(this));
+        assertEquals(0, a.getAndIncrement(this));
+        assertEquals(1, a.get(this));
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(3, a.addAndGet(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(-1, a.addAndGet(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(0, a.decrementAndGet(this));
+        assertEquals(-1, a.decrementAndGet(this));
+        assertEquals(-2, a.decrementAndGet(this));
+        assertEquals(-2, a.get(this));
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(2, a.incrementAndGet(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-1, a.incrementAndGet(this));
+        assertEquals(0, a.incrementAndGet(this));
+        assertEquals(1, a.incrementAndGet(this));
+        assertEquals(1, a.get(this));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerTest.java
new file mode 100644
index 0000000..111cd3f
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicIntegerTest.java
@@ -0,0 +1,295 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicIntegerTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicIntegerTest.class);
+    }
+
+    final int[] VALUES = {
+        Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE,
+    };
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor2() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+        ai.set(2);
+        assertEquals(2, ai.get());
+        ai.set(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.get());
+        ai.lazySet(2);
+        assertEquals(2, ai.get());
+        ai.lazySet(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertTrue(ai.compareAndSet(1, 2));
+        assertTrue(ai.compareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        assertFalse(ai.compareAndSet(-5, 7));
+        assertEquals(-4, ai.get());
+        assertTrue(ai.compareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicInteger ai = new AtomicInteger(1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        do {} while (!ai.weakCompareAndSet(1, 2));
+        do {} while (!ai.weakCompareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        do {} while (!ai.weakCompareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndSet(0));
+        assertEquals(0, ai.getAndSet(-10));
+        assertEquals(-10, ai.getAndSet(1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndAdd(2));
+        assertEquals(3, ai.get());
+        assertEquals(3, ai.getAndAdd(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndDecrement());
+        assertEquals(0, ai.getAndDecrement());
+        assertEquals(-1, ai.getAndDecrement());
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(1, ai.getAndIncrement());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-2, ai.getAndIncrement());
+        assertEquals(-1, ai.getAndIncrement());
+        assertEquals(0, ai.getAndIncrement());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(3, ai.addAndGet(2));
+        assertEquals(3, ai.get());
+        assertEquals(-1, ai.addAndGet(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(0, ai.decrementAndGet());
+        assertEquals(-1, ai.decrementAndGet());
+        assertEquals(-2, ai.decrementAndGet());
+        assertEquals(-2, ai.get());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicInteger ai = new AtomicInteger(1);
+        assertEquals(2, ai.incrementAndGet());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-1, ai.incrementAndGet());
+        assertEquals(0, ai.incrementAndGet());
+        assertEquals(1, ai.incrementAndGet());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicInteger x = new AtomicInteger();
+        AtomicInteger y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(22);
+        AtomicInteger z = serialClone(x);
+        assertEquals(22, x.get());
+        assertEquals(0, y.get());
+        assertEquals(22, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals("0", ai.toString());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals(Integer.toString(x), ai.toString());
+        }
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0, ai.intValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals(x, ai.intValue());
+        }
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0L, ai.longValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((long)x, ai.longValue());
+        }
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0.0f, ai.floatValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((float)x, ai.floatValue());
+        }
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        AtomicInteger ai = new AtomicInteger();
+        assertEquals(0.0d, ai.doubleValue());
+        for (int x : VALUES) {
+            ai.set(x);
+            assertEquals((double)x, ai.doubleValue());
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicLongArrayTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicLongArrayTest.java
new file mode 100644
index 0000000..c271a06
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicLongArrayTest.java
@@ -0,0 +1,368 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLongArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLongArrayTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicLongArrayTest.class);
+    }
+
+    /**
+     * constructor creates array of given size with all elements zero
+     */
+    public void testConstructor() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            assertEquals(0, aa.get(i));
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            long[] a = null;
+            new AtomicLongArray(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        long[] a = { 17L, 3L, -42L, 99L, -7L };
+        AtomicLongArray aa = new AtomicLongArray(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, 1, 2);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.getAndAdd(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.addAndGet(index, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.set(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.set(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, 1);
+            assertEquals(1, aa.get(i));
+            aa.lazySet(i, 2);
+            assertEquals(2, aa.get(i));
+            aa.lazySet(i, -3);
+            assertEquals(-3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertTrue(aa.compareAndSet(i, 1, 2));
+            assertTrue(aa.compareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, -5, 7));
+            assertEquals(-4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws InterruptedException {
+        final AtomicLongArray a = new AtomicLongArray(1);
+        a.set(0, 1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            do {} while (!aa.weakCompareAndSet(i, 1, 2));
+            do {} while (!aa.weakCompareAndSet(i, 2, -4));
+            assertEquals(-4, aa.get(i));
+            do {} while (!aa.weakCompareAndSet(i, -4, 7));
+            assertEquals(7, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndSet(i, 0));
+            assertEquals(0, aa.getAndSet(i, -10));
+            assertEquals(-10, aa.getAndSet(i, 1));
+        }
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndAdd(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(3, aa.getAndAdd(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndDecrement(i));
+            assertEquals(0, aa.getAndDecrement(i));
+            assertEquals(-1, aa.getAndDecrement(i));
+        }
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(1, aa.getAndIncrement(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-2, aa.getAndIncrement(i));
+            assertEquals(-1, aa.getAndIncrement(i));
+            assertEquals(0, aa.getAndIncrement(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(3, aa.addAndGet(i, 2));
+            assertEquals(3, aa.get(i));
+            assertEquals(-1, aa.addAndGet(i, -4));
+            assertEquals(-1, aa.get(i));
+        }
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(0, aa.decrementAndGet(i));
+            assertEquals(-1, aa.decrementAndGet(i));
+            assertEquals(-2, aa.decrementAndGet(i));
+            assertEquals(-2, aa.get(i));
+        }
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLongArray aa = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, 1);
+            assertEquals(2, aa.incrementAndGet(i));
+            assertEquals(2, aa.get(i));
+            aa.set(i, -2);
+            assertEquals(-1, aa.incrementAndGet(i));
+            assertEquals(0, aa.incrementAndGet(i));
+            assertEquals(1, aa.incrementAndGet(i));
+            assertEquals(1, aa.get(i));
+        }
+    }
+
+    class Counter extends CheckedRunnable {
+        final AtomicLongArray aa;
+        int decs;
+        Counter(AtomicLongArray a) { aa = a; }
+        public void realRun() {
+            for (;;) {
+                boolean done = true;
+                for (int i = 0; i < aa.length(); i++) {
+                    long v = aa.get(i);
+                    assertTrue(v >= 0);
+                    if (v != 0) {
+                        done = false;
+                        if (aa.compareAndSet(i, v, v - 1))
+                            decs++;
+                    }
+                }
+                if (done)
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Multiple threads using same array of counters successfully
+     * update a number of times equal to total count
+     */
+    public void testCountingInMultipleThreads() throws InterruptedException {
+        final AtomicLongArray aa = new AtomicLongArray(SIZE);
+        long countdown = 10000;
+        for (int i = 0; i < SIZE; i++)
+            aa.set(i, countdown);
+        Counter c1 = new Counter(aa);
+        Counter c2 = new Counter(aa);
+        Thread t1 = newStartedThread(c1);
+        Thread t2 = newStartedThread(c2);
+        t1.join();
+        t2.join();
+        assertEquals(c1.decs + c2.decs, SIZE * countdown);
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicLongArray x = new AtomicLongArray(SIZE);
+        for (int i = 0; i < SIZE; i++)
+            x.set(i, -i);
+        AtomicLongArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        long[] a = { 17, 3, -42, 99, -7 };
+        AtomicLongArray aa = new AtomicLongArray(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java
new file mode 100644
index 0000000..70f0bb9
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicLongFieldUpdaterTest.java
@@ -0,0 +1,364 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLongFieldUpdaterTest extends JSR166TestCase {
+    volatile long x = 0;
+    protected volatile long protectedField;
+    private volatile long privateField;
+    long w;
+    float z;
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicLongFieldUpdaterTest.class);
+    }
+
+    // for testing subclass access
+    static class AtomicLongFieldUpdaterTestSubclass extends AtomicLongFieldUpdaterTest {
+        public void checkPrivateAccess() {
+            try {
+                AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                    AtomicLongFieldUpdater.newUpdater
+                    (AtomicLongFieldUpdaterTest.class, "privateField");
+                shouldThrow();
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+
+        public void checkCompareAndSetProtectedSub() {
+            AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                AtomicLongFieldUpdater.newUpdater
+                (AtomicLongFieldUpdaterTest.class, "protectedField");
+            this.protectedField = 1;
+            assertTrue(a.compareAndSet(this, 1, 2));
+            assertTrue(a.compareAndSet(this, 2, -4));
+            assertEquals(-4, a.get(this));
+            assertFalse(a.compareAndSet(this, -5, 7));
+            assertEquals(-4, a.get(this));
+            assertTrue(a.compareAndSet(this, -4, 7));
+            assertEquals(7, a.get(this));
+        }
+    }
+
+    static class UnrelatedClass {
+        public void checkPackageAccess(AtomicLongFieldUpdaterTest obj) {
+            obj.x = 72L;
+            AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                AtomicLongFieldUpdater.newUpdater
+                (AtomicLongFieldUpdaterTest.class, "x");
+            assertEquals(72L, a.get(obj));
+            assertTrue(a.compareAndSet(obj, 72L, 73L));
+            assertEquals(73L, a.get(obj));
+        }
+
+        public void checkPrivateAccess(AtomicLongFieldUpdaterTest obj) {
+            try {
+                AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a =
+                    AtomicLongFieldUpdater.newUpdater
+                    (AtomicLongFieldUpdaterTest.class, "privateField");
+                throw new AssertionError("should throw");
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+    }
+
+    AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> updaterFor(String fieldName) {
+        return AtomicLongFieldUpdater.newUpdater
+            (AtomicLongFieldUpdaterTest.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws IllegalArgumentException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * construction using private field from subclass throws RuntimeException
+     */
+    public void testPrivateFieldInSubclass() {
+        AtomicLongFieldUpdaterTestSubclass s =
+            new AtomicLongFieldUpdaterTestSubclass();
+        s.checkPrivateAccess();
+    }
+
+    /**
+     * construction from unrelated class; package access is allowed,
+     * private access is not
+     */
+    public void testUnrelatedClassAccess() {
+        new UnrelatedClass().checkPackageAccess(this);
+        new UnrelatedClass().checkPrivateAccess(this);
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.set(this, 2);
+        assertEquals(2, a.get(this));
+        a.set(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.get(this));
+        a.lazySet(this, 2);
+        assertEquals(2, a.get(this));
+        a.lazySet(this, -3);
+        assertEquals(-3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtected() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("protectedField");
+        protectedField = 1;
+        assertTrue(a.compareAndSet(this, 1, 2));
+        assertTrue(a.compareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        assertFalse(a.compareAndSet(this, -5, 7));
+        assertEquals(-4, a.get(this));
+        assertTrue(a.compareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing protected field value if
+     * equal to expected else fails
+     */
+    public void testCompareAndSetProtectedInSubclass() {
+        AtomicLongFieldUpdaterTestSubclass s =
+            new AtomicLongFieldUpdaterTestSubclass();
+        s.checkCompareAndSetProtectedSub();
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = 1;
+        final AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicLongFieldUpdaterTest.this, 2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, 1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        do {} while (!a.weakCompareAndSet(this, 1, 2));
+        do {} while (!a.weakCompareAndSet(this, 2, -4));
+        assertEquals(-4, a.get(this));
+        do {} while (!a.weakCompareAndSet(this, -4, 7));
+        assertEquals(7, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndSet(this, 0));
+        assertEquals(0, a.getAndSet(this, -10));
+        assertEquals(-10, a.getAndSet(this, 1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndAdd(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(3, a.getAndAdd(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndDecrement(this));
+        assertEquals(0, a.getAndDecrement(this));
+        assertEquals(-1, a.getAndDecrement(this));
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(1, a.getAndIncrement(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-2, a.getAndIncrement(this));
+        assertEquals(-1, a.getAndIncrement(this));
+        assertEquals(0, a.getAndIncrement(this));
+        assertEquals(1, a.get(this));
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(3, a.addAndGet(this, 2));
+        assertEquals(3, a.get(this));
+        assertEquals(-1, a.addAndGet(this, -4));
+        assertEquals(-1, a.get(this));
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(0, a.decrementAndGet(this));
+        assertEquals(-1, a.decrementAndGet(this));
+        assertEquals(-2, a.decrementAndGet(this));
+        assertEquals(-2, a.get(this));
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> a;
+        a = updaterFor("x");
+        x = 1;
+        assertEquals(2, a.incrementAndGet(this));
+        assertEquals(2, a.get(this));
+        a.set(this, -2);
+        assertEquals(-1, a.incrementAndGet(this));
+        assertEquals(0, a.incrementAndGet(this));
+        assertEquals(1, a.incrementAndGet(this));
+        assertEquals(1, a.get(this));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicLongTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicLongTest.java
new file mode 100644
index 0000000..5b65757
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicLongTest.java
@@ -0,0 +1,298 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLongTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicLongTest.class);
+    }
+
+    final long[] VALUES = {
+        Long.MIN_VALUE,
+        Integer.MIN_VALUE, -1, 0, 1, 42, Integer.MAX_VALUE,
+        Long.MAX_VALUE,
+    };
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor2() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+        ai.set(2);
+        assertEquals(2, ai.get());
+        ai.set(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.get());
+        ai.lazySet(2);
+        assertEquals(2, ai.get());
+        ai.lazySet(-3);
+        assertEquals(-3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertTrue(ai.compareAndSet(1, 2));
+        assertTrue(ai.compareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        assertFalse(ai.compareAndSet(-5, 7));
+        assertEquals(-4, ai.get());
+        assertTrue(ai.compareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicLong ai = new AtomicLong(1);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(2, 3))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(1, 2));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertEquals(3, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        do {} while (!ai.weakCompareAndSet(1, 2));
+        do {} while (!ai.weakCompareAndSet(2, -4));
+        assertEquals(-4, ai.get());
+        do {} while (!ai.weakCompareAndSet(-4, 7));
+        assertEquals(7, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndSet(0));
+        assertEquals(0, ai.getAndSet(-10));
+        assertEquals(-10, ai.getAndSet(1));
+    }
+
+    /**
+     * getAndAdd returns previous value and adds given value
+     */
+    public void testGetAndAdd() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndAdd(2));
+        assertEquals(3, ai.get());
+        assertEquals(3, ai.getAndAdd(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * getAndDecrement returns previous value and decrements
+     */
+    public void testGetAndDecrement() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndDecrement());
+        assertEquals(0, ai.getAndDecrement());
+        assertEquals(-1, ai.getAndDecrement());
+    }
+
+    /**
+     * getAndIncrement returns previous value and increments
+     */
+    public void testGetAndIncrement() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(1, ai.getAndIncrement());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-2, ai.getAndIncrement());
+        assertEquals(-1, ai.getAndIncrement());
+        assertEquals(0, ai.getAndIncrement());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * addAndGet adds given value to current, and returns current value
+     */
+    public void testAddAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(3, ai.addAndGet(2));
+        assertEquals(3, ai.get());
+        assertEquals(-1, ai.addAndGet(-4));
+        assertEquals(-1, ai.get());
+    }
+
+    /**
+     * decrementAndGet decrements and returns current value
+     */
+    public void testDecrementAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(0, ai.decrementAndGet());
+        assertEquals(-1, ai.decrementAndGet());
+        assertEquals(-2, ai.decrementAndGet());
+        assertEquals(-2, ai.get());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndGet() {
+        AtomicLong ai = new AtomicLong(1);
+        assertEquals(2, ai.incrementAndGet());
+        assertEquals(2, ai.get());
+        ai.set(-2);
+        assertEquals(-1, ai.incrementAndGet());
+        assertEquals(0, ai.incrementAndGet());
+        assertEquals(1, ai.incrementAndGet());
+        assertEquals(1, ai.get());
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicLong x = new AtomicLong();
+        AtomicLong y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(-22);
+        AtomicLong z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(-22, x.get());
+        assertEquals(0, y.get());
+        assertEquals(-22, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals("0", ai.toString());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals(Long.toString(x), ai.toString());
+        }
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0, ai.intValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((int)x, ai.intValue());
+        }
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0L, ai.longValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals(x, ai.longValue());
+        }
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0.0f, ai.floatValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((float)x, ai.floatValue());
+        }
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        AtomicLong ai = new AtomicLong();
+        assertEquals(0.0d, ai.doubleValue());
+        for (long x : VALUES) {
+            ai.set(x);
+            assertEquals((double)x, ai.doubleValue());
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java
new file mode 100644
index 0000000..8fc0369
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicMarkableReferenceTest.java
@@ -0,0 +1,181 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicMarkableReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicMarkableReferenceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicMarkableReferenceTest.class);
+    }
+
+    /**
+     * constructor initializes to given reference and mark
+     */
+    public void testConstructor() {
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+        AtomicMarkableReference a2 = new AtomicMarkableReference(null, true);
+        assertNull(a2.getReference());
+        assertTrue(a2.isMarked());
+    }
+
+    /**
+     * get returns the last values of reference and mark set
+     */
+    public void testGetSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertFalse(mark[0]);
+        ai.set(two, false);
+        assertSame(two, ai.getReference());
+        assertFalse(ai.isMarked());
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+        ai.set(one, true);
+        assertSame(one, ai.getReference());
+        assertTrue(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * attemptMark succeeds in single thread
+     */
+    public void testAttemptMark() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertFalse(ai.isMarked());
+        assertTrue(ai.attemptMark(one, true));
+        assertTrue(ai.isMarked());
+        assertSame(one, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * compareAndSet succeeds in changing values if equal to expected reference
+     * and mark else fails
+     */
+    public void testCompareAndSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.get(mark));
+        assertFalse(ai.isMarked());
+        assertFalse(mark[0]);
+
+        assertTrue(ai.compareAndSet(one, two, false, false));
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+
+        assertTrue(ai.compareAndSet(two, m3, false, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+
+        assertFalse(ai.compareAndSet(two, m3, true, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for reference value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three, false, false))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two, false, false));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.getReference());
+        assertFalse(ai.isMarked());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for mark value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads2() throws Exception {
+        final AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(one, one, true, false))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, one, false, true));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(one, ai.getReference());
+        assertFalse(ai.isMarked());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing values when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        boolean[] mark = new boolean[1];
+        AtomicMarkableReference ai = new AtomicMarkableReference(one, false);
+        assertSame(one, ai.get(mark));
+        assertFalse(ai.isMarked());
+        assertFalse(mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(one, two, false, false));
+        assertSame(two, ai.get(mark));
+        assertFalse(mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(two, m3, false, true));
+        assertSame(m3, ai.get(mark));
+        assertTrue(mark[0]);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java
new file mode 100644
index 0000000..6e078f8
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java
@@ -0,0 +1,248 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReferenceArrayTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicReferenceArrayTest.class);
+    }
+
+    /**
+     * constructor creates array of given size with all elements null
+     */
+    public void testConstructor() {
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<>(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            assertNull(aa.get(i));
+        }
+    }
+
+    /**
+     * constructor with null array throws NPE
+     */
+    public void testConstructor2NPE() {
+        try {
+            Integer[] a = null;
+            new AtomicReferenceArray<Integer>(a);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * constructor with array is of same size and has all elements
+     */
+    public void testConstructor2() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<>(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++)
+            assertEquals(a[i], aa.get(i));
+    }
+
+    /**
+     * Initialize AtomicReferenceArray<Class> with SubClass[]
+     */
+    public void testConstructorSubClassArray() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Number> aa = new AtomicReferenceArray<Number>(a);
+        assertEquals(a.length, aa.length());
+        for (int i = 0; i < a.length; i++) {
+            assertSame(a[i], aa.get(i));
+            Long x = Long.valueOf(i);
+            aa.set(i, x);
+            assertSame(x, aa.get(i));
+        }
+    }
+
+    /**
+     * get and set for out of bound indices throw IndexOutOfBoundsException
+     */
+    public void testIndexing() {
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<>(SIZE);
+        for (int index : new int[] { -1, SIZE }) {
+            try {
+                aa.get(index);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.set(index, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.lazySet(index, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.compareAndSet(index, null, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+            try {
+                aa.weakCompareAndSet(index, null, null);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get returns the last value set at index
+     */
+    public void testGetSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertSame(one, aa.get(i));
+            aa.set(i, two);
+            assertSame(two, aa.get(i));
+            aa.set(i, m3);
+            assertSame(m3, aa.get(i));
+        }
+    }
+
+    /**
+     * get returns the last value lazySet at index by same thread
+     */
+    public void testGetLazySet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.lazySet(i, one);
+            assertSame(one, aa.get(i));
+            aa.lazySet(i, two);
+            assertSame(two, aa.get(i));
+            aa.lazySet(i, m3);
+            assertSame(m3, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertTrue(aa.compareAndSet(i, one, two));
+            assertTrue(aa.compareAndSet(i, two, m4));
+            assertSame(m4, aa.get(i));
+            assertFalse(aa.compareAndSet(i, m5, seven));
+            assertSame(m4, aa.get(i));
+            assertTrue(aa.compareAndSet(i, m4, seven));
+            assertSame(seven, aa.get(i));
+        }
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws InterruptedException {
+        final AtomicReferenceArray a = new AtomicReferenceArray(1);
+        a.set(0, one);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(0, two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(0, one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, a.get(0));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            do {} while (!aa.weakCompareAndSet(i, one, two));
+            do {} while (!aa.weakCompareAndSet(i, two, m4));
+            assertSame(m4, aa.get(i));
+            do {} while (!aa.weakCompareAndSet(i, m4, seven));
+            assertSame(seven, aa.get(i));
+        }
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value at given index
+     */
+    public void testGetAndSet() {
+        AtomicReferenceArray aa = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            aa.set(i, one);
+            assertSame(one, aa.getAndSet(i, zero));
+            assertSame(zero, aa.getAndSet(i, m10));
+            assertSame(m10, aa.getAndSet(i, one));
+        }
+    }
+
+    /**
+     * a deserialized serialized array holds same values
+     */
+    public void testSerialization() throws Exception {
+        AtomicReferenceArray x = new AtomicReferenceArray(SIZE);
+        for (int i = 0; i < SIZE; i++) {
+            x.set(i, new Integer(-i));
+        }
+        AtomicReferenceArray y = serialClone(x);
+        assertNotSame(x, y);
+        assertEquals(x.length(), y.length());
+        for (int i = 0; i < SIZE; i++) {
+            assertEquals(x.get(i), y.get(i));
+        }
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        Integer[] a = { two, one, three, four, seven };
+        AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<>(a);
+        assertEquals(Arrays.toString(a), aa.toString());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java
new file mode 100644
index 0000000..2517842
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceFieldUpdaterTest.java
@@ -0,0 +1,266 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase {
+    volatile Integer x = null;
+    protected volatile Integer protectedField;
+    private volatile Integer privateField;
+    Object z;
+    Integer w;
+    volatile int i;
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicReferenceFieldUpdaterTest.class);
+    }
+
+    // for testing subclass access
+    static class AtomicReferenceFieldUpdaterTestSubclass extends AtomicReferenceFieldUpdaterTest {
+        public void checkPrivateAccess() {
+            try {
+                AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                    AtomicReferenceFieldUpdater.newUpdater
+                    (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField");
+                shouldThrow();
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+
+        public void checkCompareAndSetProtectedSub() {
+            AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                AtomicReferenceFieldUpdater.newUpdater
+                (AtomicReferenceFieldUpdaterTest.class, Integer.class, "protectedField");
+            this.protectedField = one;
+            assertTrue(a.compareAndSet(this, one, two));
+            assertTrue(a.compareAndSet(this, two, m4));
+            assertSame(m4, a.get(this));
+            assertFalse(a.compareAndSet(this, m5, seven));
+            assertFalse(seven == a.get(this));
+            assertTrue(a.compareAndSet(this, m4, seven));
+            assertSame(seven, a.get(this));
+        }
+    }
+
+    static class UnrelatedClass {
+        public void checkPackageAccess(AtomicReferenceFieldUpdaterTest obj) {
+            obj.x = one;
+            AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                AtomicReferenceFieldUpdater.newUpdater
+                (AtomicReferenceFieldUpdaterTest.class, Integer.class, "x");
+            assertSame(one, a.get(obj));
+            assertTrue(a.compareAndSet(obj, one, two));
+            assertSame(two, a.get(obj));
+        }
+
+        public void checkPrivateAccess(AtomicReferenceFieldUpdaterTest obj) {
+            try {
+                AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest,Integer> a =
+                    AtomicReferenceFieldUpdater.newUpdater
+                    (AtomicReferenceFieldUpdaterTest.class, Integer.class, "privateField");
+                throw new AssertionError("should throw");
+            } catch (RuntimeException success) {
+                assertNotNull(success.getCause());
+            }
+        }
+    }
+
+    static AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> updaterFor(String fieldName) {
+        return AtomicReferenceFieldUpdater.newUpdater
+            (AtomicReferenceFieldUpdaterTest.class, Integer.class, fieldName);
+    }
+
+    /**
+     * Construction with non-existent field throws RuntimeException
+     */
+    public void testConstructor() {
+        try {
+            updaterFor("y");
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertNotNull(success.getCause());
+        }
+    }
+
+    /**
+     * construction with field not of given type throws ClassCastException
+     */
+    public void testConstructor2() {
+        try {
+            updaterFor("z");
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * Constructor with non-volatile field throws IllegalArgumentException
+     */
+    public void testConstructor3() {
+        try {
+            updaterFor("w");
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor with non-reference field throws ClassCastException
+     */
+    public void testConstructor4() {
+        try {
+            updaterFor("i");
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * construction using private field from subclass throws RuntimeException
+     */
+    public void testPrivateFieldInSubclass() {
+        AtomicReferenceFieldUpdaterTestSubclass s =
+            new AtomicReferenceFieldUpdaterTestSubclass();
+        s.checkPrivateAccess();
+    }
+
+    /**
+     * construction from unrelated class; package access is allowed,
+     * private access is not
+     */
+    public void testUnrelatedClassAccess() {
+        new UnrelatedClass().checkPackageAccess(this);
+        new UnrelatedClass().checkPrivateAccess(this);
+    }
+
+    /**
+     * get returns the last value set or assigned
+     */
+    public void testGetSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.get(this));
+        a.set(this, two);
+        assertSame(two, a.get(this));
+        a.set(this, m3);
+        assertSame(m3, a.get(this));
+    }
+
+    /**
+     * get returns the last value lazySet by same thread
+     */
+    public void testGetLazySet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.get(this));
+        a.lazySet(this, two);
+        assertSame(two, a.get(this));
+        a.lazySet(this, m3);
+        assertSame(m3, a.get(this));
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertTrue(a.compareAndSet(this, one, two));
+        assertTrue(a.compareAndSet(this, two, m4));
+        assertSame(m4, a.get(this));
+        assertFalse(a.compareAndSet(this, m5, seven));
+        assertFalse(seven == a.get(this));
+        assertTrue(a.compareAndSet(this, m4, seven));
+        assertSame(seven, a.get(this));
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        x = one;
+        final AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!a.compareAndSet(AtomicReferenceFieldUpdaterTest.this, two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(a.compareAndSet(this, one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, a.get(this));
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        do {} while (!a.weakCompareAndSet(this, one, two));
+        do {} while (!a.weakCompareAndSet(this, two, m4));
+        assertSame(m4, a.get(this));
+        do {} while (!a.weakCompareAndSet(this, m4, seven));
+        assertSame(seven, a.get(this));
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, Integer> a;
+        a = updaterFor("x");
+        x = one;
+        assertSame(one, a.getAndSet(this, zero));
+        assertSame(zero, a.getAndSet(this, m10));
+        assertSame(m10, a.getAndSet(this, 1));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceTest.java
new file mode 100644
index 0000000..1c5e303
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicReferenceTest.java
@@ -0,0 +1,171 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReferenceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicReferenceTest.class);
+    }
+
+    /**
+     * constructor initializes to given value
+     */
+    public void testConstructor() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+    }
+
+    /**
+     * default constructed initializes to null
+     */
+    public void testConstructor2() {
+        AtomicReference ai = new AtomicReference();
+        assertNull(ai.get());
+    }
+
+    /**
+     * get returns the last value set
+     */
+    public void testGetSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+        ai.set(two);
+        assertSame(two, ai.get());
+        ai.set(m3);
+        assertSame(m3, ai.get());
+    }
+
+    /**
+     * get returns the last value lazySet in same thread
+     */
+    public void testGetLazySet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.get());
+        ai.lazySet(two);
+        assertSame(two, ai.get());
+        ai.lazySet(m3);
+        assertSame(m3, ai.get());
+    }
+
+    /**
+     * compareAndSet succeeds in changing value if equal to expected else fails
+     */
+    public void testCompareAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertTrue(ai.compareAndSet(one, two));
+        assertTrue(ai.compareAndSet(two, m4));
+        assertSame(m4, ai.get());
+        assertFalse(ai.compareAndSet(m5, seven));
+        assertSame(m4, ai.get());
+        assertTrue(ai.compareAndSet(m4, seven));
+        assertSame(seven, ai.get());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicReference ai = new AtomicReference(one);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.get());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing value when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        do {} while (!ai.weakCompareAndSet(one, two));
+        do {} while (!ai.weakCompareAndSet(two, m4));
+        assertSame(m4, ai.get());
+        do {} while (!ai.weakCompareAndSet(m4, seven));
+        assertSame(seven, ai.get());
+    }
+
+    /**
+     * getAndSet returns previous value and sets to given value
+     */
+    public void testGetAndSet() {
+        AtomicReference ai = new AtomicReference(one);
+        assertSame(one, ai.getAndSet(zero));
+        assertSame(zero, ai.getAndSet(m10));
+        assertSame(m10, ai.getAndSet(one));
+    }
+
+    /**
+     * a deserialized serialized atomic holds same value
+     */
+    public void testSerialization() throws Exception {
+        AtomicReference x = new AtomicReference();
+        AtomicReference y = serialClone(x);
+        assertNotSame(x, y);
+        x.set(one);
+        AtomicReference z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(one, x.get());
+        assertEquals(null, y.get());
+        assertEquals(one, z.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        AtomicReference<Integer> ai = new AtomicReference<>(one);
+        assertEquals(one.toString(), ai.toString());
+        ai.set(two);
+        assertEquals(two.toString(), ai.toString());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java b/ojluni/src/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java
new file mode 100644
index 0000000..244bfed
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/AtomicStampedReferenceTest.java
@@ -0,0 +1,181 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.atomic.AtomicStampedReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicStampedReferenceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(AtomicStampedReferenceTest.class);
+    }
+
+    /**
+     * constructor initializes to given reference and stamp
+     */
+    public void testConstructor() {
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        AtomicStampedReference a2 = new AtomicStampedReference(null, 1);
+        assertNull(a2.getReference());
+        assertEquals(1, a2.getStamp());
+    }
+
+    /**
+     * get returns the last values of reference and stamp set
+     */
+    public void testGetSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(0, mark[0]);
+        ai.set(two, 0);
+        assertSame(two, ai.getReference());
+        assertEquals(0, ai.getStamp());
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+        ai.set(one, 1);
+        assertSame(one, ai.getReference());
+        assertEquals(1, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * attemptStamp succeeds in single thread
+     */
+    public void testAttemptStamp() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertEquals(0, ai.getStamp());
+        assertTrue(ai.attemptStamp(one, 1));
+        assertEquals(1, ai.getStamp());
+        assertSame(one, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * compareAndSet succeeds in changing values if equal to expected reference
+     * and stamp else fails
+     */
+    public void testCompareAndSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.get(mark));
+        assertEquals(0, ai.getStamp());
+        assertEquals(0, mark[0]);
+
+        assertTrue(ai.compareAndSet(one, two, 0, 0));
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+
+        assertTrue(ai.compareAndSet(two, m3, 0, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+
+        assertFalse(ai.compareAndSet(two, m3, 1, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for reference value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads() throws Exception {
+        final AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(two, three, 0, 0))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, two, 0, 0));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(three, ai.getReference());
+        assertEquals(0, ai.getStamp());
+    }
+
+    /**
+     * compareAndSet in one thread enables another waiting for stamp value
+     * to succeed
+     */
+    public void testCompareAndSetInMultipleThreads2() throws Exception {
+        final AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                while (!ai.compareAndSet(one, one, 1, 2))
+                    Thread.yield();
+            }});
+
+        t.start();
+        assertTrue(ai.compareAndSet(one, one, 0, 1));
+        t.join(LONG_DELAY_MS);
+        assertFalse(t.isAlive());
+        assertSame(one, ai.getReference());
+        assertEquals(2, ai.getStamp());
+    }
+
+    /**
+     * repeated weakCompareAndSet succeeds in changing values when equal
+     * to expected
+     */
+    public void testWeakCompareAndSet() {
+        int[] mark = new int[1];
+        AtomicStampedReference ai = new AtomicStampedReference(one, 0);
+        assertSame(one, ai.get(mark));
+        assertEquals(0, ai.getStamp());
+        assertEquals(0, mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(one, two, 0, 0));
+        assertSame(two, ai.get(mark));
+        assertEquals(0, mark[0]);
+
+        do {} while (!ai.weakCompareAndSet(two, m3, 0, 1));
+        assertSame(m3, ai.get(mark));
+        assertEquals(1, mark[0]);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/BlockingQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/BlockingQueueTest.java
new file mode 100644
index 0000000..6aab850
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/BlockingQueueTest.java
@@ -0,0 +1,404 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ *
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Contains "contract" tests applicable to all BlockingQueue implementations.
+ */
+public abstract class BlockingQueueTest extends JSR166TestCase {
+    /*
+     * This is the start of an attempt to refactor the tests for the
+     * various related implementations of related interfaces without
+     * too much duplicated code.  junit does not really support such
+     * testing.  Here subclasses of TestCase not only contain tests,
+     * but also configuration information that describes the
+     * implementation class, most importantly how to instantiate
+     * instances.
+     */
+
+    /** Like suite(), but non-static */
+    public Test testSuite() {
+        // TODO: filter the returned tests using the configuration
+        // information provided by the subclass via protected methods.
+        return new TestSuite(this.getClass());
+    }
+
+    //----------------------------------------------------------------
+    // Configuration methods
+    //----------------------------------------------------------------
+
+    /** Returns an empty instance of the implementation class. */
+    protected abstract BlockingQueue emptyCollection();
+
+    /**
+     * Returns an element suitable for insertion in the collection.
+     * Override for collections with unusual element types.
+     */
+    protected Object makeElement(int i) {
+        return Integer.valueOf(i);
+    }
+
+    //----------------------------------------------------------------
+    // Tests
+    //----------------------------------------------------------------
+
+    /**
+     * offer(null) throws NullPointerException
+     */
+    public void testOfferNull() {
+        final Queue q = emptyCollection();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NullPointerException
+     */
+    public void testAddNull() {
+        final Collection q = emptyCollection();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * timed offer(null) throws NullPointerException
+     */
+    public void testTimedOfferNull() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        long startTime = System.nanoTime();
+        try {
+            q.offer(null, LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+    }
+
+    /**
+     * put(null) throws NullPointerException
+     */
+    public void testPutNull() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.put(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null) throws NullPointerException
+     */
+    public void testAddAllNull() throws InterruptedException {
+        final Collection q = emptyCollection();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NullPointerException
+     */
+    public void testAddAllNullElements() {
+        final Collection q = emptyCollection();
+        final Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArray() {
+        final Collection q = emptyCollection();
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(null) throws NullPointerException
+     */
+    public void testDrainToNull() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(this) throws IllegalArgumentException
+     */
+    public void testDrainToSelf() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * drainTo(null, n) throws NullPointerException
+     */
+    public void testDrainToNullN() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(null, 0);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * drainTo(this, n) throws IllegalArgumentException
+     */
+    public void testDrainToSelfN() {
+        final BlockingQueue q = emptyCollection();
+        try {
+            q.drainTo(q, 0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * drainTo(c, n) returns 0 and does nothing when n <= 0
+     */
+    public void testDrainToNonPositiveMaxElements() {
+        final BlockingQueue q = emptyCollection();
+        final int[] ns = { 0, -1, -42, Integer.MIN_VALUE };
+        for (int n : ns)
+            assertEquals(0, q.drainTo(new ArrayList(), n));
+        if (q.remainingCapacity() > 0) {
+            // Not SynchronousQueue, that is
+            Object one = makeElement(1);
+            q.add(one);
+            ArrayList c = new ArrayList();
+            for (int n : ns)
+                assertEquals(0, q.drainTo(new ArrayList(), n));
+            assertEquals(1, q.size());
+            assertSame(one, q.poll());
+            assertTrue(c.isEmpty());
+        }
+    }
+
+    /**
+     * timed poll before a delayed offer times out; after offer succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollWithOffer() throws InterruptedException {
+        final BlockingQueue q = emptyCollection();
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        final Object zero = makeElement(0);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                barrier.await();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take() blocks interruptibly when empty
+     */
+    public void testTakeFromEmptyBlocksInterruptibly() {
+        final BlockingQueue q = emptyCollection();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeFromEmptyAfterInterrupt() {
+        final BlockingQueue q = emptyCollection();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timed poll() blocks interruptibly when empty
+     */
+    public void testTimedPollFromEmptyBlocksInterruptibly() {
+        final BlockingQueue q = emptyCollection();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed poll() throws InterruptedException immediately if
+     * interrupted before waiting
+     */
+    public void testTimedPollFromEmptyAfterInterrupt() {
+        final BlockingQueue q = emptyCollection();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     * TODO: move to superclass CollectionTest.java
+     */
+    public void testRemoveElement() {
+        final BlockingQueue q = emptyCollection();
+        final int size = Math.min(q.remainingCapacity(), SIZE);
+        final Object[] elts = new Object[size];
+        assertFalse(q.contains(makeElement(99)));
+        assertFalse(q.remove(makeElement(99)));
+        checkEmpty(q);
+        for (int i = 0; i < size; i++)
+            q.add(elts[i] = makeElement(i));
+        for (int i = 1; i < size; i += 2) {
+            for (int pass = 0; pass < 2; pass++) {
+                assertEquals((pass == 0), q.contains(elts[i]));
+                assertEquals((pass == 0), q.remove(elts[i]));
+                assertFalse(q.contains(elts[i]));
+                assertTrue(q.contains(elts[i - 1]));
+                if (i < size - 1)
+                    assertTrue(q.contains(elts[i + 1]));
+            }
+        }
+        if (size > 0)
+            assertTrue(q.contains(elts[0]));
+        for (int i = size - 2; i >= 0; i -= 2) {
+            assertTrue(q.contains(elts[i]));
+            assertFalse(q.contains(elts[i + 1]));
+            assertTrue(q.remove(elts[i]));
+            assertFalse(q.contains(elts[i]));
+            assertFalse(q.remove(elts[i + 1]));
+            assertFalse(q.contains(elts[i + 1]));
+        }
+        checkEmpty(q);
+    }
+
+    /** For debugging. */
+    public void XXXXtestFails() {
+        fail(emptyCollection().getClass().toString());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/Collection8Test.java b/ojluni/src/test/java/util/concurrent/tck/Collection8Test.java
new file mode 100644
index 0000000..d95bbe3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/Collection8Test.java
@@ -0,0 +1,924 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Spliterator;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import junit.framework.Test;
+
+/**
+ * Contains tests applicable to all jdk8+ Collection implementations.
+ * An extension of CollectionTest.
+ */
+// Android-changed: Mark this abstract to prevent scanned by the JUnit directly. http://b/181312360
+// public class Collection8Test extends JSR166TestCase {
+public abstract class Collection8Test extends JSR166TestCase {
+    final CollectionImplementation impl;
+
+    /** Tests are parameterized by a Collection implementation. */
+    Collection8Test(CollectionImplementation impl, String methodName) {
+        super(methodName);
+        this.impl = impl;
+    }
+
+    // BEGIN Android-added: Mark Collection8Test abstract. http://b/181312360
+    static class AndroidCollection8Test extends Collection8Test {
+
+        AndroidCollection8Test(CollectionImplementation impl, String methodName) {
+            super(impl, methodName);
+        }
+    }
+    // END Android-added: Mark Collection8Test abstract. http://b/181312360
+
+    public static Test testSuite(CollectionImplementation impl) {
+        // Android-changed: Prevent scanned by the JUnit directly. http://b/181312360
+        // return parameterizedTestSuite(Collection8Test.class,
+        return parameterizedTestSuite(AndroidCollection8Test.class,
+                                      CollectionImplementation.class,
+                                      impl);
+    }
+
+    Object bomb() {
+        return new Object() {
+                public boolean equals(Object x) { throw new AssertionError(); }
+                public int hashCode() { throw new AssertionError(); }
+            };
+    }
+
+    /** Checks properties of empty collections. */
+    public void testEmptyMeansEmpty() throws Throwable {
+        Collection c = impl.emptyCollection();
+        emptyMeansEmpty(c);
+
+        if (c instanceof java.io.Serializable) {
+            try {
+                emptyMeansEmpty(serialClonePossiblyFailing(c));
+            } catch (java.io.NotSerializableException ex) {
+                // excusable when we have a serializable wrapper around
+                // a non-serializable collection, as can happen with:
+                // Vector.subList() => wrapped AbstractList$RandomAccessSubList
+                if (testImplementationDetails
+                    && (! c.getClass().getName().matches(
+                                "java.util.Collections.*")))
+                    throw ex;
+            }
+        }
+
+        Collection clone = cloneableClone(c);
+        if (clone != null)
+            emptyMeansEmpty(clone);
+    }
+
+    void emptyMeansEmpty(Collection c) throws InterruptedException {
+        assertTrue(c.isEmpty());
+        assertEquals(0, c.size());
+        assertEquals("[]", c.toString());
+        {
+            Object[] a = c.toArray();
+            assertEquals(0, a.length);
+            assertSame(Object[].class, a.getClass());
+        }
+        {
+            Object[] a = new Object[0];
+            assertSame(a, c.toArray(a));
+        }
+        {
+            Integer[] a = new Integer[0];
+            assertSame(a, c.toArray(a));
+        }
+        {
+            Integer[] a = { 1, 2, 3};
+            assertSame(a, c.toArray(a));
+            assertNull(a[0]);
+            assertSame(2, a[1]);
+            assertSame(3, a[2]);
+        }
+        assertIteratorExhausted(c.iterator());
+        Consumer alwaysThrows = e -> { throw new AssertionError(); };
+        c.forEach(alwaysThrows);
+        c.iterator().forEachRemaining(alwaysThrows);
+        c.spliterator().forEachRemaining(alwaysThrows);
+        assertFalse(c.spliterator().tryAdvance(alwaysThrows));
+        if (c.spliterator().hasCharacteristics(Spliterator.SIZED))
+            assertEquals(0, c.spliterator().estimateSize());
+        assertFalse(c.contains(bomb()));
+        assertFalse(c.remove(bomb()));
+        if (c instanceof Queue) {
+            Queue q = (Queue) c;
+            assertNull(q.peek());
+            assertNull(q.poll());
+        }
+        if (c instanceof Deque) {
+            Deque d = (Deque) c;
+            assertNull(d.peekFirst());
+            assertNull(d.peekLast());
+            assertNull(d.pollFirst());
+            assertNull(d.pollLast());
+            assertIteratorExhausted(d.descendingIterator());
+            d.descendingIterator().forEachRemaining(alwaysThrows);
+            assertFalse(d.removeFirstOccurrence(bomb()));
+            assertFalse(d.removeLastOccurrence(bomb()));
+        }
+        if (c instanceof BlockingQueue) {
+            BlockingQueue q = (BlockingQueue) c;
+            assertNull(q.poll(0L, MILLISECONDS));
+        }
+        if (c instanceof BlockingDeque) {
+            BlockingDeque q = (BlockingDeque) c;
+            assertNull(q.pollFirst(0L, MILLISECONDS));
+            assertNull(q.pollLast(0L, MILLISECONDS));
+        }
+    }
+
+    public void testNullPointerExceptions() throws InterruptedException {
+        Collection c = impl.emptyCollection();
+        assertThrows(
+            NullPointerException.class,
+            () -> c.addAll(null),
+            () -> c.containsAll(null),
+            () -> c.retainAll(null),
+            () -> c.removeAll(null),
+            () -> c.removeIf(null),
+            () -> c.forEach(null),
+            () -> c.iterator().forEachRemaining(null),
+            () -> c.spliterator().forEachRemaining(null),
+            () -> c.spliterator().tryAdvance(null),
+            () -> c.toArray(null));
+
+        if (!impl.permitsNulls()) {
+            assertThrows(
+                NullPointerException.class,
+                () -> c.add(null));
+        }
+        if (!impl.permitsNulls() && c instanceof Queue) {
+            Queue q = (Queue) c;
+            assertThrows(
+                NullPointerException.class,
+                () -> q.offer(null));
+        }
+        if (!impl.permitsNulls() && c instanceof Deque) {
+            Deque d = (Deque) c;
+            assertThrows(
+                NullPointerException.class,
+                () -> d.addFirst(null),
+                () -> d.addLast(null),
+                () -> d.offerFirst(null),
+                () -> d.offerLast(null),
+                () -> d.push(null),
+                () -> d.descendingIterator().forEachRemaining(null));
+        }
+        if (c instanceof BlockingQueue) {
+            BlockingQueue q = (BlockingQueue) c;
+            assertThrows(
+                NullPointerException.class,
+                () -> {
+                    try { q.offer(null, 1L, HOURS); }
+                    catch (InterruptedException ex) {
+                        throw new AssertionError(ex);
+                    }},
+                () -> {
+                    try { q.put(null); }
+                    catch (InterruptedException ex) {
+                        throw new AssertionError(ex);
+                    }});
+        }
+        if (c instanceof BlockingDeque) {
+            BlockingDeque q = (BlockingDeque) c;
+            assertThrows(
+                NullPointerException.class,
+                () -> {
+                    try { q.offerFirst(null, 1L, HOURS); }
+                    catch (InterruptedException ex) {
+                        throw new AssertionError(ex);
+                    }},
+                () -> {
+                    try { q.offerLast(null, 1L, HOURS); }
+                    catch (InterruptedException ex) {
+                        throw new AssertionError(ex);
+                    }},
+                () -> {
+                    try { q.putFirst(null); }
+                    catch (InterruptedException ex) {
+                        throw new AssertionError(ex);
+                    }},
+                () -> {
+                    try { q.putLast(null); }
+                    catch (InterruptedException ex) {
+                        throw new AssertionError(ex);
+                    }});
+        }
+    }
+
+    public void testNoSuchElementExceptions() {
+        Collection c = impl.emptyCollection();
+        assertThrows(
+            NoSuchElementException.class,
+            () -> c.iterator().next());
+
+        if (c instanceof Queue) {
+            Queue q = (Queue) c;
+            assertThrows(
+                NoSuchElementException.class,
+                () -> q.element(),
+                () -> q.remove());
+        }
+        if (c instanceof Deque) {
+            Deque d = (Deque) c;
+            assertThrows(
+                NoSuchElementException.class,
+                () -> d.getFirst(),
+                () -> d.getLast(),
+                () -> d.removeFirst(),
+                () -> d.removeLast(),
+                () -> d.pop(),
+                () -> d.descendingIterator().next());
+        }
+    }
+
+    public void testRemoveIf() {
+        Collection c = impl.emptyCollection();
+        boolean ordered =
+            c.spliterator().hasCharacteristics(Spliterator.ORDERED);
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        int n = rnd.nextInt(6);
+        for (int i = 0; i < n; i++) c.add(impl.makeElement(i));
+        AtomicReference threwAt = new AtomicReference(null);
+        List orig = rnd.nextBoolean()
+            ? new ArrayList(c)
+            : Arrays.asList(c.toArray());
+
+        // Merely creating an iterator can change ArrayBlockingQueue behavior
+        Iterator it = rnd.nextBoolean() ? c.iterator() : null;
+
+        ArrayList survivors = new ArrayList();
+        ArrayList accepts = new ArrayList();
+        ArrayList rejects = new ArrayList();
+
+        Predicate randomPredicate = e -> {
+            assertNull(threwAt.get());
+            switch (rnd.nextInt(3)) {
+            case 0: accepts.add(e); return true;
+            case 1: rejects.add(e); return false;
+            case 2: threwAt.set(e); throw new ArithmeticException();
+            default: throw new AssertionError();
+            }
+        };
+        try {
+            try {
+                boolean modified = c.removeIf(randomPredicate);
+                assertNull(threwAt.get());
+                assertEquals(modified, accepts.size() > 0);
+                assertEquals(modified, rejects.size() != n);
+                assertEquals(accepts.size() + rejects.size(), n);
+                if (ordered) {
+                    assertEquals(rejects,
+                                 Arrays.asList(c.toArray()));
+                } else {
+                    assertEquals(new HashSet(rejects),
+                                 new HashSet(Arrays.asList(c.toArray())));
+                }
+            } catch (ArithmeticException ok) {
+                assertNotNull(threwAt.get());
+                assertTrue(c.contains(threwAt.get()));
+            }
+            if (it != null && impl.isConcurrent())
+                // check for weakly consistent iterator
+                while (it.hasNext()) assertTrue(orig.contains(it.next()));
+            switch (rnd.nextInt(4)) {
+            case 0: survivors.addAll(c); break;
+            case 1: survivors.addAll(Arrays.asList(c.toArray())); break;
+            case 2: c.forEach(survivors::add); break;
+            case 3: for (Object e : c) survivors.add(e); break;
+            }
+            assertTrue(orig.containsAll(accepts));
+            assertTrue(orig.containsAll(rejects));
+            assertTrue(orig.containsAll(survivors));
+            assertTrue(orig.containsAll(c));
+            assertTrue(c.containsAll(rejects));
+            assertTrue(c.containsAll(survivors));
+            assertTrue(survivors.containsAll(rejects));
+            if (threwAt.get() == null) {
+                assertEquals(n - accepts.size(), c.size());
+                for (Object x : accepts) assertFalse(c.contains(x));
+            } else {
+                // Two acceptable behaviors: entire removeIf call is one
+                // transaction, or each element processed is one transaction.
+                assertTrue(n == c.size() || n == c.size() + accepts.size());
+                int k = 0;
+                for (Object x : accepts) if (c.contains(x)) k++;
+                assertTrue(k == accepts.size() || k == 0);
+            }
+        } catch (Throwable ex) {
+            System.err.println(impl.klazz());
+            // c is at risk of corruption if we got here, so be lenient
+            try { System.err.printf("c=%s%n", c); }
+            catch (Throwable t) { t.printStackTrace(); }
+            System.err.printf("n=%d%n", n);
+            System.err.printf("orig=%s%n", orig);
+            System.err.printf("accepts=%s%n", accepts);
+            System.err.printf("rejects=%s%n", rejects);
+            System.err.printf("survivors=%s%n", survivors);
+            System.err.printf("threwAt=%s%n", threwAt.get());
+            throw ex;
+        }
+    }
+
+    /**
+     * All elements removed in the middle of CONCURRENT traversal.
+     */
+    public void testElementRemovalDuringTraversal() {
+        Collection c = impl.emptyCollection();
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        int n = rnd.nextInt(6);
+        ArrayList copy = new ArrayList();
+        for (int i = 0; i < n; i++) {
+            Object x = impl.makeElement(i);
+            copy.add(x);
+            c.add(x);
+        }
+        ArrayList iterated = new ArrayList();
+        ArrayList spliterated = new ArrayList();
+        Spliterator s = c.spliterator();
+        Iterator it = c.iterator();
+        for (int i = rnd.nextInt(n + 1); --i >= 0; ) {
+            assertTrue(s.tryAdvance(spliterated::add));
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            iterated.add(it.next());
+        }
+        Consumer alwaysThrows = e -> { throw new AssertionError(); };
+        if (s.hasCharacteristics(Spliterator.CONCURRENT)) {
+            c.clear();          // TODO: many more removal methods
+            if (testImplementationDetails
+                && !(c instanceof java.util.concurrent.ArrayBlockingQueue)) {
+                if (rnd.nextBoolean())
+                    assertFalse(s.tryAdvance(alwaysThrows));
+                else
+                    s.forEachRemaining(alwaysThrows);
+            }
+            if (it.hasNext()) iterated.add(it.next());
+            if (rnd.nextBoolean()) assertIteratorExhausted(it);
+        }
+        assertTrue(copy.containsAll(iterated));
+        assertTrue(copy.containsAll(spliterated));
+    }
+
+    /**
+     * Some elements randomly disappear in the middle of traversal.
+     */
+    public void testRandomElementRemovalDuringTraversal() {
+        Collection c = impl.emptyCollection();
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        int n = rnd.nextInt(6);
+        ArrayList copy = new ArrayList();
+        for (int i = 0; i < n; i++) {
+            Object x = impl.makeElement(i);
+            copy.add(x);
+            c.add(x);
+        }
+        ArrayList iterated = new ArrayList();
+        ArrayList spliterated = new ArrayList();
+        ArrayList removed = new ArrayList();
+        Spliterator s = c.spliterator();
+        Iterator it = c.iterator();
+        if (! (s.hasCharacteristics(Spliterator.CONCURRENT) ||
+               s.hasCharacteristics(Spliterator.IMMUTABLE)))
+            return;
+        for (int i = rnd.nextInt(n + 1); --i >= 0; ) {
+            assertTrue(s.tryAdvance(e -> {}));
+            if (rnd.nextBoolean()) assertTrue(it.hasNext());
+            it.next();
+        }
+        Consumer alwaysThrows = e -> { throw new AssertionError(); };
+        // TODO: many more removal methods
+        if (rnd.nextBoolean()) {
+            for (Iterator z = c.iterator(); z.hasNext(); ) {
+                Object e = z.next();
+                if (rnd.nextBoolean()) {
+                    try {
+                        z.remove();
+                    } catch (UnsupportedOperationException ok) { return; }
+                    removed.add(e);
+                }
+            }
+        } else {
+            Predicate randomlyRemove = e -> {
+                if (rnd.nextBoolean()) { removed.add(e); return true; }
+                else return false;
+            };
+            c.removeIf(randomlyRemove);
+        }
+        s.forEachRemaining(spliterated::add);
+        while (it.hasNext())
+            iterated.add(it.next());
+        assertTrue(copy.containsAll(iterated));
+        assertTrue(copy.containsAll(spliterated));
+        assertTrue(copy.containsAll(removed));
+        if (s.hasCharacteristics(Spliterator.CONCURRENT)) {
+            ArrayList iteratedAndRemoved = new ArrayList(iterated);
+            ArrayList spliteratedAndRemoved = new ArrayList(spliterated);
+            iteratedAndRemoved.retainAll(removed);
+            spliteratedAndRemoved.retainAll(removed);
+            assertTrue(iteratedAndRemoved.size() <= 1);
+            assertTrue(spliteratedAndRemoved.size() <= 1);
+            if (testImplementationDetails
+                && !(c instanceof java.util.concurrent.ArrayBlockingQueue))
+                assertTrue(spliteratedAndRemoved.isEmpty());
+        }
+    }
+
+    /**
+     * Various ways of traversing a collection yield same elements
+     */
+    public void testTraversalEquivalence() {
+        Collection c = impl.emptyCollection();
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        int n = rnd.nextInt(6);
+        for (int i = 0; i < n; i++) c.add(impl.makeElement(i));
+        ArrayList iterated = new ArrayList();
+        ArrayList iteratedForEachRemaining = new ArrayList();
+        ArrayList tryAdvanced = new ArrayList();
+        ArrayList spliterated = new ArrayList();
+        ArrayList splitonced = new ArrayList();
+        ArrayList forEached = new ArrayList();
+        ArrayList streamForEached = new ArrayList();
+        ConcurrentLinkedQueue parallelStreamForEached = new ConcurrentLinkedQueue();
+        ArrayList removeIfed = new ArrayList();
+        for (Object x : c) iterated.add(x);
+        c.iterator().forEachRemaining(iteratedForEachRemaining::add);
+        for (Spliterator s = c.spliterator();
+             s.tryAdvance(tryAdvanced::add); ) {}
+        c.spliterator().forEachRemaining(spliterated::add);
+        {                       // trySplit returns "strict prefix"
+            Spliterator s1 = c.spliterator(), s2 = s1.trySplit();
+            if (s2 != null) s2.forEachRemaining(splitonced::add);
+            s1.forEachRemaining(splitonced::add);
+        }
+        c.forEach(forEached::add);
+        c.stream().forEach(streamForEached::add);
+        c.parallelStream().forEach(parallelStreamForEached::add);
+        c.removeIf(e -> { removeIfed.add(e); return false; });
+        boolean ordered =
+            c.spliterator().hasCharacteristics(Spliterator.ORDERED);
+        if (c instanceof List || c instanceof Deque)
+            assertTrue(ordered);
+        HashSet cset = new HashSet(c);
+        assertEquals(cset, new HashSet(parallelStreamForEached));
+        if (ordered) {
+            assertEquals(iterated, iteratedForEachRemaining);
+            assertEquals(iterated, tryAdvanced);
+            assertEquals(iterated, spliterated);
+            assertEquals(iterated, splitonced);
+            assertEquals(iterated, forEached);
+            assertEquals(iterated, streamForEached);
+            assertEquals(iterated, removeIfed);
+        } else {
+            assertEquals(cset, new HashSet(iterated));
+            assertEquals(cset, new HashSet(iteratedForEachRemaining));
+            assertEquals(cset, new HashSet(tryAdvanced));
+            assertEquals(cset, new HashSet(spliterated));
+            assertEquals(cset, new HashSet(splitonced));
+            assertEquals(cset, new HashSet(forEached));
+            assertEquals(cset, new HashSet(streamForEached));
+            assertEquals(cset, new HashSet(removeIfed));
+        }
+        if (c instanceof Deque) {
+            Deque d = (Deque) c;
+            ArrayList descending = new ArrayList();
+            ArrayList descendingForEachRemaining = new ArrayList();
+            for (Iterator it = d.descendingIterator(); it.hasNext(); )
+                descending.add(it.next());
+            d.descendingIterator().forEachRemaining(
+                e -> descendingForEachRemaining.add(e));
+            Collections.reverse(descending);
+            Collections.reverse(descendingForEachRemaining);
+            assertEquals(iterated, descending);
+            assertEquals(iterated, descendingForEachRemaining);
+        }
+    }
+
+    /**
+     * Iterator.forEachRemaining has same behavior as Iterator's
+     * default implementation.
+     */
+    public void testForEachRemainingConsistentWithDefaultImplementation() {
+        Collection c = impl.emptyCollection();
+        if (!testImplementationDetails
+            || c.getClass() == java.util.LinkedList.class)
+            return;
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        int n = 1 + rnd.nextInt(3);
+        for (int i = 0; i < n; i++) c.add(impl.makeElement(i));
+        ArrayList iterated = new ArrayList();
+        ArrayList iteratedForEachRemaining = new ArrayList();
+        Iterator it1 = c.iterator();
+        Iterator it2 = c.iterator();
+        assertTrue(it1.hasNext());
+        assertTrue(it2.hasNext());
+        c.clear();
+        Object r1, r2;
+        try {
+            while (it1.hasNext()) iterated.add(it1.next());
+            r1 = iterated;
+        } catch (ConcurrentModificationException ex) {
+            r1 = ConcurrentModificationException.class;
+            assertFalse(impl.isConcurrent());
+        }
+        try {
+            it2.forEachRemaining(iteratedForEachRemaining::add);
+            r2 = iteratedForEachRemaining;
+        } catch (ConcurrentModificationException ex) {
+            r2 = ConcurrentModificationException.class;
+            assertFalse(impl.isConcurrent());
+        }
+        assertEquals(r1, r2);
+    }
+
+    /**
+     * Calling Iterator#remove() after Iterator#forEachRemaining
+     * should (maybe) remove last element
+     */
+    public void testRemoveAfterForEachRemaining() {
+        Collection c = impl.emptyCollection();
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        testCollection: {
+            int n = 3 + rnd.nextInt(2);
+            for (int i = 0; i < n; i++) c.add(impl.makeElement(i));
+            Iterator it = c.iterator();
+            assertTrue(it.hasNext());
+            assertEquals(impl.makeElement(0), it.next());
+            assertTrue(it.hasNext());
+            assertEquals(impl.makeElement(1), it.next());
+            it.forEachRemaining(e -> assertTrue(c.contains(e)));
+            if (testImplementationDetails) {
+                if (c instanceof java.util.concurrent.ArrayBlockingQueue) {
+                    assertIteratorExhausted(it);
+                } else {
+                    try { it.remove(); }
+                    catch (UnsupportedOperationException ok) {
+                        break testCollection;
+                    }
+                    assertEquals(n - 1, c.size());
+                    for (int i = 0; i < n - 1; i++)
+                        assertTrue(c.contains(impl.makeElement(i)));
+                    assertFalse(c.contains(impl.makeElement(n - 1)));
+                }
+            }
+        }
+        if (c instanceof Deque) {
+            Deque d = (Deque) impl.emptyCollection();
+            int n = 3 + rnd.nextInt(2);
+            for (int i = 0; i < n; i++) d.add(impl.makeElement(i));
+            Iterator it = d.descendingIterator();
+            assertTrue(it.hasNext());
+            assertEquals(impl.makeElement(n - 1), it.next());
+            assertTrue(it.hasNext());
+            assertEquals(impl.makeElement(n - 2), it.next());
+            it.forEachRemaining(e -> assertTrue(c.contains(e)));
+            if (testImplementationDetails) {
+                it.remove();
+                assertEquals(n - 1, d.size());
+                for (int i = 1; i < n; i++)
+                    assertTrue(d.contains(impl.makeElement(i)));
+                assertFalse(d.contains(impl.makeElement(0)));
+            }
+        }
+    }
+
+    /**
+     * stream().forEach returns elements in the collection
+     */
+    public void testStreamForEach() throws Throwable {
+        final Collection c = impl.emptyCollection();
+        final AtomicLong count = new AtomicLong(0L);
+        final Object x = impl.makeElement(1);
+        final Object y = impl.makeElement(2);
+        final ArrayList found = new ArrayList();
+        Consumer<Object> spy = o -> found.add(o);
+        c.stream().forEach(spy);
+        assertTrue(found.isEmpty());
+
+        assertTrue(c.add(x));
+        c.stream().forEach(spy);
+        assertEquals(Collections.singletonList(x), found);
+        found.clear();
+
+        assertTrue(c.add(y));
+        c.stream().forEach(spy);
+        assertEquals(2, found.size());
+        assertTrue(found.contains(x));
+        assertTrue(found.contains(y));
+        found.clear();
+
+        c.clear();
+        c.stream().forEach(spy);
+        assertTrue(found.isEmpty());
+    }
+
+    public void testStreamForEachConcurrentStressTest() throws Throwable {
+        if (!impl.isConcurrent()) return;
+        final Collection c = impl.emptyCollection();
+        final long testDurationMillis = timeoutMillis();
+        final AtomicBoolean done = new AtomicBoolean(false);
+        final Object elt = impl.makeElement(1);
+        final Future<?> f1, f2;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        try (PoolCleaner cleaner = cleaner(pool, done)) {
+            final CountDownLatch threadsStarted = new CountDownLatch(2);
+            Runnable checkElt = () -> {
+                threadsStarted.countDown();
+                while (!done.get())
+                    c.stream().forEach(x -> assertSame(x, elt)); };
+            Runnable addRemove = () -> {
+                threadsStarted.countDown();
+                while (!done.get()) {
+                    assertTrue(c.add(elt));
+                    assertTrue(c.remove(elt));
+                }};
+            f1 = pool.submit(checkElt);
+            f2 = pool.submit(addRemove);
+            Thread.sleep(testDurationMillis);
+        }
+        assertNull(f1.get(0L, MILLISECONDS));
+        assertNull(f2.get(0L, MILLISECONDS));
+    }
+
+    /**
+     * collection.forEach returns elements in the collection
+     */
+    public void testForEach() throws Throwable {
+        final Collection c = impl.emptyCollection();
+        final AtomicLong count = new AtomicLong(0L);
+        final Object x = impl.makeElement(1);
+        final Object y = impl.makeElement(2);
+        final ArrayList found = new ArrayList();
+        Consumer<Object> spy = o -> found.add(o);
+        c.forEach(spy);
+        assertTrue(found.isEmpty());
+
+        assertTrue(c.add(x));
+        c.forEach(spy);
+        assertEquals(Collections.singletonList(x), found);
+        found.clear();
+
+        assertTrue(c.add(y));
+        c.forEach(spy);
+        assertEquals(2, found.size());
+        assertTrue(found.contains(x));
+        assertTrue(found.contains(y));
+        found.clear();
+
+        c.clear();
+        c.forEach(spy);
+        assertTrue(found.isEmpty());
+    }
+
+    /** TODO: promote to a common utility */
+    static <T> T chooseOne(T ... ts) {
+        return ts[ThreadLocalRandom.current().nextInt(ts.length)];
+    }
+
+    /** TODO: more random adders and removers */
+    static <E> Runnable adderRemover(Collection<E> c, E e) {
+        return chooseOne(
+            () -> {
+                assertTrue(c.add(e));
+                assertTrue(c.contains(e));
+                assertTrue(c.remove(e));
+                assertFalse(c.contains(e));
+            },
+            () -> {
+                assertTrue(c.add(e));
+                assertTrue(c.contains(e));
+                assertTrue(c.removeIf(x -> x == e));
+                assertFalse(c.contains(e));
+            },
+            () -> {
+                assertTrue(c.add(e));
+                assertTrue(c.contains(e));
+                for (Iterator it = c.iterator();; )
+                    if (it.next() == e) {
+                        try { it.remove(); }
+                        catch (UnsupportedOperationException ok) {
+                            c.remove(e);
+                        }
+                        break;
+                    }
+                assertFalse(c.contains(e));
+            });
+    }
+
+    /**
+     * Concurrent Spliterators, once exhausted, stay exhausted.
+     */
+    public void testStickySpliteratorExhaustion() throws Throwable {
+        if (!impl.isConcurrent()) return;
+        if (!testImplementationDetails) return;
+        final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        final Consumer alwaysThrows = e -> { throw new AssertionError(); };
+        final Collection c = impl.emptyCollection();
+        final Spliterator s = c.spliterator();
+        if (rnd.nextBoolean()) {
+            assertFalse(s.tryAdvance(alwaysThrows));
+        } else {
+            s.forEachRemaining(alwaysThrows);
+        }
+        final Object one = impl.makeElement(1);
+        // Spliterator should not notice added element
+        c.add(one);
+        if (rnd.nextBoolean()) {
+            assertFalse(s.tryAdvance(alwaysThrows));
+        } else {
+            s.forEachRemaining(alwaysThrows);
+        }
+    }
+
+    /**
+     * Motley crew of threads concurrently randomly hammer the collection.
+     */
+    public void testDetectRaces() throws Throwable {
+        if (!impl.isConcurrent()) return;
+        final ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        final Collection c = impl.emptyCollection();
+        final long testDurationMillis
+            = expensiveTests ? LONG_DELAY_MS : timeoutMillis();
+        final AtomicBoolean done = new AtomicBoolean(false);
+        final Object one = impl.makeElement(1);
+        final Object two = impl.makeElement(2);
+        final Consumer checkSanity = x -> assertTrue(x == one || x == two);
+        final Consumer<Object[]> checkArraySanity = array -> {
+            // assertTrue(array.length <= 2); // duplicates are permitted
+            for (Object x : array) assertTrue(x == one || x == two);
+        };
+        final Object[] emptyArray =
+            (Object[]) java.lang.reflect.Array.newInstance(one.getClass(), 0);
+        final List<Future<?>> futures;
+        final Phaser threadsStarted = new Phaser(1); // register this thread
+        final Runnable[] frobbers = {
+            () -> c.forEach(checkSanity),
+            () -> c.stream().forEach(checkSanity),
+            () -> c.parallelStream().forEach(checkSanity),
+            () -> c.spliterator().trySplit(),
+            () -> {
+                Spliterator s = c.spliterator();
+                s.tryAdvance(checkSanity);
+                s.trySplit();
+            },
+            () -> {
+                Spliterator s = c.spliterator();
+                do {} while (s.tryAdvance(checkSanity));
+            },
+            () -> { for (Object x : c) checkSanity.accept(x); },
+            () -> checkArraySanity.accept(c.toArray()),
+            () -> checkArraySanity.accept(c.toArray(emptyArray)),
+            () -> {
+                Object[] a = new Object[5];
+                Object three = impl.makeElement(3);
+                Arrays.fill(a, 0, a.length, three);
+                Object[] x = c.toArray(a);
+                if (x == a)
+                    for (int i = 0; i < a.length && a[i] != null; i++)
+                        checkSanity.accept(a[i]);
+                    // A careful reading of the spec does not support:
+                    // for (i++; i < a.length; i++) assertSame(three, a[i]);
+                else
+                    checkArraySanity.accept(x);
+                },
+            adderRemover(c, one),
+            adderRemover(c, two),
+        };
+        final List<Runnable> tasks =
+            Arrays.stream(frobbers)
+            .filter(task -> rnd.nextBoolean()) // random subset
+            .map(task -> (Runnable) () -> {
+                     threadsStarted.arriveAndAwaitAdvance();
+                     while (!done.get())
+                         task.run();
+                 })
+            .collect(Collectors.toList());
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        try (PoolCleaner cleaner = cleaner(pool, done)) {
+            threadsStarted.bulkRegister(tasks.size());
+            futures = tasks.stream()
+                .map(pool::submit)
+                .collect(Collectors.toList());
+            threadsStarted.arriveAndDeregister();
+            Thread.sleep(testDurationMillis);
+        }
+        for (Future future : futures)
+            assertNull(future.get(0L, MILLISECONDS));
+    }
+
+    /**
+     * Spliterators are either IMMUTABLE or truly late-binding or, if
+     * concurrent, use the same "late-binding style" of returning
+     * elements added between creation and first use.
+     */
+    public void testLateBindingStyle() {
+        if (!testImplementationDetails) return;
+        if (impl.klazz() == ArrayList.class) return; // for jdk8
+        // Immutable (snapshot) spliterators are exempt
+        if (impl.emptyCollection().spliterator()
+            .hasCharacteristics(Spliterator.IMMUTABLE))
+            return;
+        final Object one = impl.makeElement(1);
+        {
+            final Collection c = impl.emptyCollection();
+            final Spliterator split = c.spliterator();
+            c.add(one);
+            assertTrue(split.tryAdvance(e -> { assertSame(e, one); }));
+            assertFalse(split.tryAdvance(e -> { throw new AssertionError(); }));
+            assertTrue(c.contains(one));
+        }
+        {
+            final AtomicLong count = new AtomicLong(0);
+            final Collection c = impl.emptyCollection();
+            final Spliterator split = c.spliterator();
+            c.add(one);
+            split.forEachRemaining(
+                e -> { assertSame(e, one); count.getAndIncrement(); });
+            assertEquals(1L, count.get());
+            assertFalse(split.tryAdvance(e -> { throw new AssertionError(); }));
+            assertTrue(c.contains(one));
+        }
+    }
+
+    /**
+     * Spliterator.getComparator throws IllegalStateException iff the
+     * spliterator does not report SORTED.
+     */
+    public void testGetComparator_IllegalStateException() {
+        Collection c = impl.emptyCollection();
+        Spliterator s = c.spliterator();
+        boolean reportsSorted = s.hasCharacteristics(Spliterator.SORTED);
+        try {
+            s.getComparator();
+            assertTrue(reportsSorted);
+        } catch (IllegalStateException ex) {
+            assertFalse(reportsSorted);
+        }
+    }
+
+//     public void testCollection8DebugFail() {
+//         fail(impl.klazz().getSimpleName());
+//     }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CollectionImplementation.java b/ojluni/src/test/java/util/concurrent/tck/CollectionImplementation.java
new file mode 100644
index 0000000..fc3b3be
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CollectionImplementation.java
@@ -0,0 +1,47 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Collection;
+
+/** Allows tests to work with different Collection implementations. */
+public interface CollectionImplementation {
+    /** Returns the Collection class. */
+    public Class<?> klazz();
+    /** Returns an empty collection. */
+    public Collection emptyCollection();
+    public Object makeElement(int i);
+    public boolean isConcurrent();
+    public boolean permitsNulls();
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CollectionTest.java b/ojluni/src/test/java/util/concurrent/tck/CollectionTest.java
new file mode 100644
index 0000000..2f3943a
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CollectionTest.java
@@ -0,0 +1,63 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import junit.framework.Test;
+
+/**
+ * Contains tests applicable to all Collection implementations.
+ */
+public class CollectionTest extends JSR166TestCase {
+    final CollectionImplementation impl;
+
+    /** Tests are parameterized by a Collection implementation. */
+    CollectionTest(CollectionImplementation impl, String methodName) {
+        super(methodName);
+        this.impl = impl;
+    }
+
+    public static Test testSuite(CollectionImplementation impl) {
+        return newTestSuite
+            (parameterizedTestSuite(CollectionTest.class,
+                                    CollectionImplementation.class,
+                                    impl),
+             jdk8ParameterizedTestSuite(CollectionTest.class,
+                                        CollectionImplementation.class,
+                                        impl));
+    }
+
+//     public void testCollectionDebugFail() {
+//         fail(impl.klazz().getSimpleName());
+//     }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CompletableFutureTest.java b/ojluni/src/test/java/util/concurrent/tck/CompletableFutureTest.java
new file mode 100644
index 0000000..e97a0ab
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CompletableFutureTest.java
@@ -0,0 +1,4513 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static java.util.concurrent.CompletableFuture.failedFuture;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CompletableFutureTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CompletableFutureTest.class);
+    }
+
+    static class CFException extends RuntimeException {}
+
+    void checkIncomplete(CompletableFuture<?> f) {
+        assertFalse(f.isDone());
+        assertFalse(f.isCancelled());
+        assertTrue(f.toString().contains("Not completed"));
+        try {
+            assertNull(f.getNow(null));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            f.get(0L, SECONDS);
+            shouldThrow();
+        }
+        catch (TimeoutException success) {}
+        catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(CompletableFuture<T> f, T value) {
+        checkTimedGet(f, value);
+
+        try {
+            assertEquals(value, f.join());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertEquals(value, f.getNow(null));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertEquals(value, f.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        assertTrue(f.isDone());
+        assertFalse(f.isCancelled());
+        assertFalse(f.isCompletedExceptionally());
+        assertTrue(f.toString().contains("[Completed normally]"));
+    }
+
+    /**
+     * Returns the "raw" internal exceptional completion of f,
+     * without any additional wrapping with CompletionException.
+     */
+    Throwable exceptionalCompletion(CompletableFuture<?> f) {
+        // handle (and whenComplete and exceptionally) can distinguish
+        // between "direct" and "wrapped" exceptional completion
+        return f.handle((u, t) -> t).join();
+    }
+
+    void checkCompletedExceptionally(CompletableFuture<?> f,
+                                     boolean wrapped,
+                                     Consumer<Throwable> checker) {
+        Throwable cause = exceptionalCompletion(f);
+        if (wrapped) {
+            assertTrue(cause instanceof CompletionException);
+            cause = cause.getCause();
+        }
+        checker.accept(cause);
+
+        long startTime = System.nanoTime();
+        try {
+            f.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CompletionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.getNow(null);
+            shouldThrow();
+        } catch (CompletionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(cause, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        assertFalse(f.isCancelled());
+        assertTrue(f.isDone());
+        assertTrue(f.isCompletedExceptionally());
+        assertTrue(f.toString().contains("[Completed exceptionally]"));
+    }
+
+    void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
+        checkCompletedExceptionally(f, true,
+            t -> assertTrue(t instanceof CFException));
+    }
+
+    void checkCompletedWithWrappedCancellationException(CompletableFuture<?> f) {
+        checkCompletedExceptionally(f, true,
+            t -> assertTrue(t instanceof CancellationException));
+    }
+
+    void checkCompletedWithTimeoutException(CompletableFuture<?> f) {
+        checkCompletedExceptionally(f, false,
+            t -> assertTrue(t instanceof TimeoutException));
+    }
+
+    void checkCompletedWithWrappedException(CompletableFuture<?> f,
+                                            Throwable ex) {
+        checkCompletedExceptionally(f, true, t -> assertSame(t, ex));
+    }
+
+    void checkCompletedExceptionally(CompletableFuture<?> f, Throwable ex) {
+        checkCompletedExceptionally(f, false, t -> assertSame(t, ex));
+    }
+
+    void checkCancelled(CompletableFuture<?> f) {
+        long startTime = System.nanoTime();
+        try {
+            f.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CancellationException success) {}
+        try {
+            f.getNow(null);
+            shouldThrow();
+        } catch (CancellationException success) {}
+        try {
+            f.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        assertTrue(exceptionalCompletion(f) instanceof CancellationException);
+
+        assertTrue(f.isDone());
+        assertTrue(f.isCompletedExceptionally());
+        assertTrue(f.isCancelled());
+        assertTrue(f.toString().contains("[Completed exceptionally]"));
+    }
+
+    /**
+     * A newly constructed CompletableFuture is incomplete, as indicated
+     * by methods isDone, isCancelled, and getNow
+     */
+    public void testConstructor() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+    }
+
+    /**
+     * complete completes normally, as indicated by methods isDone,
+     * isCancelled, join, get, and getNow
+     */
+    public void testComplete() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+        assertTrue(f.complete(v1));
+        assertFalse(f.complete(v1));
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * completeExceptionally completes exceptionally, as indicated by
+     * methods isDone, isCancelled, join, get, and getNow
+     */
+    public void testCompleteExceptionally() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CFException ex = new CFException();
+        checkIncomplete(f);
+        f.completeExceptionally(ex);
+        checkCompletedExceptionally(f, ex);
+    }
+
+    /**
+     * cancel completes exceptionally and reports cancelled, as indicated by
+     * methods isDone, isCancelled, join, get, and getNow
+     */
+    public void testCancel() {
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        assertTrue(f.cancel(!mayInterruptIfRunning));
+        checkCancelled(f);
+    }}
+
+    /**
+     * obtrudeValue forces completion with given value
+     */
+    public void testObtrudeValue() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        checkIncomplete(f);
+        assertTrue(f.complete(one));
+        checkCompletedNormally(f, one);
+        f.obtrudeValue(three);
+        checkCompletedNormally(f, three);
+        f.obtrudeValue(two);
+        checkCompletedNormally(f, two);
+        f = new CompletableFuture<>();
+        f.obtrudeValue(three);
+        checkCompletedNormally(f, three);
+        f.obtrudeValue(null);
+        checkCompletedNormally(f, null);
+        f = new CompletableFuture<>();
+        f.completeExceptionally(new CFException());
+        f.obtrudeValue(four);
+        checkCompletedNormally(f, four);
+    }
+
+    /**
+     * obtrudeException forces completion with given exception
+     */
+    public void testObtrudeException() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CFException ex;
+        CompletableFuture<Integer> f;
+
+        f = new CompletableFuture<>();
+        assertTrue(f.complete(v1));
+        for (int i = 0; i < 2; i++) {
+            f.obtrudeException(ex = new CFException());
+            checkCompletedExceptionally(f, ex);
+        }
+
+        f = new CompletableFuture<>();
+        for (int i = 0; i < 2; i++) {
+            f.obtrudeException(ex = new CFException());
+            checkCompletedExceptionally(f, ex);
+        }
+
+        f = new CompletableFuture<>();
+        f.completeExceptionally(ex = new CFException());
+        f.obtrudeValue(v1);
+        checkCompletedNormally(f, v1);
+        f.obtrudeException(ex = new CFException());
+        checkCompletedExceptionally(f, ex);
+        f.completeExceptionally(new CFException());
+        checkCompletedExceptionally(f, ex);
+        assertFalse(f.complete(v1));
+        checkCompletedExceptionally(f, ex);
+    }}
+
+    /**
+     * getNumberOfDependents returns number of dependent tasks
+     */
+    public void testGetNumberOfDependents() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        assertEquals(0, f.getNumberOfDependents());
+        final CompletableFuture<Void> g = m.thenRun(f, new Noop(m));
+        assertEquals(1, f.getNumberOfDependents());
+        assertEquals(0, g.getNumberOfDependents());
+        final CompletableFuture<Void> h = m.thenRun(f, new Noop(m));
+        assertEquals(2, f.getNumberOfDependents());
+        assertEquals(0, h.getNumberOfDependents());
+        assertTrue(f.complete(v1));
+        checkCompletedNormally(g, null);
+        checkCompletedNormally(h, null);
+        assertEquals(0, f.getNumberOfDependents());
+        assertEquals(0, g.getNumberOfDependents());
+        assertEquals(0, h.getNumberOfDependents());
+    }}
+
+    /**
+     * toString indicates current completion state
+     */
+    public void testToString() {
+        CompletableFuture<String> f;
+
+        f = new CompletableFuture<String>();
+        assertTrue(f.toString().contains("[Not completed]"));
+
+        assertTrue(f.complete("foo"));
+        assertTrue(f.toString().contains("[Completed normally]"));
+
+        f = new CompletableFuture<String>();
+        assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
+        assertTrue(f.toString().contains("[Completed exceptionally]"));
+
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            f = new CompletableFuture<String>();
+            assertTrue(f.cancel(mayInterruptIfRunning));
+            assertTrue(f.toString().contains("[Completed exceptionally]"));
+        }
+    }
+
+    /**
+     * completedFuture returns a completed CompletableFuture with given value
+     */
+    public void testCompletedFuture() {
+        CompletableFuture<String> f = CompletableFuture.completedFuture("test");
+        checkCompletedNormally(f, "test");
+    }
+
+    abstract static class CheckedAction {
+        int invocationCount = 0;
+        final ExecutionMode m;
+        CheckedAction(ExecutionMode m) { this.m = m; }
+        void invoked() {
+            m.checkExecutionMode();
+            assertEquals(0, invocationCount++);
+        }
+        void assertNotInvoked() { assertEquals(0, invocationCount); }
+        void assertInvoked() { assertEquals(1, invocationCount); }
+    }
+
+    abstract static class CheckedIntegerAction extends CheckedAction {
+        Integer value;
+        CheckedIntegerAction(ExecutionMode m) { super(m); }
+        void assertValue(Integer expected) {
+            assertInvoked();
+            assertEquals(expected, value);
+        }
+    }
+
+    static class IntegerSupplier extends CheckedAction
+        implements Supplier<Integer>
+    {
+        final Integer value;
+        IntegerSupplier(ExecutionMode m, Integer value) {
+            super(m);
+            this.value = value;
+        }
+        public Integer get() {
+            invoked();
+            return value;
+        }
+    }
+
+    // A function that handles and produces null values as well.
+    static Integer inc(Integer x) {
+        return (x == null) ? null : x + 1;
+    }
+
+    static class NoopConsumer extends CheckedIntegerAction
+        implements Consumer<Integer>
+    {
+        NoopConsumer(ExecutionMode m) { super(m); }
+        public void accept(Integer x) {
+            invoked();
+            value = x;
+        }
+    }
+
+    static class IncFunction extends CheckedIntegerAction
+        implements Function<Integer,Integer>
+    {
+        IncFunction(ExecutionMode m) { super(m); }
+        public Integer apply(Integer x) {
+            invoked();
+            return value = inc(x);
+        }
+    }
+
+    // Choose non-commutative actions for better coverage
+    // A non-commutative function that handles and produces null values as well.
+    static Integer subtract(Integer x, Integer y) {
+        return (x == null && y == null) ? null :
+            ((x == null) ? 42 : x.intValue())
+            - ((y == null) ? 99 : y.intValue());
+    }
+
+    static class SubtractAction extends CheckedIntegerAction
+        implements BiConsumer<Integer, Integer>
+    {
+        SubtractAction(ExecutionMode m) { super(m); }
+        public void accept(Integer x, Integer y) {
+            invoked();
+            value = subtract(x, y);
+        }
+    }
+
+    static class SubtractFunction extends CheckedIntegerAction
+        implements BiFunction<Integer, Integer, Integer>
+    {
+        SubtractFunction(ExecutionMode m) { super(m); }
+        public Integer apply(Integer x, Integer y) {
+            invoked();
+            return value = subtract(x, y);
+        }
+    }
+
+    static class Noop extends CheckedAction implements Runnable {
+        Noop(ExecutionMode m) { super(m); }
+        public void run() {
+            invoked();
+        }
+    }
+
+    static class FailingSupplier extends CheckedAction
+        implements Supplier<Integer>
+    {
+        final CFException ex;
+        FailingSupplier(ExecutionMode m) { super(m); ex = new CFException(); }
+        public Integer get() {
+            invoked();
+            throw ex;
+        }
+    }
+
+    static class FailingConsumer extends CheckedIntegerAction
+        implements Consumer<Integer>
+    {
+        final CFException ex;
+        FailingConsumer(ExecutionMode m) { super(m); ex = new CFException(); }
+        public void accept(Integer x) {
+            invoked();
+            value = x;
+            throw ex;
+        }
+    }
+
+    static class FailingBiConsumer extends CheckedIntegerAction
+        implements BiConsumer<Integer, Integer>
+    {
+        final CFException ex;
+        FailingBiConsumer(ExecutionMode m) { super(m); ex = new CFException(); }
+        public void accept(Integer x, Integer y) {
+            invoked();
+            value = subtract(x, y);
+            throw ex;
+        }
+    }
+
+    static class FailingFunction extends CheckedIntegerAction
+        implements Function<Integer, Integer>
+    {
+        final CFException ex;
+        FailingFunction(ExecutionMode m) { super(m); ex = new CFException(); }
+        public Integer apply(Integer x) {
+            invoked();
+            value = x;
+            throw ex;
+        }
+    }
+
+    static class FailingBiFunction extends CheckedIntegerAction
+        implements BiFunction<Integer, Integer, Integer>
+    {
+        final CFException ex;
+        FailingBiFunction(ExecutionMode m) { super(m); ex = new CFException(); }
+        public Integer apply(Integer x, Integer y) {
+            invoked();
+            value = subtract(x, y);
+            throw ex;
+        }
+    }
+
+    static class FailingRunnable extends CheckedAction implements Runnable {
+        final CFException ex;
+        FailingRunnable(ExecutionMode m) { super(m); ex = new CFException(); }
+        public void run() {
+            invoked();
+            throw ex;
+        }
+    }
+
+    static class CompletableFutureInc extends CheckedIntegerAction
+        implements Function<Integer, CompletableFuture<Integer>>
+    {
+        CompletableFutureInc(ExecutionMode m) { super(m); }
+        public CompletableFuture<Integer> apply(Integer x) {
+            invoked();
+            value = x;
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            assertTrue(f.complete(inc(x)));
+            return f;
+        }
+    }
+
+    static class FailingCompletableFutureFunction extends CheckedIntegerAction
+        implements Function<Integer, CompletableFuture<Integer>>
+    {
+        final CFException ex;
+        FailingCompletableFutureFunction(ExecutionMode m) { super(m); ex = new CFException(); }
+        public CompletableFuture<Integer> apply(Integer x) {
+            invoked();
+            value = x;
+            throw ex;
+        }
+    }
+
+    static class CountingRejectingExecutor implements Executor {
+        final RejectedExecutionException ex = new RejectedExecutionException();
+        final AtomicInteger count = new AtomicInteger(0);
+        public void execute(Runnable r) {
+            count.getAndIncrement();
+            throw ex;
+        }
+    }
+
+    // Used for explicit executor tests
+    static final class ThreadExecutor implements Executor {
+        final AtomicInteger count = new AtomicInteger(0);
+        static final ThreadGroup tg = new ThreadGroup("ThreadExecutor");
+        static boolean startedCurrentThread() {
+            return Thread.currentThread().getThreadGroup() == tg;
+        }
+
+        public void execute(Runnable r) {
+            count.getAndIncrement();
+            new Thread(tg, r).start();
+        }
+    }
+
+    static final boolean defaultExecutorIsCommonPool
+        = ForkJoinPool.getCommonPoolParallelism() > 1;
+
+    /**
+     * Permits the testing of parallel code for the 3 different
+     * execution modes without copy/pasting all the test methods.
+     */
+    enum ExecutionMode {
+        SYNC {
+            public void checkExecutionMode() {
+                assertFalse(ThreadExecutor.startedCurrentThread());
+                assertNull(ForkJoinTask.getPool());
+            }
+            public CompletableFuture<Void> runAsync(Runnable a) {
+                throw new UnsupportedOperationException();
+            }
+            public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+                throw new UnsupportedOperationException();
+            }
+            public <T> CompletableFuture<Void> thenRun
+                (CompletableFuture<T> f, Runnable a) {
+                return f.thenRun(a);
+            }
+            public <T> CompletableFuture<Void> thenAccept
+                (CompletableFuture<T> f, Consumer<? super T> a) {
+                return f.thenAccept(a);
+            }
+            public <T,U> CompletableFuture<U> thenApply
+                (CompletableFuture<T> f, Function<? super T,U> a) {
+                return f.thenApply(a);
+            }
+            public <T,U> CompletableFuture<U> thenCompose
+                (CompletableFuture<T> f,
+                 Function<? super T,? extends CompletionStage<U>> a) {
+                return f.thenCompose(a);
+            }
+            public <T,U> CompletableFuture<U> handle
+                (CompletableFuture<T> f,
+                 BiFunction<? super T,Throwable,? extends U> a) {
+                return f.handle(a);
+            }
+            public <T> CompletableFuture<T> whenComplete
+                (CompletableFuture<T> f,
+                 BiConsumer<? super T,? super Throwable> a) {
+                return f.whenComplete(a);
+            }
+            public <T,U> CompletableFuture<Void> runAfterBoth
+                (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+                return f.runAfterBoth(g, a);
+            }
+            public <T,U> CompletableFuture<Void> thenAcceptBoth
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiConsumer<? super T,? super U> a) {
+                return f.thenAcceptBoth(g, a);
+            }
+            public <T,U,V> CompletableFuture<V> thenCombine
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiFunction<? super T,? super U,? extends V> a) {
+                return f.thenCombine(g, a);
+            }
+            public <T> CompletableFuture<Void> runAfterEither
+                (CompletableFuture<T> f,
+                 CompletionStage<?> g,
+                 java.lang.Runnable a) {
+                return f.runAfterEither(g, a);
+            }
+            public <T> CompletableFuture<Void> acceptEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Consumer<? super T> a) {
+                return f.acceptEither(g, a);
+            }
+            public <T,U> CompletableFuture<U> applyToEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Function<? super T,U> a) {
+                return f.applyToEither(g, a);
+            }
+        },
+
+        ASYNC {
+            public void checkExecutionMode() {
+                assertEquals(defaultExecutorIsCommonPool,
+                             (ForkJoinPool.commonPool() == ForkJoinTask.getPool()));
+            }
+            public CompletableFuture<Void> runAsync(Runnable a) {
+                return CompletableFuture.runAsync(a);
+            }
+            public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+                return CompletableFuture.supplyAsync(a);
+            }
+            public <T> CompletableFuture<Void> thenRun
+                (CompletableFuture<T> f, Runnable a) {
+                return f.thenRunAsync(a);
+            }
+            public <T> CompletableFuture<Void> thenAccept
+                (CompletableFuture<T> f, Consumer<? super T> a) {
+                return f.thenAcceptAsync(a);
+            }
+            public <T,U> CompletableFuture<U> thenApply
+                (CompletableFuture<T> f, Function<? super T,U> a) {
+                return f.thenApplyAsync(a);
+            }
+            public <T,U> CompletableFuture<U> thenCompose
+                (CompletableFuture<T> f,
+                 Function<? super T,? extends CompletionStage<U>> a) {
+                return f.thenComposeAsync(a);
+            }
+            public <T,U> CompletableFuture<U> handle
+                (CompletableFuture<T> f,
+                 BiFunction<? super T,Throwable,? extends U> a) {
+                return f.handleAsync(a);
+            }
+            public <T> CompletableFuture<T> whenComplete
+                (CompletableFuture<T> f,
+                 BiConsumer<? super T,? super Throwable> a) {
+                return f.whenCompleteAsync(a);
+            }
+            public <T,U> CompletableFuture<Void> runAfterBoth
+                (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+                return f.runAfterBothAsync(g, a);
+            }
+            public <T,U> CompletableFuture<Void> thenAcceptBoth
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiConsumer<? super T,? super U> a) {
+                return f.thenAcceptBothAsync(g, a);
+            }
+            public <T,U,V> CompletableFuture<V> thenCombine
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiFunction<? super T,? super U,? extends V> a) {
+                return f.thenCombineAsync(g, a);
+            }
+            public <T> CompletableFuture<Void> runAfterEither
+                (CompletableFuture<T> f,
+                 CompletionStage<?> g,
+                 java.lang.Runnable a) {
+                return f.runAfterEitherAsync(g, a);
+            }
+            public <T> CompletableFuture<Void> acceptEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Consumer<? super T> a) {
+                return f.acceptEitherAsync(g, a);
+            }
+            public <T,U> CompletableFuture<U> applyToEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Function<? super T,U> a) {
+                return f.applyToEitherAsync(g, a);
+            }
+        },
+
+        EXECUTOR {
+            public void checkExecutionMode() {
+                assertTrue(ThreadExecutor.startedCurrentThread());
+            }
+            public CompletableFuture<Void> runAsync(Runnable a) {
+                return CompletableFuture.runAsync(a, new ThreadExecutor());
+            }
+            public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) {
+                return CompletableFuture.supplyAsync(a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> thenRun
+                (CompletableFuture<T> f, Runnable a) {
+                return f.thenRunAsync(a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> thenAccept
+                (CompletableFuture<T> f, Consumer<? super T> a) {
+                return f.thenAcceptAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> thenApply
+                (CompletableFuture<T> f, Function<? super T,U> a) {
+                return f.thenApplyAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> thenCompose
+                (CompletableFuture<T> f,
+                 Function<? super T,? extends CompletionStage<U>> a) {
+                return f.thenComposeAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> handle
+                (CompletableFuture<T> f,
+                 BiFunction<? super T,Throwable,? extends U> a) {
+                return f.handleAsync(a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<T> whenComplete
+                (CompletableFuture<T> f,
+                 BiConsumer<? super T,? super Throwable> a) {
+                return f.whenCompleteAsync(a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<Void> runAfterBoth
+                (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) {
+                return f.runAfterBothAsync(g, a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<Void> thenAcceptBoth
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiConsumer<? super T,? super U> a) {
+                return f.thenAcceptBothAsync(g, a, new ThreadExecutor());
+            }
+            public <T,U,V> CompletableFuture<V> thenCombine
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends U> g,
+                 BiFunction<? super T,? super U,? extends V> a) {
+                return f.thenCombineAsync(g, a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> runAfterEither
+                (CompletableFuture<T> f,
+                 CompletionStage<?> g,
+                 java.lang.Runnable a) {
+                return f.runAfterEitherAsync(g, a, new ThreadExecutor());
+            }
+            public <T> CompletableFuture<Void> acceptEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Consumer<? super T> a) {
+                return f.acceptEitherAsync(g, a, new ThreadExecutor());
+            }
+            public <T,U> CompletableFuture<U> applyToEither
+                (CompletableFuture<T> f,
+                 CompletionStage<? extends T> g,
+                 Function<? super T,U> a) {
+                return f.applyToEitherAsync(g, a, new ThreadExecutor());
+            }
+        };
+
+        public abstract void checkExecutionMode();
+        public abstract CompletableFuture<Void> runAsync(Runnable a);
+        public abstract <U> CompletableFuture<U> supplyAsync(Supplier<U> a);
+        public abstract <T> CompletableFuture<Void> thenRun
+            (CompletableFuture<T> f, Runnable a);
+        public abstract <T> CompletableFuture<Void> thenAccept
+            (CompletableFuture<T> f, Consumer<? super T> a);
+        public abstract <T,U> CompletableFuture<U> thenApply
+            (CompletableFuture<T> f, Function<? super T,U> a);
+        public abstract <T,U> CompletableFuture<U> thenCompose
+            (CompletableFuture<T> f,
+             Function<? super T,? extends CompletionStage<U>> a);
+        public abstract <T,U> CompletableFuture<U> handle
+            (CompletableFuture<T> f,
+             BiFunction<? super T,Throwable,? extends U> a);
+        public abstract <T> CompletableFuture<T> whenComplete
+            (CompletableFuture<T> f,
+             BiConsumer<? super T,? super Throwable> a);
+        public abstract <T,U> CompletableFuture<Void> runAfterBoth
+            (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a);
+        public abstract <T,U> CompletableFuture<Void> thenAcceptBoth
+            (CompletableFuture<T> f,
+             CompletionStage<? extends U> g,
+             BiConsumer<? super T,? super U> a);
+        public abstract <T,U,V> CompletableFuture<V> thenCombine
+            (CompletableFuture<T> f,
+             CompletionStage<? extends U> g,
+             BiFunction<? super T,? super U,? extends V> a);
+        public abstract <T> CompletableFuture<Void> runAfterEither
+            (CompletableFuture<T> f,
+             CompletionStage<?> g,
+             java.lang.Runnable a);
+        public abstract <T> CompletableFuture<Void> acceptEither
+            (CompletableFuture<T> f,
+             CompletionStage<? extends T> g,
+             Consumer<? super T> a);
+        public abstract <T,U> CompletableFuture<U> applyToEither
+            (CompletableFuture<T> f,
+             CompletionStage<? extends T> g,
+             Function<? super T,U> a);
+    }
+
+    /**
+     * exceptionally action is not invoked when source completes
+     * normally, and source result is propagated
+     */
+    public void testExceptionally_normalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = f.exceptionally
+            ((Throwable t) -> {
+                a.getAndIncrement();
+                threadFail("should not be called");
+                return null;            // unreached
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, v1);
+        checkCompletedNormally(f, v1);
+        assertEquals(0, a.get());
+    }}
+
+    /**
+     * exceptionally action completes with function value on source
+     * exception
+     */
+    public void testExceptionally_exceptionalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = f.exceptionally
+            ((Throwable t) -> {
+                ExecutionMode.SYNC.checkExecutionMode();
+                threadAssertSame(t, ex);
+                a.getAndIncrement();
+                return v1;
+            });
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedNormally(g, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If an "exceptionally action" throws an exception, it completes
+     * exceptionally with that exception
+     */
+    public void testExceptionally_exceptionalCompletionActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex1 = new CFException();
+        final CFException ex2 = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex1);
+        final CompletableFuture<Integer> g = f.exceptionally
+            ((Throwable t) -> {
+                ExecutionMode.SYNC.checkExecutionMode();
+                threadAssertSame(t, ex1);
+                a.getAndIncrement();
+                throw ex2;
+            });
+        if (createIncomplete) f.completeExceptionally(ex1);
+
+        checkCompletedWithWrappedException(g, ex2);
+        checkCompletedExceptionally(f, ex1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * whenComplete action executes on normal completion, propagating
+     * source result.
+     */
+    public void testWhenComplete_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, v1);
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * whenComplete action executes on exceptional completion, propagating
+     * source result.
+     */
+    public void testWhenComplete_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertSame(t, ex);
+                a.getAndIncrement();
+            });
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedExceptionally(f, ex);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * whenComplete action executes on cancelled source, propagating
+     * CancellationException.
+     */
+    public void testWhenComplete_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertTrue(t instanceof CancellationException);
+                a.getAndIncrement();
+            });
+        if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+
+        checkCompletedWithWrappedCancellationException(g);
+        checkCancelled(f);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a whenComplete action throws an exception when triggered by
+     * a normal completion, it completes exceptionally
+     */
+    public void testWhenComplete_sourceCompletedNormallyActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+                throw ex;
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a whenComplete action throws an exception when triggered by
+     * a source completion that also throws an exception, the source
+     * exception takes precedence (unlike handle)
+     */
+    public void testWhenComplete_sourceFailedActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex1 = new CFException();
+        final CFException ex2 = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+
+        if (!createIncomplete) f.completeExceptionally(ex1);
+        final CompletableFuture<Integer> g = m.whenComplete
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(t, ex1);
+                threadAssertNull(result);
+                a.getAndIncrement();
+                throw ex2;
+            });
+        if (createIncomplete) f.completeExceptionally(ex1);
+
+        checkCompletedWithWrappedException(g, ex1);
+        checkCompletedExceptionally(f, ex1);
+        if (testImplementationDetails) {
+            assertEquals(1, ex1.getSuppressed().length);
+            assertSame(ex2, ex1.getSuppressed()[0]);
+        }
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * handle action completes normally with function value on normal
+     * completion of source
+     */
+    public void testHandle_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+                return inc(v1);
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, inc(v1));
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * handle action completes normally with function value on
+     * exceptional completion of source
+     */
+    public void testHandle_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertSame(t, ex);
+                a.getAndIncrement();
+                return v1;
+            });
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedNormally(g, v1);
+        checkCompletedExceptionally(f, ex);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * handle action completes normally with function value on
+     * cancelled source
+     */
+    public void testHandle_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertTrue(t instanceof CancellationException);
+                a.getAndIncrement();
+                return v1;
+            });
+        if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+
+        checkCompletedNormally(g, v1);
+        checkCancelled(f);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a "handle action" throws an exception when triggered by
+     * a normal completion, it completes exceptionally
+     */
+    public void testHandle_sourceCompletedNormallyActionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex = new CFException();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertSame(result, v1);
+                threadAssertNull(t);
+                a.getAndIncrement();
+                throw ex;
+            });
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedNormally(f, v1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * If a "handle action" throws an exception when triggered by
+     * a source completion that also throws an exception, the action
+     * exception takes precedence (unlike whenComplete)
+     */
+    public void testHandle_sourceFailedActionFailed() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final AtomicInteger a = new AtomicInteger(0);
+        final CFException ex1 = new CFException();
+        final CFException ex2 = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+
+        if (!createIncomplete) f.completeExceptionally(ex1);
+        final CompletableFuture<Integer> g = m.handle
+            (f,
+             (Integer result, Throwable t) -> {
+                m.checkExecutionMode();
+                threadAssertNull(result);
+                threadAssertSame(ex1, t);
+                a.getAndIncrement();
+                throw ex2;
+            });
+        if (createIncomplete) f.completeExceptionally(ex1);
+
+        checkCompletedWithWrappedException(g, ex2);
+        checkCompletedExceptionally(f, ex1);
+        assertEquals(1, a.get());
+    }}
+
+    /**
+     * runAsync completes after running Runnable
+     */
+    public void testRunAsync_normalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+    {
+        final Noop r = new Noop(m);
+        final CompletableFuture<Void> f = m.runAsync(r);
+        assertNull(f.join());
+        checkCompletedNormally(f, null);
+        r.assertInvoked();
+    }}
+
+    /**
+     * failing runAsync completes exceptionally after running Runnable
+     */
+    public void testRunAsync_exceptionalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+    {
+        final FailingRunnable r = new FailingRunnable(m);
+        final CompletableFuture<Void> f = m.runAsync(r);
+        checkCompletedWithWrappedException(f, r.ex);
+        r.assertInvoked();
+    }}
+
+    public void testRunAsync_rejectingExecutor() {
+        CountingRejectingExecutor e = new CountingRejectingExecutor();
+        try {
+            CompletableFuture.runAsync(() -> {}, e);
+            shouldThrow();
+        } catch (Throwable t) {
+            assertSame(e.ex, t);
+        }
+
+        assertEquals(1, e.count.get());
+    }
+
+    /**
+     * supplyAsync completes with result of supplier
+     */
+    public void testSupplyAsync_normalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final IntegerSupplier r = new IntegerSupplier(m, v1);
+        final CompletableFuture<Integer> f = m.supplyAsync(r);
+        assertSame(v1, f.join());
+        checkCompletedNormally(f, v1);
+        r.assertInvoked();
+    }}
+
+    /**
+     * Failing supplyAsync completes exceptionally
+     */
+    public void testSupplyAsync_exceptionalCompletion() {
+        ExecutionMode[] executionModes = {
+            ExecutionMode.ASYNC,
+            ExecutionMode.EXECUTOR,
+        };
+        for (ExecutionMode m : executionModes)
+    {
+        FailingSupplier r = new FailingSupplier(m);
+        CompletableFuture<Integer> f = m.supplyAsync(r);
+        checkCompletedWithWrappedException(f, r.ex);
+        r.assertInvoked();
+    }}
+
+    public void testSupplyAsync_rejectingExecutor() {
+        CountingRejectingExecutor e = new CountingRejectingExecutor();
+        try {
+            CompletableFuture.supplyAsync(() -> null, e);
+            shouldThrow();
+        } catch (Throwable t) {
+            assertSame(e.ex, t);
+        }
+
+        assertEquals(1, e.count.get());
+    }
+
+    // seq completion methods
+
+    /**
+     * thenRun result completes normally after normal completion of source
+     */
+    public void testThenRun_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        checkCompletedNormally(h4, null);
+        checkCompletedNormally(h5, null);
+        checkCompletedNormally(f, v1);
+        for (Noop r : rs) r.assertInvoked();
+    }}
+
+    /**
+     * thenRun result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenRun_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(f.completeExceptionally(ex));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        checkCompletedWithWrappedException(h5, ex);
+        checkCompletedExceptionally(f, ex);
+        for (Noop r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenRun result completes exceptionally if source cancelled
+     */
+    public void testThenRun_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        checkCompletedWithWrappedCancellationException(h4);
+        checkCompletedWithWrappedCancellationException(h5);
+        checkCancelled(f);
+        for (Noop r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenRun result completes exceptionally if action does
+     */
+    public void testThenRun_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingRunnable[] rs = new FailingRunnable[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m);
+
+        final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]);
+        final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
+
+        checkCompletedWithWrappedException(h0, rs[0].ex);
+        checkCompletedWithWrappedException(h1, rs[1].ex);
+        checkCompletedWithWrappedException(h2, rs[2].ex);
+        checkCompletedWithWrappedException(h3, rs[3].ex);
+        checkCompletedWithWrappedException(h4, rs[4].ex);
+        checkCompletedWithWrappedException(h5, rs[5].ex);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenApply result completes normally after normal completion of source
+     */
+    public void testThenApply_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedNormally(h0, inc(v1));
+        checkCompletedNormally(h1, inc(v1));
+        checkCompletedNormally(h2, inc(v1));
+        checkCompletedNormally(h3, inc(v1));
+        checkCompletedNormally(f, v1);
+        for (IncFunction r : rs) r.assertValue(inc(v1));
+    }}
+
+    /**
+     * thenApply result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenApply_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        assertTrue(f.completeExceptionally(ex));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedExceptionally(f, ex);
+        for (IncFunction r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenApply result completes exceptionally if source cancelled
+     */
+    public void testThenApply_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        checkCancelled(f);
+        for (IncFunction r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenApply result completes exceptionally if action does
+     */
+    public void testThenApply_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingFunction[] rs = new FailingFunction[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedException(h0, rs[0].ex);
+        checkCompletedWithWrappedException(h1, rs[1].ex);
+        checkCompletedWithWrappedException(h2, rs[2].ex);
+        checkCompletedWithWrappedException(h3, rs[3].ex);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenAccept result completes normally after normal completion of source
+     */
+    public void testThenAccept_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        checkCompletedNormally(f, v1);
+        for (NoopConsumer r : rs) r.assertValue(v1);
+    }}
+
+    /**
+     * thenAccept result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenAccept_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        assertTrue(f.completeExceptionally(ex));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedExceptionally(f, ex);
+        for (NoopConsumer r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenAccept result completes exceptionally if source cancelled
+     */
+    public void testThenAccept_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        checkCancelled(f);
+        for (NoopConsumer r : rs) r.assertNotInvoked();
+    }}
+
+    /**
+     * thenAccept result completes exceptionally if action does
+     */
+    public void testThenAccept_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingConsumer[] rs = new FailingConsumer[4];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
+
+        checkCompletedWithWrappedException(h0, rs[0].ex);
+        checkCompletedWithWrappedException(h1, rs[1].ex);
+        checkCompletedWithWrappedException(h2, rs[2].ex);
+        checkCompletedWithWrappedException(h3, rs[3].ex);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenCombine result completes normally after normal completion
+     * of sources
+     */
+    public void testThenCombine_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractFunction[] rs = new SubtractFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new SubtractFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Integer> h0 = m.thenCombine(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.thenCombine(fst, fst, rs[1]);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.thenCombine(fst, fst, rs[3]);
+        checkIncomplete(h0); rs[0].assertNotInvoked();
+        checkIncomplete(h2); rs[2].assertNotInvoked();
+        checkCompletedNormally(h1, subtract(w1, w1));
+        checkCompletedNormally(h3, subtract(w1, w1));
+        rs[1].assertValue(subtract(w1, w1));
+        rs[3].assertValue(subtract(w1, w1));
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Integer> h4 = m.thenCombine(f, g, rs[4]);
+
+        checkCompletedNormally(h0, subtract(v1, v2));
+        checkCompletedNormally(h2, subtract(v1, v2));
+        checkCompletedNormally(h4, subtract(v1, v2));
+        rs[0].assertValue(subtract(v1, v2));
+        rs[2].assertValue(subtract(v1, v2));
+        rs[4].assertValue(subtract(v1, v2));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * thenCombine result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testThenCombine_exceptionalCompletion() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final SubtractFunction r1 = new SubtractFunction(m);
+        final SubtractFunction r2 = new SubtractFunction(m);
+        final SubtractFunction r3 = new SubtractFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.completeExceptionally(ex) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.completeExceptionally(ex);
+
+        final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCompletedExceptionally(failFirst ? fst : snd, ex);
+    }}
+
+    /**
+     * thenCombine result completes exceptionally if either source cancelled
+     */
+    public void testThenCombine_sourceCancelled() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractFunction r1 = new SubtractFunction(m);
+        final SubtractFunction r2 = new SubtractFunction(m);
+        final SubtractFunction r3 = new SubtractFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.cancel(mayInterruptIfRunning) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.cancel(mayInterruptIfRunning);
+
+        final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCancelled(failFirst ? fst : snd);
+    }}
+
+    /**
+     * thenCombine result completes exceptionally if action does
+     */
+    public void testThenCombine_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingBiFunction r1 = new FailingBiFunction(m);
+        final FailingBiFunction r2 = new FailingBiFunction(m);
+        final FailingBiFunction r3 = new FailingBiFunction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2);
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, r1.ex);
+        checkCompletedWithWrappedException(h2, r2.ex);
+        checkCompletedWithWrappedException(h3, r3.ex);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * thenAcceptBoth result completes normally after normal
+     * completion of sources
+     */
+    public void testThenAcceptBoth_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractAction r1 = new SubtractAction(m);
+        final SubtractAction r2 = new SubtractAction(m);
+        final SubtractAction r3 = new SubtractAction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        r1.assertValue(subtract(v1, v2));
+        r2.assertValue(subtract(v1, v2));
+        r3.assertValue(subtract(v1, v2));
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * thenAcceptBoth result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testThenAcceptBoth_exceptionalCompletion() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final SubtractAction r1 = new SubtractAction(m);
+        final SubtractAction r2 = new SubtractAction(m);
+        final SubtractAction r3 = new SubtractAction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.completeExceptionally(ex) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.completeExceptionally(ex);
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCompletedExceptionally(failFirst ? fst : snd, ex);
+    }}
+
+    /**
+     * thenAcceptBoth result completes exceptionally if either source cancelled
+     */
+    public void testThenAcceptBoth_sourceCancelled() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final SubtractAction r1 = new SubtractAction(m);
+        final SubtractAction r2 = new SubtractAction(m);
+        final SubtractAction r3 = new SubtractAction(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.cancel(mayInterruptIfRunning) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.cancel(mayInterruptIfRunning);
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCancelled(failFirst ? fst : snd);
+    }}
+
+    /**
+     * thenAcceptBoth result completes exceptionally if action does
+     */
+    public void testThenAcceptBoth_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingBiConsumer r1 = new FailingBiConsumer(m);
+        final FailingBiConsumer r2 = new FailingBiConsumer(m);
+        final FailingBiConsumer r3 = new FailingBiConsumer(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2);
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, r1.ex);
+        checkCompletedWithWrappedException(h2, r2.ex);
+        checkCompletedWithWrappedException(h3, r3.ex);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * runAfterBoth result completes normally after normal
+     * completion of sources
+     */
+    public void testRunAfterBoth_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop r1 = new Noop(m);
+        final Noop r2 = new Noop(m);
+        final Noop r3 = new Noop(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * runAfterBoth result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testRunAfterBoth_exceptionalCompletion() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final Noop r1 = new Noop(m);
+        final Noop r2 = new Noop(m);
+        final Noop r3 = new Noop(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.completeExceptionally(ex) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.completeExceptionally(ex);
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCompletedExceptionally(failFirst ? fst : snd, ex);
+    }}
+
+    /**
+     * runAfterBoth result completes exceptionally if either source cancelled
+     */
+    public void testRunAfterBoth_sourceCancelled() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (boolean failFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop r1 = new Noop(m);
+        final Noop r2 = new Noop(m);
+        final Noop r3 = new Noop(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Callable<Boolean> complete1 = failFirst ?
+            () -> fst.cancel(mayInterruptIfRunning) :
+            () -> fst.complete(v1);
+        final Callable<Boolean> complete2 = failFirst ?
+            () -> snd.complete(v1) :
+            () -> snd.cancel(mayInterruptIfRunning);
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(complete1.call());
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        checkIncomplete(h1);
+        checkIncomplete(h2);
+        assertTrue(complete2.call());
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        r1.assertNotInvoked();
+        r2.assertNotInvoked();
+        r3.assertNotInvoked();
+        checkCompletedNormally(failFirst ? snd : fst, v1);
+        checkCancelled(failFirst ? fst : snd);
+    }}
+
+    /**
+     * runAfterBoth result completes exceptionally if action does
+     */
+    public void testRunAfterBoth_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingRunnable r1 = new FailingRunnable(m);
+        final FailingRunnable r2 = new FailingRunnable(m);
+        final FailingRunnable r3 = new FailingRunnable(m);
+
+        final CompletableFuture<Integer> fst =  fFirst ? f : g;
+        final CompletableFuture<Integer> snd = !fFirst ? f : g;
+        final Integer w1 =  fFirst ? v1 : v2;
+        final Integer w2 = !fFirst ? v1 : v2;
+
+        final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1);
+        assertTrue(fst.complete(w1));
+        final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2);
+        assertTrue(snd.complete(w2));
+        final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
+
+        checkCompletedWithWrappedException(h1, r1.ex);
+        checkCompletedWithWrappedException(h2, r2.ex);
+        checkCompletedWithWrappedException(h3, r3.ex);
+        r1.assertInvoked();
+        r2.assertInvoked();
+        r3.assertInvoked();
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * applyToEither result completes normally after normal completion
+     * of either source
+     */
+    public void testApplyToEither_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.complete(v1);
+        checkCompletedNormally(h0, inc(v1));
+        checkCompletedNormally(h1, inc(v1));
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedNormally(h2, inc(v1));
+        checkCompletedNormally(h3, inc(v1));
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+        rs[4].assertValue(h4.join());
+        rs[5].assertValue(h5.join());
+        assertTrue(Objects.equals(inc(v1), h4.join()) ||
+                   Objects.equals(inc(v2), h4.join()));
+        assertTrue(Objects.equals(inc(v1), h5.join()) ||
+                   Objects.equals(inc(v2), h5.join()));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        checkCompletedNormally(h0, inc(v1));
+        checkCompletedNormally(h1, inc(v1));
+        checkCompletedNormally(h2, inc(v1));
+        checkCompletedNormally(h3, inc(v1));
+        for (int i = 0; i < 4; i++) rs[i].assertValue(inc(v1));
+    }}
+
+    /**
+     * applyToEither result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testApplyToEither_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.completeExceptionally(ex);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+        try {
+            assertEquals(inc(v1), h4.join());
+            rs[4].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h4, ex);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h5.join());
+            rs[5].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h5, ex);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCompletedExceptionally(f, ex);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testApplyToEither_exceptionalCompletion2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(inc(v1), h0.join());
+            rs[0].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h0, ex);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h1.join());
+            rs[1].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h1, ex);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h2.join());
+            rs[2].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h2, ex);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h3.join());
+            rs[3].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h3, ex);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCompletedExceptionally(g, ex);
+    }}
+
+    /**
+     * applyToEither result completes exceptionally if either source cancelled
+     */
+    public void testApplyToEither_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.cancel(mayInterruptIfRunning);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+        try {
+            assertEquals(inc(v1), h4.join());
+            rs[4].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h4);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h5.join());
+            rs[5].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h5);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCancelled(f);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testApplyToEither_sourceCancelled2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final IncFunction[] rs = new IncFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        assertTrue(fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning));
+        assertTrue(!fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(inc(v1), h0.join());
+            rs[0].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h0);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h1.join());
+            rs[1].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h1);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h2.join());
+            rs[2].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h2);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(inc(v1), h3.join());
+            rs[3].assertValue(inc(v1));
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h3);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCancelled(g);
+    }}
+
+    /**
+     * applyToEither result completes exceptionally if action does
+     */
+    public void testApplyToEither_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingFunction[] rs = new FailingFunction[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m);
+
+        final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]);
+        final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]);
+        f.complete(v1);
+        final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
+        final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h0, rs[0].ex);
+        checkCompletedWithWrappedException(h1, rs[1].ex);
+        checkCompletedWithWrappedException(h2, rs[2].ex);
+        checkCompletedWithWrappedException(h3, rs[3].ex);
+        for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
+        final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
+
+        checkCompletedWithWrappedException(h4, rs[4].ex);
+        assertTrue(Objects.equals(v1, rs[4].value) ||
+                   Objects.equals(v2, rs[4].value));
+        checkCompletedWithWrappedException(h5, rs[5].ex);
+        assertTrue(Objects.equals(v1, rs[5].value) ||
+                   Objects.equals(v2, rs[5].value));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * acceptEither result completes normally after normal completion
+     * of either source
+     */
+    public void testAcceptEither_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.complete(v1);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        rs[0].assertValue(v1);
+        rs[1].assertValue(v1);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        rs[2].assertValue(v1);
+        rs[3].assertValue(v1);
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+        checkCompletedNormally(h4, null);
+        checkCompletedNormally(h5, null);
+        assertTrue(Objects.equals(v1, rs[4].value) ||
+                   Objects.equals(v2, rs[4].value));
+        assertTrue(Objects.equals(v1, rs[5].value) ||
+                   Objects.equals(v2, rs[5].value));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+    }}
+
+    /**
+     * acceptEither result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testAcceptEither_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.completeExceptionally(ex);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h4, ex);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h5, ex);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCompletedExceptionally(f, ex);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testAcceptEither_exceptionalCompletion2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(null, h0.join());
+            rs[0].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h0, ex);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h1.join());
+            rs[1].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h1, ex);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h2.join());
+            rs[2].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h2, ex);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h3.join());
+            rs[3].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h3, ex);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCompletedExceptionally(g, ex);
+    }}
+
+    /**
+     * acceptEither result completes exceptionally if either source cancelled
+     */
+    public void testAcceptEither_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final NoopConsumer[] rs = new NoopConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.cancel(mayInterruptIfRunning);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+
+        g.complete(v1);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h4);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertValue(v1);
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h5);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCancelled(f);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    /**
+     * acceptEither result completes exceptionally if action does
+     */
+    public void testAcceptEither_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingConsumer[] rs = new FailingConsumer[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m);
+
+        final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]);
+        f.complete(v1);
+        final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h0, rs[0].ex);
+        checkCompletedWithWrappedException(h1, rs[1].ex);
+        checkCompletedWithWrappedException(h2, rs[2].ex);
+        checkCompletedWithWrappedException(h3, rs[3].ex);
+        for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
+
+        g.complete(v2);
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
+
+        checkCompletedWithWrappedException(h4, rs[4].ex);
+        assertTrue(Objects.equals(v1, rs[4].value) ||
+                   Objects.equals(v2, rs[4].value));
+        checkCompletedWithWrappedException(h5, rs[5].ex);
+        assertTrue(Objects.equals(v1, rs[5].value) ||
+                   Objects.equals(v2, rs[5].value));
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+    }}
+
+    /**
+     * runAfterEither result completes normally after normal completion
+     * of either source
+     */
+    public void testRunAfterEither_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+        for (boolean pushNop : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        if (pushNop) {          // ad hoc test of intra-completion interference
+            m.thenRun(f, () -> {});
+            m.thenRun(g, () -> {});
+        }
+        f.complete(v1);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        rs[0].assertInvoked();
+        rs[1].assertInvoked();
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        rs[2].assertInvoked();
+        rs[3].assertInvoked();
+
+        g.complete(v2);
+
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        checkCompletedNormally(h0, null);
+        checkCompletedNormally(h1, null);
+        checkCompletedNormally(h2, null);
+        checkCompletedNormally(h3, null);
+        checkCompletedNormally(h4, null);
+        checkCompletedNormally(h5, null);
+        for (int i = 0; i < 6; i++) rs[i].assertInvoked();
+    }}
+
+    /**
+     * runAfterEither result completes exceptionally after exceptional
+     * completion of either source
+     */
+    public void testRunAfterEither_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        assertTrue(f.completeExceptionally(ex));
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+
+        assertTrue(g.complete(v1));
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h4, ex);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h5, ex);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCompletedExceptionally(f, ex);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedException(h0, ex);
+        checkCompletedWithWrappedException(h1, ex);
+        checkCompletedWithWrappedException(h2, ex);
+        checkCompletedWithWrappedException(h3, ex);
+        checkCompletedWithWrappedException(h4, ex);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    public void testRunAfterEither_exceptionalCompletion2() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean fFirst : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CFException ex = new CFException();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        assertTrue( fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex));
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+
+        // unspecified behavior - both source completions available
+        try {
+            assertEquals(null, h0.join());
+            rs[0].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h0, ex);
+            rs[0].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h1.join());
+            rs[1].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h1, ex);
+            rs[1].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h2.join());
+            rs[2].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h2, ex);
+            rs[2].assertNotInvoked();
+        }
+        try {
+            assertEquals(null, h3.join());
+            rs[3].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedException(h3, ex);
+            rs[3].assertNotInvoked();
+        }
+
+        checkCompletedNormally(f, v1);
+        checkCompletedExceptionally(g, ex);
+    }}
+
+    /**
+     * runAfterEither result completes exceptionally if either source cancelled
+     */
+    public void testRunAfterEither_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final Noop[] rs = new Noop[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        checkIncomplete(h0);
+        checkIncomplete(h1);
+        rs[0].assertNotInvoked();
+        rs[1].assertNotInvoked();
+        f.cancel(mayInterruptIfRunning);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+
+        assertTrue(g.complete(v1));
+
+        // unspecified behavior - both source completions available
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+        try {
+            assertNull(h4.join());
+            rs[4].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h4);
+            rs[4].assertNotInvoked();
+        }
+        try {
+            assertNull(h5.join());
+            rs[5].assertInvoked();
+        } catch (CompletionException ok) {
+            checkCompletedWithWrappedCancellationException(h5);
+            rs[5].assertNotInvoked();
+        }
+
+        checkCancelled(f);
+        checkCompletedNormally(g, v1);
+        checkCompletedWithWrappedCancellationException(h0);
+        checkCompletedWithWrappedCancellationException(h1);
+        checkCompletedWithWrappedCancellationException(h2);
+        checkCompletedWithWrappedCancellationException(h3);
+        for (int i = 0; i < 4; i++) rs[i].assertNotInvoked();
+    }}
+
+    /**
+     * runAfterEither result completes exceptionally if action does
+     */
+    public void testRunAfterEither_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (Integer v1 : new Integer[] { 1, null })
+        for (Integer v2 : new Integer[] { 2, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final FailingRunnable[] rs = new FailingRunnable[6];
+        for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m);
+
+        final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]);
+        final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]);
+        assertTrue(f.complete(v1));
+        final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
+        final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]);
+        checkCompletedWithWrappedException(h0, rs[0].ex);
+        checkCompletedWithWrappedException(h1, rs[1].ex);
+        checkCompletedWithWrappedException(h2, rs[2].ex);
+        checkCompletedWithWrappedException(h3, rs[3].ex);
+        for (int i = 0; i < 4; i++) rs[i].assertInvoked();
+        assertTrue(g.complete(v2));
+        final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]);
+        final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
+        checkCompletedWithWrappedException(h4, rs[4].ex);
+        checkCompletedWithWrappedException(h5, rs[5].ex);
+
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v2);
+        for (int i = 0; i < 6; i++) rs[i].assertInvoked();
+    }}
+
+    /**
+     * thenCompose result completes normally after normal completion of source
+     */
+    public void testThenCompose_normalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFutureInc r = new CompletableFutureInc(m);
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedNormally(g, inc(v1));
+        checkCompletedNormally(f, v1);
+        r.assertValue(v1);
+    }}
+
+    /**
+     * thenCompose result completes exceptionally after exceptional
+     * completion of source
+     */
+    public void testThenCompose_exceptionalCompletion() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        final CFException ex = new CFException();
+        final CompletableFutureInc r = new CompletableFutureInc(m);
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) f.completeExceptionally(ex);
+
+        checkCompletedWithWrappedException(g, ex);
+        checkCompletedExceptionally(f, ex);
+        r.assertNotInvoked();
+    }}
+
+    /**
+     * thenCompose result completes exceptionally if action does
+     */
+    public void testThenCompose_actionFailed() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final FailingCompletableFutureFunction r
+            = new FailingCompletableFutureFunction(m);
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) assertTrue(f.complete(v1));
+
+        checkCompletedWithWrappedException(g, r.ex);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * thenCompose result completes exceptionally if source cancelled
+     */
+    public void testThenCompose_sourceCancelled() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+    {
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFutureInc r = new CompletableFutureInc(m);
+        if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
+        final CompletableFuture<Integer> g = m.thenCompose(f, r);
+        if (createIncomplete) {
+            checkIncomplete(g);
+            assertTrue(f.cancel(mayInterruptIfRunning));
+        }
+
+        checkCompletedWithWrappedCancellationException(g);
+        checkCancelled(f);
+    }}
+
+    /**
+     * thenCompose result completes exceptionally if the result of the action does
+     */
+    public void testThenCompose_actionReturnsFailingFuture() {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (int order = 0; order < 6; order++)
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> f = new CompletableFuture<>();
+        final CompletableFuture<Integer> g = new CompletableFuture<>();
+        final CompletableFuture<Integer> h;
+        // Test all permutations of orders
+        switch (order) {
+        case 0:
+            assertTrue(f.complete(v1));
+            assertTrue(g.completeExceptionally(ex));
+            h = m.thenCompose(f, (x -> g));
+            break;
+        case 1:
+            assertTrue(f.complete(v1));
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(g.completeExceptionally(ex));
+            break;
+        case 2:
+            assertTrue(g.completeExceptionally(ex));
+            assertTrue(f.complete(v1));
+            h = m.thenCompose(f, (x -> g));
+            break;
+        case 3:
+            assertTrue(g.completeExceptionally(ex));
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(f.complete(v1));
+            break;
+        case 4:
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(f.complete(v1));
+            assertTrue(g.completeExceptionally(ex));
+            break;
+        case 5:
+            h = m.thenCompose(f, (x -> g));
+            assertTrue(f.complete(v1));
+            assertTrue(g.completeExceptionally(ex));
+            break;
+        default: throw new AssertionError();
+        }
+
+        checkCompletedExceptionally(g, ex);
+        checkCompletedWithWrappedException(h, ex);
+        checkCompletedNormally(f, v1);
+    }}
+
+    // other static methods
+
+    /**
+     * allOf(no component futures) returns a future completed normally
+     * with the value null
+     */
+    public void testAllOf_empty() throws Exception {
+        CompletableFuture<Void> f = CompletableFuture.allOf();
+        checkCompletedNormally(f, null);
+    }
+
+    /**
+     * allOf returns a future completed normally with the value null
+     * when all components complete normally
+     */
+    public void testAllOf_normal() throws Exception {
+        for (int k = 1; k < 10; k++) {
+            CompletableFuture<Integer>[] fs
+                = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+            for (int i = 0; i < k; i++) {
+                checkIncomplete(f);
+                checkIncomplete(CompletableFuture.allOf(fs));
+                fs[i].complete(one);
+            }
+            checkCompletedNormally(f, null);
+            checkCompletedNormally(CompletableFuture.allOf(fs), null);
+        }
+    }
+
+    public void testAllOf_normal_backwards() throws Exception {
+        for (int k = 1; k < 10; k++) {
+            CompletableFuture<Integer>[] fs
+                = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+            for (int i = k - 1; i >= 0; i--) {
+                checkIncomplete(f);
+                checkIncomplete(CompletableFuture.allOf(fs));
+                fs[i].complete(one);
+            }
+            checkCompletedNormally(f, null);
+            checkCompletedNormally(CompletableFuture.allOf(fs), null);
+        }
+    }
+
+    public void testAllOf_exceptional() throws Exception {
+        for (int k = 1; k < 10; k++) {
+            CompletableFuture<Integer>[] fs
+                = (CompletableFuture<Integer>[]) new CompletableFuture[k];
+            CFException ex = new CFException();
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Void> f = CompletableFuture.allOf(fs);
+            for (int i = 0; i < k; i++) {
+                checkIncomplete(f);
+                checkIncomplete(CompletableFuture.allOf(fs));
+                if (i != k / 2) {
+                    fs[i].complete(i);
+                    checkCompletedNormally(fs[i], i);
+                } else {
+                    fs[i].completeExceptionally(ex);
+                    checkCompletedExceptionally(fs[i], ex);
+                }
+            }
+            checkCompletedWithWrappedException(f, ex);
+            checkCompletedWithWrappedException(CompletableFuture.allOf(fs), ex);
+        }
+    }
+
+    /**
+     * anyOf(no component futures) returns an incomplete future
+     */
+    public void testAnyOf_empty() throws Exception {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Object> f = CompletableFuture.anyOf();
+        checkIncomplete(f);
+
+        f.complete(v1);
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * anyOf returns a future completed normally with a value when
+     * a component future does
+     */
+    public void testAnyOf_normal() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = 0; i < k; i++) {
+                fs[i].complete(i);
+                checkCompletedNormally(f, 0);
+                int x = (int) CompletableFuture.anyOf(fs).join();
+                assertTrue(0 <= x && x <= i);
+            }
+        }
+    }
+    public void testAnyOf_normal_backwards() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            for (int i = 0; i < k; i++)
+                fs[i] = new CompletableFuture<>();
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = k - 1; i >= 0; i--) {
+                fs[i].complete(i);
+                checkCompletedNormally(f, k - 1);
+                int x = (int) CompletableFuture.anyOf(fs).join();
+                assertTrue(i <= x && x <= k - 1);
+            }
+        }
+    }
+
+    /**
+     * anyOf result completes exceptionally when any component does.
+     */
+    public void testAnyOf_exceptional() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            CFException[] exs = new CFException[k];
+            for (int i = 0; i < k; i++) {
+                fs[i] = new CompletableFuture<>();
+                exs[i] = new CFException();
+            }
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = 0; i < k; i++) {
+                fs[i].completeExceptionally(exs[i]);
+                checkCompletedWithWrappedException(f, exs[0]);
+                checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs));
+            }
+        }
+    }
+
+    public void testAnyOf_exceptional_backwards() throws Exception {
+        for (int k = 0; k < 10; k++) {
+            CompletableFuture[] fs = new CompletableFuture[k];
+            CFException[] exs = new CFException[k];
+            for (int i = 0; i < k; i++) {
+                fs[i] = new CompletableFuture<>();
+                exs[i] = new CFException();
+            }
+            CompletableFuture<Object> f = CompletableFuture.anyOf(fs);
+            checkIncomplete(f);
+            for (int i = k - 1; i >= 0; i--) {
+                fs[i].completeExceptionally(exs[i]);
+                checkCompletedWithWrappedException(f, exs[k - 1]);
+                checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs));
+            }
+        }
+    }
+
+    /**
+     * Completion methods throw NullPointerException with null arguments
+     */
+    public void testNPE() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = new CompletableFuture<>();
+        CompletableFuture<Integer> nullFuture = (CompletableFuture<Integer>)null;
+        ThreadExecutor exec = new ThreadExecutor();
+
+        Runnable[] throwingActions = {
+            () -> CompletableFuture.supplyAsync(null),
+            () -> CompletableFuture.supplyAsync(null, exec),
+            () -> CompletableFuture.supplyAsync(new IntegerSupplier(ExecutionMode.SYNC, 42), null),
+
+            () -> CompletableFuture.runAsync(null),
+            () -> CompletableFuture.runAsync(null, exec),
+            () -> CompletableFuture.runAsync(() -> {}, null),
+
+            () -> f.completeExceptionally(null),
+
+            () -> f.thenApply(null),
+            () -> f.thenApplyAsync(null),
+            () -> f.thenApplyAsync(x -> x, null),
+            () -> f.thenApplyAsync(null, exec),
+
+            () -> f.thenAccept(null),
+            () -> f.thenAcceptAsync(null),
+            () -> f.thenAcceptAsync(x -> {} , null),
+            () -> f.thenAcceptAsync(null, exec),
+
+            () -> f.thenRun(null),
+            () -> f.thenRunAsync(null),
+            () -> f.thenRunAsync(() -> {} , null),
+            () -> f.thenRunAsync(null, exec),
+
+            () -> f.thenCombine(g, null),
+            () -> f.thenCombineAsync(g, null),
+            () -> f.thenCombineAsync(g, null, exec),
+            () -> f.thenCombine(nullFuture, (x, y) -> x),
+            () -> f.thenCombineAsync(nullFuture, (x, y) -> x),
+            () -> f.thenCombineAsync(nullFuture, (x, y) -> x, exec),
+            () -> f.thenCombineAsync(g, (x, y) -> x, null),
+
+            () -> f.thenAcceptBoth(g, null),
+            () -> f.thenAcceptBothAsync(g, null),
+            () -> f.thenAcceptBothAsync(g, null, exec),
+            () -> f.thenAcceptBoth(nullFuture, (x, y) -> {}),
+            () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}),
+            () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}, exec),
+            () -> f.thenAcceptBothAsync(g, (x, y) -> {}, null),
+
+            () -> f.runAfterBoth(g, null),
+            () -> f.runAfterBothAsync(g, null),
+            () -> f.runAfterBothAsync(g, null, exec),
+            () -> f.runAfterBoth(nullFuture, () -> {}),
+            () -> f.runAfterBothAsync(nullFuture, () -> {}),
+            () -> f.runAfterBothAsync(nullFuture, () -> {}, exec),
+            () -> f.runAfterBothAsync(g, () -> {}, null),
+
+            () -> f.applyToEither(g, null),
+            () -> f.applyToEitherAsync(g, null),
+            () -> f.applyToEitherAsync(g, null, exec),
+            () -> f.applyToEither(nullFuture, x -> x),
+            () -> f.applyToEitherAsync(nullFuture, x -> x),
+            () -> f.applyToEitherAsync(nullFuture, x -> x, exec),
+            () -> f.applyToEitherAsync(g, x -> x, null),
+
+            () -> f.acceptEither(g, null),
+            () -> f.acceptEitherAsync(g, null),
+            () -> f.acceptEitherAsync(g, null, exec),
+            () -> f.acceptEither(nullFuture, x -> {}),
+            () -> f.acceptEitherAsync(nullFuture, x -> {}),
+            () -> f.acceptEitherAsync(nullFuture, x -> {}, exec),
+            () -> f.acceptEitherAsync(g, x -> {}, null),
+
+            () -> f.runAfterEither(g, null),
+            () -> f.runAfterEitherAsync(g, null),
+            () -> f.runAfterEitherAsync(g, null, exec),
+            () -> f.runAfterEither(nullFuture, () -> {}),
+            () -> f.runAfterEitherAsync(nullFuture, () -> {}),
+            () -> f.runAfterEitherAsync(nullFuture, () -> {}, exec),
+            () -> f.runAfterEitherAsync(g, () -> {}, null),
+
+            () -> f.thenCompose(null),
+            () -> f.thenComposeAsync(null),
+            () -> f.thenComposeAsync(new CompletableFutureInc(ExecutionMode.EXECUTOR), null),
+            () -> f.thenComposeAsync(null, exec),
+
+            () -> f.exceptionally(null),
+
+            () -> f.handle(null),
+
+            () -> CompletableFuture.allOf((CompletableFuture<?>)null),
+            () -> CompletableFuture.allOf((CompletableFuture<?>[])null),
+            () -> CompletableFuture.allOf(f, null),
+            () -> CompletableFuture.allOf(null, f),
+
+            () -> CompletableFuture.anyOf((CompletableFuture<?>)null),
+            () -> CompletableFuture.anyOf((CompletableFuture<?>[])null),
+            () -> CompletableFuture.anyOf(f, null),
+            () -> CompletableFuture.anyOf(null, f),
+
+            () -> f.obtrudeException(null),
+
+            () -> CompletableFuture.delayedExecutor(1L, SECONDS, null),
+            () -> CompletableFuture.delayedExecutor(1L, null, exec),
+            () -> CompletableFuture.delayedExecutor(1L, null),
+
+            () -> f.orTimeout(1L, null),
+            () -> f.completeOnTimeout(42, 1L, null),
+
+            () -> CompletableFuture.failedFuture(null),
+            () -> CompletableFuture.failedStage(null),
+        };
+
+        assertThrows(NullPointerException.class, throwingActions);
+        assertEquals(0, exec.count.get());
+    }
+
+    /**
+     * Test submissions to an executor that rejects all tasks.
+     */
+    public void testRejectingExecutor() {
+        for (Integer v : new Integer[] { 1, null })
+    {
+        final CountingRejectingExecutor e = new CountingRejectingExecutor();
+
+        final CompletableFuture<Integer> complete = CompletableFuture.completedFuture(v);
+        final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+
+        List<CompletableFuture<?>> futures = new ArrayList<>();
+
+        List<CompletableFuture<Integer>> srcs = new ArrayList<>();
+        srcs.add(complete);
+        srcs.add(incomplete);
+
+        for (CompletableFuture<Integer> src : srcs) {
+            List<CompletableFuture<?>> fs = new ArrayList<>();
+            fs.add(src.thenRunAsync(() -> {}, e));
+            fs.add(src.thenAcceptAsync(z -> {}, e));
+            fs.add(src.thenApplyAsync(z -> z, e));
+
+            fs.add(src.thenCombineAsync(src, (x, y) -> x, e));
+            fs.add(src.thenAcceptBothAsync(src, (x, y) -> {}, e));
+            fs.add(src.runAfterBothAsync(src, () -> {}, e));
+
+            fs.add(src.applyToEitherAsync(src, z -> z, e));
+            fs.add(src.acceptEitherAsync(src, z -> {}, e));
+            fs.add(src.runAfterEitherAsync(src, () -> {}, e));
+
+            fs.add(src.thenComposeAsync(z -> null, e));
+            fs.add(src.whenCompleteAsync((z, t) -> {}, e));
+            fs.add(src.handleAsync((z, t) -> null, e));
+
+            for (CompletableFuture<?> future : fs) {
+                if (src.isDone())
+                    checkCompletedWithWrappedException(future, e.ex);
+                else
+                    checkIncomplete(future);
+            }
+            futures.addAll(fs);
+        }
+
+        {
+            List<CompletableFuture<?>> fs = new ArrayList<>();
+
+            fs.add(complete.thenCombineAsync(incomplete, (x, y) -> x, e));
+            fs.add(incomplete.thenCombineAsync(complete, (x, y) -> x, e));
+
+            fs.add(complete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e));
+            fs.add(incomplete.thenAcceptBothAsync(complete, (x, y) -> {}, e));
+
+            fs.add(complete.runAfterBothAsync(incomplete, () -> {}, e));
+            fs.add(incomplete.runAfterBothAsync(complete, () -> {}, e));
+
+            for (CompletableFuture<?> future : fs)
+                checkIncomplete(future);
+            futures.addAll(fs);
+        }
+
+        {
+            List<CompletableFuture<?>> fs = new ArrayList<>();
+
+            fs.add(complete.applyToEitherAsync(incomplete, z -> z, e));
+            fs.add(incomplete.applyToEitherAsync(complete, z -> z, e));
+
+            fs.add(complete.acceptEitherAsync(incomplete, z -> {}, e));
+            fs.add(incomplete.acceptEitherAsync(complete, z -> {}, e));
+
+            fs.add(complete.runAfterEitherAsync(incomplete, () -> {}, e));
+            fs.add(incomplete.runAfterEitherAsync(complete, () -> {}, e));
+
+            for (CompletableFuture<?> future : fs)
+                checkCompletedWithWrappedException(future, e.ex);
+            futures.addAll(fs);
+        }
+
+        incomplete.complete(v);
+
+        for (CompletableFuture<?> future : futures)
+            checkCompletedWithWrappedException(future, e.ex);
+
+        assertEquals(futures.size(), e.count.get());
+    }}
+
+    /**
+     * Test submissions to an executor that rejects all tasks, but
+     * should never be invoked because the dependent future is
+     * explicitly completed.
+     */
+    public void testRejectingExecutorNeverInvoked() {
+        for (Integer v : new Integer[] { 1, null })
+    {
+        final CountingRejectingExecutor e = new CountingRejectingExecutor();
+
+        final CompletableFuture<Integer> complete = CompletableFuture.completedFuture(v);
+        final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+
+        List<CompletableFuture<?>> futures = new ArrayList<>();
+
+        List<CompletableFuture<Integer>> srcs = new ArrayList<>();
+        srcs.add(complete);
+        srcs.add(incomplete);
+
+        List<CompletableFuture<?>> fs = new ArrayList<>();
+        fs.add(incomplete.thenRunAsync(() -> {}, e));
+        fs.add(incomplete.thenAcceptAsync(z -> {}, e));
+        fs.add(incomplete.thenApplyAsync(z -> z, e));
+
+        fs.add(incomplete.thenCombineAsync(incomplete, (x, y) -> x, e));
+        fs.add(incomplete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e));
+        fs.add(incomplete.runAfterBothAsync(incomplete, () -> {}, e));
+
+        fs.add(incomplete.applyToEitherAsync(incomplete, z -> z, e));
+        fs.add(incomplete.acceptEitherAsync(incomplete, z -> {}, e));
+        fs.add(incomplete.runAfterEitherAsync(incomplete, () -> {}, e));
+
+        fs.add(incomplete.thenComposeAsync(z -> null, e));
+        fs.add(incomplete.whenCompleteAsync((z, t) -> {}, e));
+        fs.add(incomplete.handleAsync((z, t) -> null, e));
+
+        fs.add(complete.thenCombineAsync(incomplete, (x, y) -> x, e));
+        fs.add(incomplete.thenCombineAsync(complete, (x, y) -> x, e));
+
+        fs.add(complete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e));
+        fs.add(incomplete.thenAcceptBothAsync(complete, (x, y) -> {}, e));
+
+        fs.add(complete.runAfterBothAsync(incomplete, () -> {}, e));
+        fs.add(incomplete.runAfterBothAsync(complete, () -> {}, e));
+
+        for (CompletableFuture<?> future : fs)
+            checkIncomplete(future);
+
+        for (CompletableFuture<?> future : fs)
+            future.complete(null);
+
+        incomplete.complete(v);
+
+        for (CompletableFuture<?> future : fs)
+            checkCompletedNormally(future, null);
+
+        assertEquals(0, e.count.get());
+    }}
+
+    /**
+     * toCompletableFuture returns this CompletableFuture.
+     */
+    public void testToCompletableFuture() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        assertSame(f, f.toCompletableFuture());
+    }
+
+    // jdk9
+
+    /**
+     * newIncompleteFuture returns an incomplete CompletableFuture
+     */
+    public void testNewIncompleteFuture() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = f.newIncompleteFuture();
+        checkIncomplete(f);
+        checkIncomplete(g);
+        f.complete(v1);
+        checkCompletedNormally(f, v1);
+        checkIncomplete(g);
+        g.complete(v1);
+        checkCompletedNormally(g, v1);
+        assertSame(g.getClass(), CompletableFuture.class);
+    }}
+
+    /**
+     * completedStage returns a completed CompletionStage
+     */
+    public void testCompletedStage() {
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<>();
+        CompletionStage<Integer> f = CompletableFuture.completedStage(1);
+        f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        assertEquals(x.get(), 1);
+        assertNull(r.get());
+    }
+
+    /**
+     * defaultExecutor by default returns the commonPool if
+     * it supports more than one thread.
+     */
+    public void testDefaultExecutor() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        Executor e = f.defaultExecutor();
+        Executor c = ForkJoinPool.commonPool();
+        if (ForkJoinPool.getCommonPoolParallelism() > 1)
+            assertSame(e, c);
+        else
+            assertNotSame(e, c);
+    }
+
+    /**
+     * failedFuture returns a CompletableFuture completed
+     * exceptionally with the given Exception
+     */
+    public void testFailedFuture() {
+        CFException ex = new CFException();
+        CompletableFuture<Integer> f = CompletableFuture.failedFuture(ex);
+        checkCompletedExceptionally(f, ex);
+    }
+
+    /**
+     * failedFuture(null) throws NPE
+     */
+    public void testFailedFuture_null() {
+        try {
+            CompletableFuture<Integer> f = CompletableFuture.failedFuture(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * copy returns a CompletableFuture that is completed normally,
+     * with the same value, when source is.
+     */
+    public void testCopy_normalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        CompletableFuture<Integer> g = f.copy();
+        if (createIncomplete) {
+            checkIncomplete(f);
+            checkIncomplete(g);
+            assertTrue(f.complete(v1));
+        }
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v1);
+    }}
+
+    /**
+     * copy returns a CompletableFuture that is completed exceptionally
+     * when source is.
+     */
+    public void testCopy_exceptionalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        CFException ex = new CFException();
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        CompletableFuture<Integer> g = f.copy();
+        if (createIncomplete) {
+            checkIncomplete(f);
+            checkIncomplete(g);
+            f.completeExceptionally(ex);
+        }
+        checkCompletedExceptionally(f, ex);
+        checkCompletedWithWrappedException(g, ex);
+    }}
+
+    /**
+     * Completion of a copy does not complete its source.
+     */
+    public void testCopy_oneWayPropagation() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        assertTrue(f.copy().complete(1));
+        assertTrue(f.copy().complete(null));
+        assertTrue(f.copy().cancel(true));
+        assertTrue(f.copy().cancel(false));
+        assertTrue(f.copy().completeExceptionally(new CFException()));
+        checkIncomplete(f);
+    }
+
+    /**
+     * minimalCompletionStage returns a CompletableFuture that is
+     * completed normally, with the same value, when source is.
+     */
+    public void testMinimalCompletionStage() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> g = f.minimalCompletionStage();
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<>();
+        checkIncomplete(f);
+        g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        f.complete(1);
+        checkCompletedNormally(f, 1);
+        assertEquals(x.get(), 1);
+        assertNull(r.get());
+    }
+
+    /**
+     * minimalCompletionStage returns a CompletableFuture that is
+     * completed exceptionally when source is.
+     */
+    public void testMinimalCompletionStage2() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> g = f.minimalCompletionStage();
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<>();
+        g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        checkIncomplete(f);
+        CFException ex = new CFException();
+        f.completeExceptionally(ex);
+        checkCompletedExceptionally(f, ex);
+        assertEquals(x.get(), 0);
+        assertEquals(r.get().getCause(), ex);
+    }
+
+    /**
+     * failedStage returns a CompletionStage completed
+     * exceptionally with the given Exception
+     */
+    public void testFailedStage() {
+        CFException ex = new CFException();
+        CompletionStage<Integer> f = CompletableFuture.failedStage(ex);
+        AtomicInteger x = new AtomicInteger(0);
+        AtomicReference<Throwable> r = new AtomicReference<>();
+        f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);});
+        assertEquals(x.get(), 0);
+        assertEquals(r.get(), ex);
+    }
+
+    /**
+     * completeAsync completes with value of given supplier
+     */
+    public void testCompleteAsync() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        f.completeAsync(() -> v1);
+        f.join();
+        checkCompletedNormally(f, v1);
+    }}
+
+    /**
+     * completeAsync completes exceptionally if given supplier throws
+     */
+    public void testCompleteAsync2() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CFException ex = new CFException();
+        f.completeAsync(() -> { throw ex; });
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CompletionException success) {}
+        checkCompletedWithWrappedException(f, ex);
+    }
+
+    /**
+     * completeAsync with given executor completes with value of given supplier
+     */
+    public void testCompleteAsync3() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        ThreadExecutor executor = new ThreadExecutor();
+        f.completeAsync(() -> v1, executor);
+        assertSame(v1, f.join());
+        checkCompletedNormally(f, v1);
+        assertEquals(1, executor.count.get());
+    }}
+
+    /**
+     * completeAsync with given executor completes exceptionally if
+     * given supplier throws
+     */
+    public void testCompleteAsync4() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CFException ex = new CFException();
+        ThreadExecutor executor = new ThreadExecutor();
+        f.completeAsync(() -> { throw ex; }, executor);
+        try {
+            f.join();
+            shouldThrow();
+        } catch (CompletionException success) {}
+        checkCompletedWithWrappedException(f, ex);
+        assertEquals(1, executor.count.get());
+    }
+
+    /**
+     * orTimeout completes with TimeoutException if not complete
+     */
+    public void testOrTimeout_timesOut() {
+        long timeoutMillis = timeoutMillis();
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        assertSame(f, f.orTimeout(timeoutMillis, MILLISECONDS));
+        checkCompletedWithTimeoutException(f);
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+    }
+
+    /**
+     * orTimeout completes normally if completed before timeout
+     */
+    public void testOrTimeout_completed() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        f.complete(v1);
+        assertSame(f, f.orTimeout(LONG_DELAY_MS, MILLISECONDS));
+        assertSame(g, g.orTimeout(LONG_DELAY_MS, MILLISECONDS));
+        g.complete(v1);
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v1);
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+    }}
+
+    /**
+     * completeOnTimeout completes with given value if not complete
+     */
+    public void testCompleteOnTimeout_timesOut() {
+        testInParallel(() -> testCompleteOnTimeout_timesOut(42),
+                       () -> testCompleteOnTimeout_timesOut(null));
+    }
+
+    /**
+     * completeOnTimeout completes with given value if not complete
+     */
+    public void testCompleteOnTimeout_timesOut(Integer v) {
+        long timeoutMillis = timeoutMillis();
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        assertSame(f, f.completeOnTimeout(v, timeoutMillis, MILLISECONDS));
+        assertSame(v, f.join());
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        f.complete(99);         // should have no effect
+        checkCompletedNormally(f, v);
+    }
+
+    /**
+     * completeOnTimeout has no effect if completed within timeout
+     */
+    public void testCompleteOnTimeout_completed() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletableFuture<Integer> g = new CompletableFuture<>();
+        long startTime = System.nanoTime();
+        f.complete(v1);
+        assertSame(f, f.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS));
+        assertSame(g, g.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS));
+        g.complete(v1);
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v1);
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2);
+    }}
+
+    /**
+     * delayedExecutor returns an executor that delays submission
+     */
+    public void testDelayedExecutor() {
+        testInParallel(() -> testDelayedExecutor(null, null),
+                       () -> testDelayedExecutor(null, 1),
+                       () -> testDelayedExecutor(new ThreadExecutor(), 1),
+                       () -> testDelayedExecutor(new ThreadExecutor(), 1));
+    }
+
+    public void testDelayedExecutor(Executor executor, Integer v) throws Exception {
+        long timeoutMillis = timeoutMillis();
+        // Use an "unreasonably long" long timeout to catch lingering threads
+        long longTimeoutMillis = 1000 * 60 * 60 * 24;
+        final Executor delayer, longDelayer;
+        if (executor == null) {
+            delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS);
+            longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS);
+        } else {
+            delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS, executor);
+            longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS, executor);
+        }
+        long startTime = System.nanoTime();
+        CompletableFuture<Integer> f =
+            CompletableFuture.supplyAsync(() -> v, delayer);
+        CompletableFuture<Integer> g =
+            CompletableFuture.supplyAsync(() -> v, longDelayer);
+
+        assertNull(g.getNow(null));
+
+        assertSame(v, f.get(LONG_DELAY_MS, MILLISECONDS));
+        long millisElapsed = millisElapsedSince(startTime);
+        assertTrue(millisElapsed >= timeoutMillis);
+        assertTrue(millisElapsed < LONG_DELAY_MS / 2);
+
+        checkCompletedNormally(f, v);
+
+        checkIncomplete(g);
+        assertTrue(g.cancel(true));
+    }
+
+    //--- tests of implementation details; not part of official tck ---
+
+    Object resultOf(CompletableFuture<?> f) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            try {
+                System.setSecurityManager(null);
+            } catch (SecurityException giveUp) {
+                return "Reflection not available";
+            }
+        }
+
+        try {
+            java.lang.reflect.Field resultField
+                = CompletableFuture.class.getDeclaredField("result");
+            resultField.setAccessible(true);
+            return resultField.get(f);
+        } catch (Throwable t) {
+            throw new AssertionError(t);
+        } finally {
+            if (sm != null) System.setSecurityManager(sm);
+        }
+    }
+
+    public void testExceptionPropagationReusesResultObject() {
+        if (!testImplementationDetails) return;
+        for (ExecutionMode m : ExecutionMode.values())
+    {
+        final CFException ex = new CFException();
+        final CompletableFuture<Integer> v42 = CompletableFuture.completedFuture(42);
+        final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+
+        final Runnable noopRunnable = new Noop(m);
+        final Consumer<Integer> noopConsumer = new NoopConsumer(m);
+        final Function<Integer, Integer> incFunction = new IncFunction(m);
+
+        List<Function<CompletableFuture<Integer>, CompletableFuture<?>>> funs
+            = new ArrayList<>();
+
+        funs.add(y -> m.thenRun(y, noopRunnable));
+        funs.add(y -> m.thenAccept(y, noopConsumer));
+        funs.add(y -> m.thenApply(y, incFunction));
+
+        funs.add(y -> m.runAfterEither(y, incomplete, noopRunnable));
+        funs.add(y -> m.acceptEither(y, incomplete, noopConsumer));
+        funs.add(y -> m.applyToEither(y, incomplete, incFunction));
+
+        funs.add(y -> m.runAfterBoth(y, v42, noopRunnable));
+        funs.add(y -> m.runAfterBoth(v42, y, noopRunnable));
+        funs.add(y -> m.thenAcceptBoth(y, v42, new SubtractAction(m)));
+        funs.add(y -> m.thenAcceptBoth(v42, y, new SubtractAction(m)));
+        funs.add(y -> m.thenCombine(y, v42, new SubtractFunction(m)));
+        funs.add(y -> m.thenCombine(v42, y, new SubtractFunction(m)));
+
+        funs.add(y -> m.whenComplete(y, (Integer r, Throwable t) -> {}));
+
+        funs.add(y -> m.thenCompose(y, new CompletableFutureInc(m)));
+
+        funs.add(y -> CompletableFuture.allOf(y));
+        funs.add(y -> CompletableFuture.allOf(y, v42));
+        funs.add(y -> CompletableFuture.allOf(v42, y));
+        funs.add(y -> CompletableFuture.anyOf(y));
+        funs.add(y -> CompletableFuture.anyOf(y, incomplete));
+        funs.add(y -> CompletableFuture.anyOf(incomplete, y));
+
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            f.completeExceptionally(ex);
+            CompletableFuture<Integer> src = m.thenApply(f, incFunction);
+            checkCompletedWithWrappedException(src, ex);
+            CompletableFuture<?> dep = fun.apply(src);
+            checkCompletedWithWrappedException(dep, ex);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            CompletableFuture<Integer> src = m.thenApply(f, incFunction);
+            CompletableFuture<?> dep = fun.apply(src);
+            f.completeExceptionally(ex);
+            checkCompletedWithWrappedException(src, ex);
+            checkCompletedWithWrappedException(dep, ex);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            f.cancel(mayInterruptIfRunning);
+            checkCancelled(f);
+            CompletableFuture<Integer> src = m.thenApply(f, incFunction);
+            checkCompletedWithWrappedCancellationException(src);
+            CompletableFuture<?> dep = fun.apply(src);
+            checkCompletedWithWrappedCancellationException(dep);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false })
+        for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
+                 fun : funs) {
+            CompletableFuture<Integer> f = new CompletableFuture<>();
+            CompletableFuture<Integer> src = m.thenApply(f, incFunction);
+            CompletableFuture<?> dep = fun.apply(src);
+            f.cancel(mayInterruptIfRunning);
+            checkCancelled(f);
+            checkCompletedWithWrappedCancellationException(src);
+            checkCompletedWithWrappedCancellationException(dep);
+            assertSame(resultOf(src), resultOf(dep));
+        }
+    }}
+
+    /**
+     * Minimal completion stages throw UOE for most non-CompletionStage methods
+     */
+    public void testMinimalCompletionStage_minimality() {
+        if (!testImplementationDetails) return;
+        Function<Method, String> toSignature =
+            method -> method.getName() + Arrays.toString(method.getParameterTypes());
+        Predicate<Method> isNotStatic =
+            method -> (method.getModifiers() & Modifier.STATIC) == 0;
+        List<Method> minimalMethods =
+            Stream.of(Object.class, CompletionStage.class)
+            .flatMap(klazz -> Stream.of(klazz.getMethods()))
+            .filter(isNotStatic)
+            .collect(Collectors.toList());
+        // Methods from CompletableFuture permitted NOT to throw UOE
+        String[] signatureWhitelist = {
+            "newIncompleteFuture[]",
+            "defaultExecutor[]",
+            "minimalCompletionStage[]",
+            "copy[]",
+        };
+        Set<String> permittedMethodSignatures =
+            Stream.concat(minimalMethods.stream().map(toSignature),
+                          Stream.of(signatureWhitelist))
+            .collect(Collectors.toSet());
+        List<Method> allMethods = Stream.of(CompletableFuture.class.getMethods())
+            .filter(isNotStatic)
+            .filter(method -> !permittedMethodSignatures.contains(toSignature.apply(method)))
+            .collect(Collectors.toList());
+
+        List<CompletionStage<Integer>> stages = new ArrayList<>();
+        CompletionStage<Integer> min =
+            new CompletableFuture<Integer>().minimalCompletionStage();
+        stages.add(min);
+        stages.add(min.thenApply(x -> x));
+        stages.add(CompletableFuture.completedStage(1));
+        stages.add(CompletableFuture.failedStage(new CFException()));
+
+        List<Method> bugs = new ArrayList<>();
+        for (Method method : allMethods) {
+            Class<?>[] parameterTypes = method.getParameterTypes();
+            Object[] args = new Object[parameterTypes.length];
+            // Manufacture boxed primitives for primitive params
+            for (int i = 0; i < args.length; i++) {
+                Class<?> type = parameterTypes[i];
+                if (parameterTypes[i] == boolean.class)
+                    args[i] = false;
+                else if (parameterTypes[i] == int.class)
+                    args[i] = 0;
+                else if (parameterTypes[i] == long.class)
+                    args[i] = 0L;
+            }
+            for (CompletionStage<Integer> stage : stages) {
+                try {
+                    method.invoke(stage, args);
+                    bugs.add(method);
+                }
+                catch (java.lang.reflect.InvocationTargetException expected) {
+                    if (! (expected.getCause() instanceof UnsupportedOperationException)) {
+                        bugs.add(method);
+                        // expected.getCause().printStackTrace();
+                    }
+                }
+                catch (ReflectiveOperationException bad) { throw new Error(bad); }
+            }
+        }
+        if (!bugs.isEmpty())
+            throw new Error("Methods did not throw UOE: " + bugs);
+    }
+
+    /**
+     * minimalStage.toCompletableFuture() returns a CompletableFuture that
+     * is completed normally, with the same value, when source is.
+     */
+    public void testMinimalCompletionStage_toCompletableFuture_normalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> minimal = f.minimalCompletionStage();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        CompletableFuture<Integer> g = minimal.toCompletableFuture();
+        if (createIncomplete) {
+            checkIncomplete(f);
+            checkIncomplete(g);
+            assertTrue(f.complete(v1));
+        }
+        checkCompletedNormally(f, v1);
+        checkCompletedNormally(g, v1);
+    }}
+
+    /**
+     * minimalStage.toCompletableFuture() returns a CompletableFuture that
+     * is completed exceptionally when source is.
+     */
+    public void testMinimalCompletionStage_toCompletableFuture_exceptionalCompletion() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+    {
+        CFException ex = new CFException();
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> minimal = f.minimalCompletionStage();
+        if (!createIncomplete) f.completeExceptionally(ex);
+        CompletableFuture<Integer> g = minimal.toCompletableFuture();
+        if (createIncomplete) {
+            checkIncomplete(f);
+            checkIncomplete(g);
+            f.completeExceptionally(ex);
+        }
+        checkCompletedExceptionally(f, ex);
+        checkCompletedWithWrappedException(g, ex);
+    }}
+
+    /**
+     * minimalStage.toCompletableFuture() gives mutable CompletableFuture
+     */
+    public void testMinimalCompletionStage_toCompletableFuture_mutable() {
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage minimal = f.minimalCompletionStage();
+        CompletableFuture<Integer> g = minimal.toCompletableFuture();
+        assertTrue(g.complete(v1));
+        checkCompletedNormally(g, v1);
+        checkIncomplete(f);
+        checkIncomplete(minimal.toCompletableFuture());
+    }}
+
+    /**
+     * minimalStage.toCompletableFuture().join() awaits completion
+     */
+    public void testMinimalCompletionStage_toCompletableFuture_join() throws Exception {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        CompletionStage<Integer> minimal = f.minimalCompletionStage();
+        if (createIncomplete) assertTrue(f.complete(v1));
+        assertEquals(v1, minimal.toCompletableFuture().join());
+        assertEquals(v1, minimal.toCompletableFuture().get());
+        checkCompletedNormally(minimal.toCompletableFuture(), v1);
+    }}
+
+    /**
+     * Completion of a toCompletableFuture copy of a minimal stage
+     * does not complete its source.
+     */
+    public void testMinimalCompletionStage_toCompletableFuture_oneWayPropagation() {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> g = f.minimalCompletionStage();
+        assertTrue(g.toCompletableFuture().complete(1));
+        assertTrue(g.toCompletableFuture().complete(null));
+        assertTrue(g.toCompletableFuture().cancel(true));
+        assertTrue(g.toCompletableFuture().cancel(false));
+        assertTrue(g.toCompletableFuture().completeExceptionally(new CFException()));
+        checkIncomplete(g.toCompletableFuture());
+        f.complete(1);
+        checkCompletedNormally(g.toCompletableFuture(), 1);
+    }
+
+    /** Demo utility method for external reliable toCompletableFuture */
+    static <T> CompletableFuture<T> toCompletableFuture(CompletionStage<T> stage) {
+        CompletableFuture<T> f = new CompletableFuture<>();
+        stage.handle((T t, Throwable ex) -> {
+                         if (ex != null) f.completeExceptionally(ex);
+                         else f.complete(t);
+                         return null;
+                     });
+        return f;
+    }
+
+    /** Demo utility method to join a CompletionStage */
+    static <T> T join(CompletionStage<T> stage) {
+        return toCompletableFuture(stage).join();
+    }
+
+    /**
+     * Joining a minimal stage "by hand" works
+     */
+    public void testMinimalCompletionStage_join_by_hand() {
+        for (boolean createIncomplete : new boolean[] { true, false })
+        for (Integer v1 : new Integer[] { 1, null })
+    {
+        CompletableFuture<Integer> f = new CompletableFuture<>();
+        CompletionStage<Integer> minimal = f.minimalCompletionStage();
+        CompletableFuture<Integer> g = new CompletableFuture<>();
+        if (!createIncomplete) assertTrue(f.complete(v1));
+        minimal.thenAccept(x -> g.complete(x));
+        if (createIncomplete) assertTrue(f.complete(v1));
+        g.join();
+        checkCompletedNormally(g, v1);
+        checkCompletedNormally(f, v1);
+        assertEquals(v1, join(minimal));
+    }}
+
+    static class Monad {
+        static class ZeroException extends RuntimeException {
+            public ZeroException() { super("monadic zero"); }
+        }
+        // "return", "unit"
+        static <T> CompletableFuture<T> unit(T value) {
+            return completedFuture(value);
+        }
+        // monadic zero ?
+        static <T> CompletableFuture<T> zero() {
+            return failedFuture(new ZeroException());
+        }
+        // >=>
+        static <T,U,V> Function<T, CompletableFuture<V>> compose
+            (Function<T, CompletableFuture<U>> f,
+             Function<U, CompletableFuture<V>> g) {
+            return x -> f.apply(x).thenCompose(g);
+        }
+
+        static void assertZero(CompletableFuture<?> f) {
+            try {
+                f.getNow(null);
+                throw new AssertionFailedError("should throw");
+            } catch (CompletionException success) {
+                assertTrue(success.getCause() instanceof ZeroException);
+            }
+        }
+
+        static <T> void assertFutureEquals(CompletableFuture<T> f,
+                                           CompletableFuture<T> g) {
+            T fval = null, gval = null;
+            Throwable fex = null, gex = null;
+
+            try { fval = f.get(); }
+            catch (ExecutionException ex) { fex = ex.getCause(); }
+            catch (Throwable ex) { fex = ex; }
+
+            try { gval = g.get(); }
+            catch (ExecutionException ex) { gex = ex.getCause(); }
+            catch (Throwable ex) { gex = ex; }
+
+            if (fex != null || gex != null)
+                assertSame(fex.getClass(), gex.getClass());
+            else
+                assertEquals(fval, gval);
+        }
+
+        static class PlusFuture<T> extends CompletableFuture<T> {
+            AtomicReference<Throwable> firstFailure = new AtomicReference<>(null);
+        }
+
+        /** Implements "monadic plus". */
+        static <T> CompletableFuture<T> plus(CompletableFuture<? extends T> f,
+                                             CompletableFuture<? extends T> g) {
+            PlusFuture<T> plus = new PlusFuture<T>();
+            BiConsumer<T, Throwable> action = (T result, Throwable ex) -> {
+                try {
+                    if (ex == null) {
+                        if (plus.complete(result))
+                            if (plus.firstFailure.get() != null)
+                                plus.firstFailure.set(null);
+                    }
+                    else if (plus.firstFailure.compareAndSet(null, ex)) {
+                        if (plus.isDone())
+                            plus.firstFailure.set(null);
+                    }
+                    else {
+                        // first failure has precedence
+                        Throwable first = plus.firstFailure.getAndSet(null);
+
+                        // may fail with "Self-suppression not permitted"
+                        try { first.addSuppressed(ex); }
+                        catch (Exception ignored) {}
+
+                        plus.completeExceptionally(first);
+                    }
+                } catch (Throwable unexpected) {
+                    plus.completeExceptionally(unexpected);
+                }
+            };
+            f.whenComplete(action);
+            g.whenComplete(action);
+            return plus;
+        }
+    }
+
+    /**
+     * CompletableFuture is an additive monad - sort of.
+     * https://en.wikipedia.org/wiki/Monad_(functional_programming)#Additive_monads
+     */
+    public void testAdditiveMonad() throws Throwable {
+        Function<Long, CompletableFuture<Long>> unit = Monad::unit;
+        CompletableFuture<Long> zero = Monad.zero();
+
+        // Some mutually non-commutative functions
+        Function<Long, CompletableFuture<Long>> triple
+            = x -> Monad.unit(3 * x);
+        Function<Long, CompletableFuture<Long>> inc
+            = x -> Monad.unit(x + 1);
+
+        // unit is a right identity: m >>= unit === m
+        Monad.assertFutureEquals(inc.apply(5L).thenCompose(unit),
+                                 inc.apply(5L));
+        // unit is a left identity: (unit x) >>= f === f x
+        Monad.assertFutureEquals(unit.apply(5L).thenCompose(inc),
+                                 inc.apply(5L));
+
+        // associativity: (m >>= f) >>= g === m >>= ( \x -> (f x >>= g) )
+        Monad.assertFutureEquals(
+            unit.apply(5L).thenCompose(inc).thenCompose(triple),
+            unit.apply(5L).thenCompose(x -> inc.apply(x).thenCompose(triple)));
+
+        // The case for CompletableFuture as an additive monad is weaker...
+
+        // zero is a monadic zero
+        Monad.assertZero(zero);
+
+        // left zero: zero >>= f === zero
+        Monad.assertZero(zero.thenCompose(inc));
+        // right zero: f >>= (\x -> zero) === zero
+        Monad.assertZero(inc.apply(5L).thenCompose(x -> zero));
+
+        // f plus zero === f
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(Monad.unit(5L), zero));
+        // zero plus f === f
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(zero, Monad.unit(5L)));
+        // zero plus zero === zero
+        Monad.assertZero(Monad.plus(zero, zero));
+        {
+            CompletableFuture<Long> f = Monad.plus(Monad.unit(5L),
+                                                   Monad.unit(8L));
+            // non-determinism
+            assertTrue(f.get() == 5L || f.get() == 8L);
+        }
+
+        CompletableFuture<Long> godot = new CompletableFuture<>();
+        // f plus godot === f (doesn't wait for godot)
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(Monad.unit(5L), godot));
+        // godot plus f === f (doesn't wait for godot)
+        Monad.assertFutureEquals(Monad.unit(5L),
+                                 Monad.plus(godot, Monad.unit(5L)));
+    }
+
+    /** Test long recursive chains of CompletableFutures with cascading completions */
+    public void testRecursiveChains() throws Throwable {
+        for (ExecutionMode m : ExecutionMode.values())
+        for (boolean addDeadEnds : new boolean[] { true, false })
+    {
+        final int val = 42;
+        final int n = expensiveTests ? 1_000 : 2;
+        CompletableFuture<Integer> head = new CompletableFuture<>();
+        CompletableFuture<Integer> tail = head;
+        for (int i = 0; i < n; i++) {
+            if (addDeadEnds) m.thenApply(tail, v -> v + 1);
+            tail = m.thenApply(tail, v -> v + 1);
+            if (addDeadEnds) m.applyToEither(tail, tail, v -> v + 1);
+            tail = m.applyToEither(tail, tail, v -> v + 1);
+            if (addDeadEnds) m.thenCombine(tail, tail, (v, w) -> v + 1);
+            tail = m.thenCombine(tail, tail, (v, w) -> v + 1);
+        }
+        head.complete(val);
+        assertEquals(val + 3 * n, (int) tail.join());
+    }}
+
+    /**
+     * A single CompletableFuture with many dependents.
+     * A demo of scalability - runtime is O(n).
+     */
+    public void testManyDependents() throws Throwable {
+        final int n = expensiveTests ? 1_000_000 : 10;
+        final CompletableFuture<Void> head = new CompletableFuture<>();
+        final CompletableFuture<Void> complete = CompletableFuture.completedFuture((Void)null);
+        final AtomicInteger count = new AtomicInteger(0);
+        for (int i = 0; i < n; i++) {
+            head.thenRun(() -> count.getAndIncrement());
+            head.thenAccept(x -> count.getAndIncrement());
+            head.thenApply(x -> count.getAndIncrement());
+
+            head.runAfterBoth(complete, () -> count.getAndIncrement());
+            head.thenAcceptBoth(complete, (x, y) -> count.getAndIncrement());
+            head.thenCombine(complete, (x, y) -> count.getAndIncrement());
+            complete.runAfterBoth(head, () -> count.getAndIncrement());
+            complete.thenAcceptBoth(head, (x, y) -> count.getAndIncrement());
+            complete.thenCombine(head, (x, y) -> count.getAndIncrement());
+
+            head.runAfterEither(new CompletableFuture<Void>(), () -> count.getAndIncrement());
+            head.acceptEither(new CompletableFuture<Void>(), x -> count.getAndIncrement());
+            head.applyToEither(new CompletableFuture<Void>(), x -> count.getAndIncrement());
+            new CompletableFuture<Void>().runAfterEither(head, () -> count.getAndIncrement());
+            new CompletableFuture<Void>().acceptEither(head, x -> count.getAndIncrement());
+            new CompletableFuture<Void>().applyToEither(head, x -> count.getAndIncrement());
+        }
+        head.complete(null);
+        assertEquals(5 * 3 * n, count.get());
+    }
+
+    /** ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest tck */
+    public void testCoCompletionGarbageRetention() throws Throwable {
+        final int n = expensiveTests ? 1_000_000 : 10;
+        final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+        CompletableFuture<Integer> f;
+        for (int i = 0; i < n; i++) {
+            f = new CompletableFuture<>();
+            f.runAfterEither(incomplete, () -> {});
+            f.complete(null);
+
+            f = new CompletableFuture<>();
+            f.acceptEither(incomplete, x -> {});
+            f.complete(null);
+
+            f = new CompletableFuture<>();
+            f.applyToEither(incomplete, x -> x);
+            f.complete(null);
+
+            f = new CompletableFuture<>();
+            CompletableFuture.anyOf(new CompletableFuture<?>[] { f, incomplete });
+            f.complete(null);
+        }
+
+        for (int i = 0; i < n; i++) {
+            f = new CompletableFuture<>();
+            incomplete.runAfterEither(f, () -> {});
+            f.complete(null);
+
+            f = new CompletableFuture<>();
+            incomplete.acceptEither(f, x -> {});
+            f.complete(null);
+
+            f = new CompletableFuture<>();
+            incomplete.applyToEither(f, x -> x);
+            f.complete(null);
+
+            f = new CompletableFuture<>();
+            CompletableFuture.anyOf(new CompletableFuture<?>[] { incomplete, f });
+            f.complete(null);
+        }
+    }
+
+    /**
+     * Reproduction recipe for:
+     * 8160402: Garbage retention with CompletableFuture.anyOf
+     * cvs update -D '2016-05-01' ./src/main/java/util/concurrent/CompletableFuture.java && ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testAnyOfGarbageRetention tck; cvs update -A
+     */
+    public void testAnyOfGarbageRetention() throws Throwable {
+        for (Integer v : new Integer[] { 1, null })
+    {
+        final int n = expensiveTests ? 100_000 : 10;
+        CompletableFuture<Integer>[] fs
+            = (CompletableFuture<Integer>[]) new CompletableFuture<?>[100];
+        for (int i = 0; i < fs.length; i++)
+            fs[i] = new CompletableFuture<>();
+        fs[fs.length - 1].complete(v);
+        for (int i = 0; i < n; i++)
+            checkCompletedNormally(CompletableFuture.anyOf(fs), v);
+    }}
+
+    /**
+     * Checks for garbage retention with allOf.
+     *
+     * As of 2016-07, fails with OOME:
+     * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testCancelledAllOfGarbageRetention tck
+     */
+    public void testCancelledAllOfGarbageRetention() throws Throwable {
+        final int n = expensiveTests ? 100_000 : 10;
+        CompletableFuture<Integer>[] fs
+            = (CompletableFuture<Integer>[]) new CompletableFuture<?>[100];
+        for (int i = 0; i < fs.length; i++)
+            fs[i] = new CompletableFuture<>();
+        for (int i = 0; i < n; i++)
+            assertTrue(CompletableFuture.allOf(fs).cancel(false));
+    }
+
+    /**
+     * Checks for garbage retention when a dependent future is
+     * cancelled and garbage-collected.
+     * 8161600: Garbage retention when source CompletableFutures are never completed
+     *
+     * As of 2016-07, fails with OOME:
+     * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testCancelledGarbageRetention tck
+     */
+    public void testCancelledGarbageRetention() throws Throwable {
+        final int n = expensiveTests ? 100_000 : 10;
+        CompletableFuture<Integer> neverCompleted = new CompletableFuture<>();
+        for (int i = 0; i < n; i++)
+            assertTrue(neverCompleted.thenRun(() -> {}).cancel(true));
+    }
+
+    /**
+     * Checks for garbage retention when MinimalStage.toCompletableFuture()
+     * is invoked many times.
+     * 8161600: Garbage retention when source CompletableFutures are never completed
+     *
+     * As of 2016-07, fails with OOME:
+     * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testToCompletableFutureGarbageRetention tck
+     */
+    public void testToCompletableFutureGarbageRetention() throws Throwable {
+        final int n = expensiveTests ? 900_000 : 10;
+        CompletableFuture<Integer> neverCompleted = new CompletableFuture<>();
+        CompletionStage minimal = neverCompleted.minimalCompletionStage();
+        for (int i = 0; i < n; i++)
+            assertTrue(minimal.toCompletableFuture().cancel(true));
+    }
+
+//     static <U> U join(CompletionStage<U> stage) {
+//         CompletableFuture<U> f = new CompletableFuture<>();
+//         stage.whenComplete((v, ex) -> {
+//             if (ex != null) f.completeExceptionally(ex); else f.complete(v);
+//         });
+//         return f.join();
+//     }
+
+//     static <U> boolean isDone(CompletionStage<U> stage) {
+//         CompletableFuture<U> f = new CompletableFuture<>();
+//         stage.whenComplete((v, ex) -> {
+//             if (ex != null) f.completeExceptionally(ex); else f.complete(v);
+//         });
+//         return f.isDone();
+//     }
+
+//     static <U> U join2(CompletionStage<U> stage) {
+//         return stage.toCompletableFuture().copy().join();
+//     }
+
+//     static <U> boolean isDone2(CompletionStage<U> stage) {
+//         return stage.toCompletableFuture().copy().isDone();
+//     }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java
new file mode 100644
index 0000000..324a5ef
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentHashMap8Test.java
@@ -0,0 +1,1147 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.Spliterator.CONCURRENT;
+import static java.util.Spliterator.DISTINCT;
+import static java.util.Spliterator.NONNULL;
+
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
+import java.util.function.BiFunction;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentHashMap8Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentHashMap8Test.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentHashMap map5() {
+        ConcurrentHashMap map = new ConcurrentHashMap(5);
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(two, "B");
+        map.put(three, "C");
+        map.put(four, "D");
+        map.put(five, "E");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * getOrDefault returns value if present, else default
+     */
+    public void testGetOrDefault() {
+        ConcurrentHashMap map = map5();
+        assertEquals(map.getOrDefault(one, "Z"), "A");
+        assertEquals(map.getOrDefault(six, "Z"), "Z");
+    }
+
+    /**
+     * computeIfAbsent adds when the given key is not present
+     */
+    public void testComputeIfAbsent() {
+        ConcurrentHashMap map = map5();
+        map.computeIfAbsent(six, x -> "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * computeIfAbsent does not replace if the key is already present
+     */
+    public void testComputeIfAbsent2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.computeIfAbsent(one, x -> "Z"));
+    }
+
+    /**
+     * computeIfAbsent does not add if function returns null
+     */
+    public void testComputeIfAbsent3() {
+        ConcurrentHashMap map = map5();
+        map.computeIfAbsent(six, x -> null);
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * computeIfPresent does not replace if the key is already present
+     */
+    public void testComputeIfPresent() {
+        ConcurrentHashMap map = map5();
+        map.computeIfPresent(six, (x, y) -> "Z");
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * computeIfPresent adds when the given key is not present
+     */
+    public void testComputeIfPresent2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.computeIfPresent(one, (x, y) -> "Z"));
+    }
+
+    /**
+     * compute does not replace if the function returns null
+     */
+    public void testCompute() {
+        ConcurrentHashMap map = map5();
+        map.compute(six, (x, y) -> null);
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * compute adds when the given key is not present
+     */
+    public void testCompute2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.compute(six, (x, y) -> "Z"));
+    }
+
+    /**
+     * compute replaces when the given key is present
+     */
+    public void testCompute3() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.compute(one, (x, y) -> "Z"));
+    }
+
+    /**
+     * compute removes when the given key is present and function returns null
+     */
+    public void testCompute4() {
+        ConcurrentHashMap map = map5();
+        map.compute(one, (x, y) -> null);
+        assertFalse(map.containsKey(one));
+    }
+
+    /**
+     * merge adds when the given key is not present
+     */
+    public void testMerge1() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Y", map.merge(six, "Y", (x, y) -> "Z"));
+    }
+
+    /**
+     * merge replaces when the given key is present
+     */
+    public void testMerge2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("Z", map.merge(one, "Y", (x, y) -> "Z"));
+    }
+
+    /**
+     * merge removes when the given key is present and function returns null
+     */
+    public void testMerge3() {
+        ConcurrentHashMap map = map5();
+        map.merge(one, "Y", (x, y) -> null);
+        assertFalse(map.containsKey(one));
+    }
+
+    static Set<Integer> populatedSet(int n) {
+        Set<Integer> a = ConcurrentHashMap.<Integer>newKeySet();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(a.add(i));
+        assertEquals(n == 0, a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static Set populatedSet(Integer[] elements) {
+        Set<Integer> a = ConcurrentHashMap.<Integer>newKeySet();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            assertTrue(a.add(elements[i]));
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * replaceAll replaces all matching values.
+     */
+    public void testReplaceAll() {
+        ConcurrentHashMap<Integer, String> map = map5();
+        map.replaceAll((x, y) -> { return x > 3 ? "Z" : y; });
+        assertEquals("A", map.get(one));
+        assertEquals("B", map.get(two));
+        assertEquals("C", map.get(three));
+        assertEquals("Z", map.get(four));
+        assertEquals("Z", map.get(five));
+    }
+
+    /**
+     * Default-constructed set is empty
+     */
+    public void testNewKeySet() {
+        Set a = ConcurrentHashMap.newKeySet();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * keySet.add adds the key with the established value to the map;
+     * remove removes it.
+     */
+    public void testKeySetAddRemove() {
+        ConcurrentHashMap map = map5();
+        Set set1 = map.keySet();
+        Set set2 = map.keySet(true);
+        set2.add(six);
+        assertTrue(((ConcurrentHashMap.KeySetView)set2).getMap() == map);
+        assertTrue(((ConcurrentHashMap.KeySetView)set1).getMap() == map);
+        assertEquals(set2.size(), map.size());
+        assertEquals(set1.size(), map.size());
+        assertTrue((Boolean)map.get(six));
+        assertTrue(set1.contains(six));
+        assertTrue(set2.contains(six));
+        set2.remove(six);
+        assertNull(map.get(six));
+        assertFalse(set1.contains(six));
+        assertFalse(set2.contains(six));
+    }
+
+    /**
+     * keySet.addAll adds each element from the given collection
+     */
+    public void testAddAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+    }
+
+    /**
+     * keySet.addAll adds each element from the given collection that did not
+     * already exist in the set
+     */
+    public void testAddAll2() {
+        Set full = populatedSet(3);
+        // "one" is duplicate and will not be added
+        assertTrue(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * keySet.add will not add the element if it already exists in the set
+     */
+    public void testAdd2() {
+        Set full = populatedSet(3);
+        assertFalse(full.add(one));
+        assertEquals(3, full.size());
+    }
+
+    /**
+     * keySet.add adds the element when it does not exist in the set
+     */
+    public void testAdd3() {
+        Set full = populatedSet(3);
+        assertTrue(full.add(three));
+        assertTrue(full.contains(three));
+        assertFalse(full.add(three));
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * keySet.add throws UnsupportedOperationException if no default
+     * mapped value
+     */
+    public void testAdd4() {
+        Set full = map5().keySet();
+        try {
+            full.add(three);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * keySet.add throws NullPointerException if the specified key is
+     * null
+     */
+    public void testAdd5() {
+        Set full = populatedSet(3);
+        try {
+            full.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * KeySetView.getMappedValue returns the map's mapped value
+     */
+    public void testGetMappedValue() {
+        ConcurrentHashMap map = map5();
+        // Android-changed: ConcurrentHashMap.keySet() has 2 return type variants on Android.
+        assertNull(((ConcurrentHashMap.KeySetView)map.keySet()).getMappedValue());
+        try {
+            map.keySet(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+        ConcurrentHashMap.KeySetView set = map.keySet(one);
+        assertFalse(set.add(one));
+        assertTrue(set.add(six));
+        assertTrue(set.add(seven));
+        assertTrue(set.getMappedValue() == one);
+        assertTrue(map.get(one) != one);
+        assertTrue(map.get(six) == one);
+        assertTrue(map.get(seven) == one);
+    }
+
+    void checkSpliteratorCharacteristics(Spliterator<?> sp,
+                                         int requiredCharacteristics) {
+        assertEquals(requiredCharacteristics,
+                     requiredCharacteristics & sp.characteristics());
+    }
+
+    /**
+     * KeySetView.spliterator returns spliterator over the elements in this set
+     */
+    public void testKeySetSpliterator() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap map = map5();
+        Set set = map.keySet();
+        Spliterator<Integer> sp = set.spliterator();
+        checkSpliteratorCharacteristics(sp, CONCURRENT | DISTINCT | NONNULL);
+        assertEquals(sp.estimateSize(), map.size());
+        Spliterator<Integer> sp2 = sp.trySplit();
+        sp.forEachRemaining((Integer x) -> adder.add(x.longValue()));
+        long v = adder.sumThenReset();
+        sp2.forEachRemaining((Integer x) -> adder.add(x.longValue()));
+        long v2 = adder.sum();
+        assertEquals(v + v2, 15);
+    }
+
+    /**
+     * keyset.clear removes all elements from the set
+     */
+    public void testClear() {
+        Set full = populatedSet(3);
+        full.clear();
+        assertEquals(0, full.size());
+    }
+
+    /**
+     * keyset.contains returns true for added elements
+     */
+    public void testContains() {
+        Set full = populatedSet(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * KeySets with equal elements are equal
+     */
+    public void testEquals() {
+        Set a = populatedSet(3);
+        Set b = populatedSet(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertEquals(a.hashCode(), b.hashCode());
+    }
+
+    /**
+     * KeySet.containsAll returns true for collections with subset of elements
+     */
+    public void testContainsAll() {
+        Collection full = populatedSet(3);
+        assertTrue(full.containsAll(Arrays.asList()));
+        assertTrue(full.containsAll(Arrays.asList(one)));
+        assertTrue(full.containsAll(Arrays.asList(one, two)));
+        assertFalse(full.containsAll(Arrays.asList(one, two, six)));
+        assertFalse(full.containsAll(Arrays.asList(six)));
+    }
+
+    /**
+     * KeySet.isEmpty is true when empty, else false
+     */
+    public void testIsEmpty() {
+        assertTrue(populatedSet(0).isEmpty());
+        assertFalse(populatedSet(3).isEmpty());
+    }
+
+    /**
+     * KeySet.iterator() returns an iterator containing the elements of the
+     * set
+     */
+    public void testIterator() {
+        Collection empty = ConcurrentHashMap.newKeySet();
+        int size = 20;
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[size];
+        for (int i = 0; i < size; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedSet(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < size; j++) {
+            assertTrue(it.hasNext());
+            it.next();
+        }
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collections has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(ConcurrentHashMap.newKeySet().iterator());
+        assertIteratorExhausted(new ConcurrentHashMap().entrySet().iterator());
+        assertIteratorExhausted(new ConcurrentHashMap().values().iterator());
+        assertIteratorExhausted(new ConcurrentHashMap().keySet().iterator());
+    }
+
+    /**
+     * KeySet.iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        Set q = populatedSet(3);
+        Iterator it = q.iterator();
+        Object removed = it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertFalse(it.next().equals(removed));
+        assertFalse(it.next().equals(removed));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * KeySet.toString holds toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", ConcurrentHashMap.newKeySet().toString());
+        Set full = populatedSet(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+    }
+
+    /**
+     * KeySet.removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+        assertFalse(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * KeySet.remove removes an element
+     */
+    public void testRemove() {
+        Set full = populatedSet(3);
+        full.remove(one);
+        assertFalse(full.contains(one));
+        assertEquals(2, full.size());
+    }
+
+    /**
+     * keySet.size returns the number of elements
+     */
+    public void testSize() {
+        Set empty = ConcurrentHashMap.newKeySet();
+        Set full = populatedSet(3);
+        assertEquals(3, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * KeySet.toArray() returns an Object array containing all elements from
+     * the set
+     */
+    public void testToArray() {
+        Object[] a = ConcurrentHashMap.newKeySet().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+        int size = 20;
+        Integer[] elements = new Integer[size];
+        for (int i = 0; i < size; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedSet(elements);
+
+        assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray())));
+        assertTrue(full.containsAll(Arrays.asList(full.toArray())));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the set
+     */
+    public void testToArray2() {
+        Collection empty = ConcurrentHashMap.newKeySet();
+        Integer[] a;
+        int size = 20;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[size / 2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[size];
+        for (int i = 0; i < size; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedSet(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a))));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[size];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.asList(elements).containsAll(Arrays.asList(full.toArray(a))));
+    }
+
+    /**
+     * A deserialized serialized set is equal
+     */
+    public void testSerialization() throws Exception {
+        int size = 20;
+        Set x = populatedSet(size);
+        Set y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static final int SIZE = 10000;
+    static ConcurrentHashMap<Long, Long> longMap;
+
+    static ConcurrentHashMap<Long, Long> longMap() {
+        if (longMap == null) {
+            longMap = new ConcurrentHashMap<Long, Long>(SIZE);
+            for (int i = 0; i < SIZE; ++i)
+                longMap.put(Long.valueOf(i), Long.valueOf(2 *i));
+        }
+        return longMap;
+    }
+
+    // explicit function class to avoid type inference problems
+    static class AddKeys implements BiFunction<Map.Entry<Long,Long>, Map.Entry<Long,Long>, Map.Entry<Long,Long>> {
+        public Map.Entry<Long,Long> apply(Map.Entry<Long,Long> x, Map.Entry<Long,Long> y) {
+            return new AbstractMap.SimpleEntry<Long,Long>
+             (Long.valueOf(x.getKey().longValue() + y.getKey().longValue()),
+              Long.valueOf(1L));
+        }
+    }
+
+    /**
+     * forEachKeySequentially traverses all keys
+     */
+    public void testForEachKeySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachValueSequentially traverses all values
+     */
+    public void testForEachValueSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(Long.MAX_VALUE, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1));
+    }
+
+    /**
+     * forEachSequentially traverses all mappings
+     */
+    public void testForEachSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(Long.MAX_VALUE, (Long x, Long y) -> adder.add(x.longValue() + y.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachEntrySequentially traverses all entries
+     */
+    public void testForEachEntrySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> adder.add(e.getKey().longValue() + e.getValue().longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachKeyInParallel traverses all keys
+     */
+    public void testForEachKeyInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(1L, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachValueInParallel traverses all values
+     */
+    public void testForEachValueInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(1L, (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), SIZE * (SIZE - 1));
+    }
+
+    /**
+     * forEachInParallel traverses all mappings
+     */
+    public void testForEachInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(1L, (Long x, Long y) -> adder.add(x.longValue() + y.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * forEachEntryInParallel traverses all entries
+     */
+    public void testForEachEntryInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(1L, (Map.Entry<Long,Long> e) -> adder.add(e.getKey().longValue() + e.getValue().longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachKeySequentially traverses the given
+     * transformations of all keys
+     */
+    public void testMappedForEachKeySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                 (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachValueSequentially traverses the given
+     * transformations of all values
+     */
+    public void testMappedForEachValueSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                   (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * Mapped forEachSequentially traverses the given
+     * transformations of all mappings
+     */
+    public void testMappedForEachSequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                              (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachEntrySequentially traverses the given
+     * transformations of all entries
+     */
+    public void testMappedForEachEntrySequentially() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()),
+                                   (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachKeyInParallel traverses the given
+     * transformations of all keys
+     */
+    public void testMappedForEachKeyInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachKey(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                               (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachValueInParallel traverses the given
+     * transformations of all values
+     */
+    public void testMappedForEachValueInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachValue(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                 (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * Mapped forEachInParallel traverses the given
+     * transformations of all mappings
+     */
+    public void testMappedForEachInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEach(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                            (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped forEachEntryInParallel traverses the given
+     * transformations of all entries
+     */
+    public void testMappedForEachEntryInParallel() {
+        LongAdder adder = new LongAdder();
+        ConcurrentHashMap<Long, Long> m = longMap();
+        m.forEachEntry(1L, (Map.Entry<Long,Long> e) -> Long.valueOf(e.getKey().longValue() + e.getValue().longValue()),
+                                 (Long x) -> adder.add(x.longValue()));
+        assertEquals(adder.sum(), 3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysSequentially accumulates across all keys,
+     */
+    public void testReduceKeysSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesSequentially accumulates across all values
+     */
+    public void testReduceValuesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceKeys(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceEntriesSequentially accumulates across all entries
+     */
+    public void testReduceEntriesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Map.Entry<Long,Long> r;
+        r = m.reduceEntries(Long.MAX_VALUE, new AddKeys());
+        assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysInParallel accumulates across all keys
+     */
+    public void testReduceKeysInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceKeys(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesInParallel accumulates across all values
+     */
+    public void testReduceValuesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduceValues(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceEntriesInParallel accumulate across all entries
+     */
+    public void testReduceEntriesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Map.Entry<Long,Long> r;
+        r = m.reduceEntries(1L, new AddKeys());
+        assertEquals(r.getKey().longValue(), (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceKeysSequentially accumulates mapped keys
+     */
+    public void testMapReduceKeysSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceKeys(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                     (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceValuesSequentially accumulates mapped values
+     */
+    public void testMapReduceValuesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceValues(Long.MAX_VALUE, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                       (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceSequentially accumulates across all transformed mappings
+     */
+    public void testMappedReduceSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduce(Long.MAX_VALUE, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                                 (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+
+        assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceKeysInParallel, accumulates mapped keys
+     */
+    public void testMapReduceKeysInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceKeys(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                   (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * Mapped reduceValuesInParallel accumulates mapped values
+     */
+    public void testMapReduceValuesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r = m.reduceValues(1L, (Long x) -> Long.valueOf(4 * x.longValue()),
+                                     (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)4 * SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceInParallel accumulate across all transformed mappings
+     */
+    public void testMappedReduceInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.reduce(1L, (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()),
+                               (Long x, Long y) -> Long.valueOf(x.longValue() + y.longValue()));
+        assertEquals((long)r, (long)3 * SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToLongSequentially accumulates mapped keys
+     */
+    public void testReduceKeysToLongSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceKeysToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToIntSequentially accumulates mapped keys
+     */
+    public void testReduceKeysToIntSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceKeysToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToDoubleSequentially accumulates mapped keys
+     */
+    public void testReduceKeysToDoubleSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceKeysToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesToLongSequentially accumulates mapped values
+     */
+    public void testReduceValuesToLongSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceValuesToLong(Long.MAX_VALUE, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToIntSequentially accumulates mapped values
+     */
+    public void testReduceValuesToIntSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceValuesToInt(Long.MAX_VALUE, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToDoubleSequentially accumulates mapped values
+     */
+    public void testReduceValuesToDoubleSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceValuesToDouble(Long.MAX_VALUE, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceKeysToLongInParallel accumulates mapped keys
+     */
+    public void testReduceKeysToLongInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceKeysToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToIntInParallel accumulates mapped keys
+     */
+    public void testReduceKeysToIntInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceKeysToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceKeysToDoubleInParallel accumulates mapped values
+     */
+    public void testReduceKeysToDoubleInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceKeysToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1) / 2);
+    }
+
+    /**
+     * reduceValuesToLongInParallel accumulates mapped values
+     */
+    public void testReduceValuesToLongInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        long lr = m.reduceValuesToLong(1L, (Long x) -> x.longValue(), 0L, Long::sum);
+        assertEquals(lr, (long)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToIntInParallel accumulates mapped values
+     */
+    public void testReduceValuesToIntInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        int ir = m.reduceValuesToInt(1L, (Long x) -> x.intValue(), 0, Integer::sum);
+        assertEquals(ir, SIZE * (SIZE - 1));
+    }
+
+    /**
+     * reduceValuesToDoubleInParallel accumulates mapped values
+     */
+    public void testReduceValuesToDoubleInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        double dr = m.reduceValuesToDouble(1L, (Long x) -> x.doubleValue(), 0.0, Double::sum);
+        assertEquals(dr, (double)SIZE * (SIZE - 1));
+    }
+
+    /**
+     * searchKeysSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchKeysSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchKeys(Long.MAX_VALUE, (Long x) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchValuesSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchValuesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchValues(Long.MAX_VALUE,
+            (Long x) -> (x.longValue() == (long)(SIZE/2)) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchValues(Long.MAX_VALUE,
+            (Long x) -> (x.longValue() < 0L) ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.search(Long.MAX_VALUE, (Long x, Long y) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchEntriesSequentially returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchEntriesSequentially() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchEntries(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchEntries(Long.MAX_VALUE, (Map.Entry<Long,Long> e) -> e.getKey().longValue() < 0L ? e.getKey() : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchKeysInParallel returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchKeysInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchKeys(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchKeys(1L, (Long x) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchValuesInParallel returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchValuesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchValues(1L, (Long x) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchValues(1L, (Long x) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchInParallel returns a non-null result of search function,
+     * or null if none
+     */
+    public void testSearchInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.search(1L, (Long x, Long y) -> x.longValue() == (long)(SIZE/2) ? x : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.search(1L, (Long x, Long y) -> x.longValue() < 0L ? x : null);
+        assertNull(r);
+    }
+
+    /**
+     * searchEntriesInParallel returns a non-null result of search
+     * function, or null if none
+     */
+    public void testSearchEntriesInParallel() {
+        ConcurrentHashMap<Long, Long> m = longMap();
+        Long r;
+        r = m.searchEntries(1L, (Map.Entry<Long,Long> e) -> e.getKey().longValue() == (long)(SIZE/2) ? e.getKey() : null);
+        assertEquals((long)r, (long)(SIZE/2));
+        r = m.searchEntries(1L, (Map.Entry<Long,Long> e) -> e.getKey().longValue() < 0L ? e.getKey() : null);
+        assertNull(r);
+    }
+
+    /**
+     * Tests performance of computeIfAbsent when the element is present.
+     * See JDK-8161372
+     * ant -Djsr166.tckTestClass=ConcurrentHashMapTest -Djsr166.methodFilter=testcomputeIfAbsent_performance -Djsr166.expensiveTests=true tck
+     */
+    public void testcomputeIfAbsent_performance() {
+        final int mapSize = 20;
+        final int iterations = expensiveTests ? (1 << 23) : mapSize * 2;
+        final int threads = expensiveTests ? 10 : 2;
+        final ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
+        for (int i = 0; i < mapSize; i++)
+            map.put(i, i);
+        final ExecutorService pool = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            Runnable r = new CheckedRunnable() {
+                public void realRun() {
+                    int result = 0;
+                    for (int i = 0; i < iterations; i++)
+                        result += map.computeIfAbsent(i % mapSize, k -> k + k);
+                    if (result == -42) throw new Error();
+                }};
+            for (int i = 0; i < threads; i++)
+                pool.execute(r);
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentHashMapTest.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentHashMapTest.java
new file mode 100644
index 0000000..3973eda
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentHashMapTest.java
@@ -0,0 +1,868 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentHashMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentHashMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentHashMap<Integer, String> map5() {
+        ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>(5);
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(two, "B");
+        map.put(three, "C");
+        map.put(four, "D");
+        map.put(five, "E");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /** Re-implement Integer.compare for old java versions */
+    static int compare(int x, int y) {
+        return (x < y) ? -1 : (x > y) ? 1 : 0;
+    }
+
+    // classes for testing Comparable fallbacks
+    static class BI implements Comparable<BI> {
+        private final int value;
+        BI(int value) { this.value = value; }
+        public int compareTo(BI other) {
+            return compare(value, other.value);
+        }
+        public boolean equals(Object x) {
+            return (x instanceof BI) && ((BI)x).value == value;
+        }
+        public int hashCode() { return 42; }
+    }
+    static class CI extends BI { CI(int value) { super(value); } }
+    static class DI extends BI { DI(int value) { super(value); } }
+
+    static class BS implements Comparable<BS> {
+        private final String value;
+        BS(String value) { this.value = value; }
+        public int compareTo(BS other) {
+            return value.compareTo(other.value);
+        }
+        public boolean equals(Object x) {
+            return (x instanceof BS) && value.equals(((BS)x).value);
+        }
+        public int hashCode() { return 42; }
+    }
+
+    static class LexicographicList<E extends Comparable<E>> extends ArrayList<E>
+        implements Comparable<LexicographicList<E>> {
+        LexicographicList(Collection<E> c) { super(c); }
+        LexicographicList(E e) { super(Collections.singleton(e)); }
+        public int compareTo(LexicographicList<E> other) {
+            int common = Math.min(size(), other.size());
+            int r = 0;
+            for (int i = 0; i < common; i++) {
+                if ((r = get(i).compareTo(other.get(i))) != 0)
+                    break;
+            }
+            if (r == 0)
+                r = compare(size(), other.size());
+            return r;
+        }
+        private static final long serialVersionUID = 0;
+    }
+
+    static class CollidingObject {
+        final String value;
+        CollidingObject(final String value) { this.value = value; }
+        public int hashCode() { return this.value.hashCode() & 1; }
+        public boolean equals(final Object obj) {
+            return (obj instanceof CollidingObject) && ((CollidingObject)obj).value.equals(value);
+        }
+    }
+
+    static class ComparableCollidingObject extends CollidingObject implements Comparable<ComparableCollidingObject> {
+        ComparableCollidingObject(final String value) { super(value); }
+        public int compareTo(final ComparableCollidingObject o) {
+            return value.compareTo(o.value);
+        }
+    }
+
+    /**
+     * Inserted elements that are subclasses of the same Comparable
+     * class are found.
+     */
+    public void testComparableFamily() {
+        int size = 500;         // makes measured test run time -> 60ms
+        ConcurrentHashMap<BI, Boolean> m =
+            new ConcurrentHashMap<BI, Boolean>();
+        for (int i = 0; i < size; i++) {
+            assertTrue(m.put(new CI(i), true) == null);
+        }
+        for (int i = 0; i < size; i++) {
+            assertTrue(m.containsKey(new CI(i)));
+            assertTrue(m.containsKey(new DI(i)));
+        }
+    }
+
+    /**
+     * Elements of classes with erased generic type parameters based
+     * on Comparable can be inserted and found.
+     */
+    public void testGenericComparable() {
+        int size = 120;         // makes measured test run time -> 60ms
+        ConcurrentHashMap<Object, Boolean> m =
+            new ConcurrentHashMap<Object, Boolean>();
+        for (int i = 0; i < size; i++) {
+            BI bi = new BI(i);
+            BS bs = new BS(String.valueOf(i));
+            LexicographicList<BI> bis = new LexicographicList<BI>(bi);
+            LexicographicList<BS> bss = new LexicographicList<BS>(bs);
+            assertTrue(m.putIfAbsent(bis, true) == null);
+            assertTrue(m.containsKey(bis));
+            if (m.putIfAbsent(bss, true) == null)
+                assertTrue(m.containsKey(bss));
+            assertTrue(m.containsKey(bis));
+        }
+        for (int i = 0; i < size; i++) {
+            assertTrue(m.containsKey(Collections.singletonList(new BI(i))));
+        }
+    }
+
+    /**
+     * Elements of non-comparable classes equal to those of classes
+     * with erased generic type parameters based on Comparable can be
+     * inserted and found.
+     */
+    public void testGenericComparable2() {
+        int size = 500;         // makes measured test run time -> 60ms
+        ConcurrentHashMap<Object, Boolean> m =
+            new ConcurrentHashMap<Object, Boolean>();
+        for (int i = 0; i < size; i++) {
+            m.put(Collections.singletonList(new BI(i)), true);
+        }
+
+        for (int i = 0; i < size; i++) {
+            LexicographicList<BI> bis = new LexicographicList<BI>(new BI(i));
+            assertTrue(m.containsKey(bis));
+        }
+    }
+
+    /**
+     * Mixtures of instances of comparable and non-comparable classes
+     * can be inserted and found.
+     */
+    public void testMixedComparable() {
+        int size = 1200;        // makes measured test run time -> 35ms
+        ConcurrentHashMap<Object, Object> map =
+            new ConcurrentHashMap<Object, Object>();
+        Random rng = new Random();
+        for (int i = 0; i < size; i++) {
+            Object x;
+            switch (rng.nextInt(4)) {
+            case 0:
+                x = new Object();
+                break;
+            case 1:
+                x = new CollidingObject(Integer.toString(i));
+                break;
+            default:
+                x = new ComparableCollidingObject(Integer.toString(i));
+            }
+            assertNull(map.put(x, x));
+        }
+        int count = 0;
+        for (Object k : map.keySet()) {
+            assertEquals(map.get(k), k);
+            ++count;
+        }
+        assertEquals(count, size);
+        assertEquals(map.size(), size);
+        for (Object k : map.keySet()) {
+            assertEquals(map.put(k, k), k);
+        }
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentHashMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentHashMap map1 = map5();
+        ConcurrentHashMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * hashCode() equals sum of each key.hashCode ^ value.hashCode
+     */
+    public void testHashCode() {
+        ConcurrentHashMap<Integer,String> map = map5();
+        int sum = 0;
+        for (Map.Entry<Integer,String> e : map.entrySet())
+            sum += e.getKey().hashCode() ^ e.getValue().hashCode();
+        assertEquals(sum, map.hashCode());
+    }
+
+    /**
+     * contains returns true for contained value
+     */
+    public void testContains() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.contains("A"));
+        assertFalse(map.contains("Z"));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentHashMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * enumeration returns an enumeration containing the correct
+     * elements
+     */
+    public void testEnumeration() {
+        ConcurrentHashMap map = map5();
+        Enumeration e = map.elements();
+        int count = 0;
+        while (e.hasMoreElements()) {
+            count++;
+            e.nextElement();
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        assertNull(map.get("anything"));
+        assertNull(empty.get("anything"));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        ConcurrentHashMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * keys returns an enumeration containing all the keys from the map
+     */
+    public void testKeys() {
+        ConcurrentHashMap map = map5();
+        Enumeration e = map.keys();
+        int count = 0;
+        while (e.hasMoreElements()) {
+            count++;
+            e.nextElement();
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentHashMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * Test keySet().removeAll on empty map
+     */
+    public void testKeySet_empty_removeAll() {
+        ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
+        Set<Integer> set = map.keySet();
+        set.removeAll(Collections.emptyList());
+        assertTrue(map.isEmpty());
+        assertTrue(set.isEmpty());
+        // following is test for JDK-8163353
+        set.removeAll(Collections.emptySet());
+        assertTrue(map.isEmpty());
+        assertTrue(set.isEmpty());
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentHashMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentHashMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        ConcurrentHashMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentHashMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentHashMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        ConcurrentHashMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentHashMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentHashMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentHashMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentHashMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentHashMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentHashMap map = map5();
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentHashMap map = map5();
+        ConcurrentHashMap empty = new ConcurrentHashMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentHashMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * Cannot create with only negative capacity
+     */
+    public void testConstructor1() {
+        try {
+            new ConcurrentHashMap(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor (initialCapacity, loadFactor) throws
+     * IllegalArgumentException if either argument is negative
+     */
+    public void testConstructor2() {
+        try {
+            new ConcurrentHashMap(-1, .75f);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        try {
+            new ConcurrentHashMap(16, -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor (initialCapacity, loadFactor, concurrencyLevel)
+     * throws IllegalArgumentException if any argument is negative
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentHashMap(-1, .75f, 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        try {
+            new ConcurrentHashMap(16, -1, 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        try {
+            new ConcurrentHashMap(16, .75f, -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * ConcurrentHashMap(map) throws NullPointerException if the given
+     * map is null
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentHashMap(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * ConcurrentHashMap(map) creates a new map with the same mappings
+     * as the given map
+     */
+    public void testConstructor5() {
+        ConcurrentHashMap map1 = map5();
+        ConcurrentHashMap map2 = new ConcurrentHashMap(map5());
+        assertTrue(map2.equals(map1));
+        map2.put(one, "F");
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * get(null) throws NPE
+     */
+    public void testGet_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * contains(null) throws NPE
+     */
+    public void testContains_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.contains(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(x, null) throws NPE
+     */
+    public void testPut2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.put("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(x, null) throws NPE
+     */
+    public void testPutIfAbsent2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.putIfAbsent("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, null) throws NPE
+     */
+    public void testReplace2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace("whatever", null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, null, y) throws NPE
+     */
+    public void testReplaceValue2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace("whatever", null, "A");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(x, y, null) throws NPE
+     */
+    public void testReplaceValue3_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        try {
+            c.replace("whatever", one, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(x, null) returns false
+     */
+    public void testRemove3() {
+        ConcurrentHashMap c = new ConcurrentHashMap(5);
+        c.put("sadsdf", "asdads");
+        assertFalse(c.remove("sadsdf", null));
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        Map x = map5();
+        Map y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * SetValue of an EntrySet entry sets value in the map.
+     */
+    public void testSetValueWriteThrough() {
+        // Adapted from a bug report by Eric Zoerner
+        ConcurrentHashMap map = new ConcurrentHashMap(2, 5.0f, 1);
+        assertTrue(map.isEmpty());
+        for (int i = 0; i < 20; i++)
+            map.put(new Integer(i), new Integer(i));
+        assertFalse(map.isEmpty());
+        Map.Entry entry1 = (Map.Entry)map.entrySet().iterator().next();
+        // Unless it happens to be first (in which case remainder of
+        // test is skipped), remove a possibly-colliding key from map
+        // which, under some implementations, may cause entry1 to be
+        // cloned in map
+        if (!entry1.getKey().equals(new Integer(16))) {
+            map.remove(new Integer(16));
+            entry1.setValue("XYZ");
+            assertTrue(map.containsValue("XYZ")); // fails if write-through broken
+        }
+    }
+
+    /**
+     * Tests performance of removeAll when the other collection is much smaller.
+     * ant -Djsr166.tckTestClass=ConcurrentHashMapTest -Djsr166.methodFilter=testRemoveAll_performance -Djsr166.expensiveTests=true tck
+     */
+    public void testRemoveAll_performance() {
+        final int mapSize = expensiveTests ? 1_000_000 : 100;
+        final int iterations = expensiveTests ? 500 : 2;
+        final ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
+        for (int i = 0; i < mapSize; i++)
+            map.put(i, i);
+        Set<Integer> keySet = map.keySet();
+        Collection<Integer> removeMe = Arrays.asList(new Integer[] { -99, -86 });
+        for (int i = 0; i < iterations; i++)
+            assertFalse(keySet.removeAll(removeMe));
+        assertEquals(mapSize, map.size());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java
new file mode 100644
index 0000000..90dbc71
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentLinkedDequeTest.java
@@ -0,0 +1,937 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentLinkedDequeTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return ConcurrentLinkedDeque.class; }
+            public Collection emptyCollection() { return new ConcurrentLinkedDeque(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(ConcurrentLinkedDequeTest.class,
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private ConcurrentLinkedDeque<Integer> populatedDeque(int n) {
+        ConcurrentLinkedDeque<Integer> q = new ConcurrentLinkedDeque<>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        assertEquals((Integer) 0, q.peekFirst());
+        assertEquals((Integer) (n - 1), q.peekLast());
+        return q;
+    }
+
+    /**
+     * new deque is empty
+     */
+    public void testConstructor1() {
+        assertTrue(new ConcurrentLinkedDeque().isEmpty());
+        assertEquals(0, new ConcurrentLinkedDeque().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentLinkedDeque((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentLinkedDeque(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ConcurrentLinkedDeque(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size() changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * peekFirst() returns element inserted with push
+     */
+    public void testPush() {
+        ConcurrentLinkedDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop() removes first element, or throws NSEE if empty
+     */
+    public void testPop() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerFirst(null) throws NPE
+     */
+    public void testOfferFirstNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NPE
+     */
+    public void testOfferLastNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offer(x) succeeds
+     */
+    public void testOffer() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * offerFirst(x) succeeds
+     */
+    public void testOfferFirst() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offerFirst(zero));
+        assertTrue(q.offerFirst(one));
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * offerLast(x) succeeds
+     */
+    public void testOfferLast() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.offerLast(zero));
+        assertTrue(q.offerLast(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addFirst(null) throws NPE
+     */
+    public void testAddFirstNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addLast(null) throws NPE
+     */
+    public void testAddLastNull() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(x) succeeds
+     */
+    public void testAdd() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addFirst(x) succeeds
+     */
+    public void testAddFirst() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.addFirst(zero);
+        q.addFirst(one);
+        assertSame(one, q.peekFirst());
+        assertSame(zero, q.peekLast());
+    }
+
+    /**
+     * addLast(x) succeeds
+     */
+    public void testAddLast() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.addLast(zero);
+        q.addLast(one);
+        assertSame(zero, q.peekFirst());
+        assertSame(one, q.peekLast());
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * pollFirst() succeeds unless empty
+     */
+    public void testPollFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast() succeeds unless empty
+     */
+    public void testPollLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * poll() succeeds unless empty
+     */
+    public void testPoll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek() returns next element, or null if empty
+     */
+    public void testPeek() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element() returns first element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove() removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst() returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peekLast() returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear() removes all elements
+     */
+    public void testClear() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        ConcurrentLinkedDeque p = new ConcurrentLinkedDeque();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if change
+     */
+    public void testRetainAll() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        ConcurrentLinkedDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentLinkedDeque q = populatedDeque(SIZE);
+            ConcurrentLinkedDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ConcurrentLinkedDeque<Integer> q = populatedDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * Iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Deque c = new ConcurrentLinkedDeque();
+        assertIteratorExhausted(c.iterator());
+        assertIteratorExhausted(c.descendingIterator());
+    }
+
+    /**
+     * Iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+
+        assertEquals("deque should be empty again", 0, q.size());
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = 1; j <= max; ++j)
+                q.add(new Integer(j));
+            Iterator it = q.iterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.iterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove() removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();
+        final Random rng = new Random();
+        for (int iters = 0; iters < 100; ++iters) {
+            int max = rng.nextInt(5) + 2;
+            int split = rng.nextInt(max - 1) + 1;
+            for (int j = max; j >= 1; --j)
+                q.add(new Integer(j));
+            Iterator it = q.descendingIterator();
+            for (int j = 1; j <= split; ++j)
+                assertEquals(it.next(), new Integer(j));
+            it.remove();
+            assertEquals(it.next(), new Integer(split + 1));
+            for (int j = 1; j <= split; ++j)
+                q.remove(new Integer(j));
+            it = q.descendingIterator();
+            for (int j = split + 1; j <= max; ++j) {
+                assertEquals(it.next(), new Integer(j));
+                it.remove();
+            }
+            assertFalse(it.hasNext());
+            assertTrue(q.isEmpty());
+        }
+    }
+
+    /**
+     * toString() contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentLinkedDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * contains(null) always return false.
+     * remove(null) always throws NullPointerException.
+     */
+    public void testNeverContainsNull() {
+        Deque<?>[] qs = {
+            new ConcurrentLinkedDeque<Object>(),
+            populatedDeque(2),
+        };
+
+        for (Deque<?> q : qs) {
+            assertFalse(q.contains(null));
+            try {
+                assertFalse(q.remove(null));
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            try {
+                assertFalse(q.removeFirstOccurrence(null));
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            try {
+                assertFalse(q.removeLastOccurrence(null));
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java
new file mode 100644
index 0000000..1d1e875
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentLinkedQueueTest.java
@@ -0,0 +1,574 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentLinkedQueueTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return ConcurrentLinkedQueue.class; }
+            public Collection emptyCollection() { return new ConcurrentLinkedQueue(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(ConcurrentLinkedQueueTest.class,
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private ConcurrentLinkedQueue<Integer> populatedQueue(int n) {
+        ConcurrentLinkedQueue<Integer> q = new ConcurrentLinkedQueue<>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        assertEquals((Integer) 0, q.peek());
+        return q;
+    }
+
+    /**
+     * new queue is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ConcurrentLinkedQueue().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentLinkedQueue((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentLinkedQueue(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ConcurrentLinkedQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Offer returns true
+     */
+    public void testOffer() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * add returns true
+     */
+    public void testAdd() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        ConcurrentLinkedQueue p = new ConcurrentLinkedQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if change
+     */
+    public void testRetainAll() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        ConcurrentLinkedQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentLinkedQueue q = populatedQueue(SIZE);
+            ConcurrentLinkedQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        ConcurrentLinkedQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new ConcurrentLinkedQueue().iterator());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+
+        assertEquals("queue should be empty again", 0, q.size());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentLinkedQueue q = new ConcurrentLinkedQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+        it = q.iterator();
+        assertSame(it.next(), two);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentLinkedQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new ConcurrentLinkedQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java
new file mode 100644
index 0000000..7e8594d
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListMapTest.java
@@ -0,0 +1,1307 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentSkipListMap map5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentSkipListMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * copy constructor creates map equal to source map
+     */
+    public void testConstructFromSorted() {
+        ConcurrentSkipListMap map = map5();
+        ConcurrentSkipListMap map2 = new ConcurrentSkipListMap(map);
+        assertEquals(map, map2);
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentSkipListMap map1 = map5();
+        ConcurrentSkipListMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        ConcurrentSkipListMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of key set is inverse ordered
+     */
+    public void testKeySetDescendingIteratorOrder() {
+        ConcurrentSkipListMap map = map5();
+        NavigableSet s = map.navigableKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descendingKeySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingKeySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of descendingKeySet is ordered
+     */
+    public void testDescendingKeySetDescendingIteratorOrder() {
+        ConcurrentSkipListMap map = map5();
+        NavigableSet s = map.descendingKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentSkipListMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentSkipListMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * descendingEntrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * descendingEntrySet.toArray contains all entries
+     */
+    public void testDescendingEntrySetToArray() {
+        ConcurrentSkipListMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        ConcurrentSkipListMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentSkipListMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentSkipListMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentSkipListMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentSkipListMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentSkipListMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentSkipListMap map = map5();
+        assertTrue(map.containsKey(five));
+        assertEquals("E", map.get(five));
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * lowerEntry, higherEntry, ceilingEntry, and floorEntry return
+     * immutable entries
+     */
+    public void testEntryImmutability() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.lowerEntry(three);
+        assertEquals(two, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.higherEntry(zero);
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.floorEntry(one);
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.ceilingEntry(five);
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("X");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * lowerKey returns preceding element
+     */
+    public void testLowerKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.lowerKey(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lowerKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lowerKey(one);
+        assertNull(e3);
+
+        Object e4 = q.lowerKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherKey returns next element
+     */
+    public void testHigherKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.higherKey(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higherKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higherKey(five);
+        assertNull(e3);
+
+        Object e4 = q.higherKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorKey returns preceding element
+     */
+    public void testFloorKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.floorKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floorKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floorKey(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floorKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingKey returns next element
+     */
+    public void testCeilingKey() {
+        ConcurrentSkipListMap q = map5();
+        Object e1 = q.ceilingKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceilingKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceilingKey(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceilingKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        ConcurrentSkipListMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentSkipListMap map = map5();
+        ConcurrentSkipListMap empty = new ConcurrentSkipListMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentSkipListMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        try {
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        ConcurrentSkipListMap c = map5();
+        try {
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(x, null) returns false
+     */
+    public void testRemove3() {
+        ConcurrentSkipListMap c = new ConcurrentSkipListMap();
+        c.put("sadsdf", "asdads");
+        assertFalse(c.remove("sadsdf", null));
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.subMap(two, true, four, false);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.subMap(two, true, three, false);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.headMap(four, false);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * tailMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        ConcurrentSkipListMap map = map5();
+        NavigableMap sm = map.tailMap(two, true);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(five, k);
+        k = (Integer)(r.next());
+        assertEquals(four, k);
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        NavigableMap ssm = sm.tailMap(four, true);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Submaps of submaps subdivide correctly
+     */
+    public void testRecursiveSubMaps() throws Exception {
+        int mapSize = expensiveTests ? 1000 : 100;
+        Class cl = ConcurrentSkipListMap.class;
+        NavigableMap<Integer, Integer> map = newMap(cl);
+        bs = new BitSet(mapSize);
+
+        populate(map, mapSize);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        mutateMap(map, 0, mapSize - 1);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        bashSubMap(map.subMap(0, true, mapSize, false),
+                   0, mapSize - 1, true);
+    }
+
+    static NavigableMap<Integer, Integer> newMap(Class cl) throws Exception {
+        NavigableMap<Integer, Integer> result =
+            (NavigableMap<Integer, Integer>) cl.getConstructor().newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.keySet().iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableMap<Integer, Integer> map, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int key = rnd.nextInt(limit);
+            put(map, key);
+        }
+    }
+
+    void mutateMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min + rnd.nextInt(rangeSize);
+            assertTrue(key >= min && key <= max);
+            put(map, key);
+        }
+    }
+
+    void mutateSubMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (key >= min && key <= max) {
+                put(map, key);
+            } else {
+                try {
+                    map.put(key, 2 * key);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableMap<Integer, Integer> map, int key) {
+        if (map.put(key, 2 * key) == null)
+            bs.set(key);
+    }
+
+    void remove(NavigableMap<Integer, Integer> map, int key) {
+        if (map.remove(key) != null)
+            bs.clear(key);
+    }
+
+    void bashSubMap(NavigableMap<Integer, Integer> map,
+                    int min, int max, boolean ascending) {
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        mutateSubMap(map, min, max);
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headMap - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> hm = map.headMap(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailMap - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> tm = map.tailMap(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subMap - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableMap<Integer, Integer> map,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int key) {
+                return ascending ? lowerAscending(key) : higherAscending(key);
+            }
+            int floor(int key) {
+                return ascending ? floorAscending(key) : ceilingAscending(key);
+            }
+            int ceiling(int key) {
+                return ascending ? ceilingAscending(key) : floorAscending(key);
+            }
+            int higher(int key) {
+                return ascending ? higherAscending(key) : lowerAscending(key);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int key) {
+                return floorAscending(key - 1);
+            }
+            int floorAscending(int key) {
+                if (key < min)
+                    return -1;
+                else if (key > max)
+                    key = max;
+
+                // BitSet should support this! Test would run much faster
+                while (key >= min) {
+                    if (bs.get(key))
+                        return key;
+                    key--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int key) {
+                if (key < min)
+                    key = min;
+                else if (key > max)
+                    return -1;
+                int result = bs.nextSetBit(key);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int key) {
+                return ceilingAscending(key + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsKey
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, map.containsKey(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, map.size());
+
+        // Test contents using contains keySet iterator
+        int size2 = 0;
+        int previousKey = -1;
+        for (int key : map.keySet()) {
+            assertTrue(bs.get(key));
+            size2++;
+            assertTrue(previousKey < 0 ||
+                (ascending ? key - previousKey > 0 : key - previousKey < 0));
+            previousKey = key;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int key = min - 1; key <= max + 1; key++) {
+            assertEq(map.lowerKey(key), rs.lower(key));
+            assertEq(map.floorKey(key), rs.floor(key));
+            assertEq(map.higherKey(key), rs.higher(key));
+            assertEq(map.ceilingKey(key), rs.ceiling(key));
+        }
+
+        // Test extrema
+        if (map.size() != 0) {
+            assertEq(map.firstKey(), rs.first());
+            assertEq(map.lastKey(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                map.firstKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                map.lastKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return (i == null) ? j == -1 : i == j;
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java
new file mode 100644
index 0000000..d27c10f
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSetTest.java
@@ -0,0 +1,1016 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private ConcurrentSkipListSet<Integer> populatedSet(int n) {
+        ConcurrentSkipListSet<Integer> q =
+            new ConcurrentSkipListSet<Integer>();
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private ConcurrentSkipListSet set5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        assertEquals(5, q.size());
+        return q;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new ConcurrentSkipListSet().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new ConcurrentSkipListSet((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new ConcurrentSkipListSet(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new ConcurrentSkipListSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet(cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.add(zero));
+        assertFalse(q.add(zero));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {
+            assertTrue(q.size() < 2);
+            for (int i = 0, size = q.size(); i < size; i++)
+                assertTrue(q.pollFirst().getClass() == Object.class);
+            assertNull(q.pollFirst());
+            assertTrue(q.isEmpty());
+            assertEquals(0, q.size());
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, q.pollFirst());
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        ConcurrentSkipListSet p = new ConcurrentSkipListSet();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        ConcurrentSkipListSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            ConcurrentSkipListSet q = populatedSet(SIZE);
+            ConcurrentSkipListSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        ConcurrentSkipListSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        ConcurrentSkipListSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        NavigableSet s = new ConcurrentSkipListSet();
+        assertIteratorExhausted(s.iterator());
+        assertIteratorExhausted(s.descendingSet().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        ConcurrentSkipListSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        ConcurrentSkipListSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    Random rnd = new Random(666);
+
+    /**
+     * Subsets of subsets subdivide correctly
+     */
+    public void testRecursiveSubSets() throws Exception {
+        int setSize = expensiveTests ? 1000 : 100;
+        Class cl = ConcurrentSkipListSet.class;
+
+        NavigableSet<Integer> set = newSet(cl);
+        BitSet bs = new BitSet(setSize);
+
+        populate(set, setSize, bs);
+        check(set,                 0, setSize - 1, true, bs);
+        check(set.descendingSet(), 0, setSize - 1, false, bs);
+
+        mutateSet(set, 0, setSize - 1, bs);
+        check(set,                 0, setSize - 1, true, bs);
+        check(set.descendingSet(), 0, setSize - 1, false, bs);
+
+        bashSubSet(set.subSet(0, true, setSize, false),
+                   0, setSize - 1, true, bs);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new ConcurrentSkipListSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static NavigableSet<Integer> newSet(Class cl) throws Exception {
+        NavigableSet<Integer> result =
+            (NavigableSet<Integer>) cl.getConstructor().newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableSet<Integer> set, int limit, BitSet bs) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int element = rnd.nextInt(limit);
+            put(set, element, bs);
+        }
+    }
+
+    void mutateSet(NavigableSet<Integer> set, int min, int max, BitSet bs) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs);
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min + rnd.nextInt(rangeSize);
+            assertTrue(element >= min && element <= max);
+            put(set, element, bs);
+        }
+    }
+
+    void mutateSubSet(NavigableSet<Integer> set, int min, int max,
+                      BitSet bs) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10), bs);
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (element >= min && element <= max) {
+                put(set, element, bs);
+            } else {
+                try {
+                    set.add(element);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableSet<Integer> set, int element, BitSet bs) {
+        if (set.add(element))
+            bs.set(element);
+    }
+
+    void remove(NavigableSet<Integer> set, int element, BitSet bs) {
+        if (set.remove(element))
+            bs.clear(element);
+    }
+
+    void bashSubSet(NavigableSet<Integer> set,
+                    int min, int max, boolean ascending,
+                    BitSet bs) {
+        check(set, min, max, ascending, bs);
+        check(set.descendingSet(), min, max, !ascending, bs);
+
+        mutateSubSet(set, min, max, bs);
+        check(set, min, max, ascending, bs);
+        check(set.descendingSet(), min, max, !ascending, bs);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headSet - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableSet<Integer> hm = set.headSet(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true, bs);
+            else
+                bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           false, bs);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false, bs);
+            else
+                bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           true, bs);
+        }
+
+        // tailSet - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableSet<Integer> tm = set.tailSet(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true, bs);
+            else
+                bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           false, bs);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false, bs);
+            } else {
+                bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           true, bs);
+            }
+        }
+
+        // subSet - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true, bs);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false, bs);
+        } else {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false, bs);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true, bs);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableSet<Integer> set,
+               final int min, final int max, final boolean ascending,
+               final BitSet bs) {
+        class ReferenceSet {
+            int lower(int element) {
+                return ascending ?
+                    lowerAscending(element) : higherAscending(element);
+            }
+            int floor(int element) {
+                return ascending ?
+                    floorAscending(element) : ceilingAscending(element);
+            }
+            int ceiling(int element) {
+                return ascending ?
+                    ceilingAscending(element) : floorAscending(element);
+            }
+            int higher(int element) {
+                return ascending ?
+                    higherAscending(element) : lowerAscending(element);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int element) {
+                return floorAscending(element - 1);
+            }
+            int floorAscending(int element) {
+                if (element < min)
+                    return -1;
+                else if (element > max)
+                    element = max;
+
+                // BitSet should support this! Test would run much faster
+                while (element >= min) {
+                    if (bs.get(element))
+                        return element;
+                    element--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int element) {
+                if (element < min)
+                    element = min;
+                else if (element > max)
+                    return -1;
+                int result = bs.nextSetBit(element);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int element) {
+                return ceilingAscending(element + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsElement
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, set.contains(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, set.size());
+
+        // Test contents using contains elementSet iterator
+        int size2 = 0;
+        int previousElement = -1;
+        for (int element : set) {
+            assertTrue(bs.get(element));
+            size2++;
+            assertTrue(previousElement < 0 || (ascending ?
+                element - previousElement > 0 : element - previousElement < 0));
+            previousElement = element;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int element = min - 1; element <= max + 1; element++) {
+            assertEq(set.lower(element), rs.lower(element));
+            assertEq(set.floor(element), rs.floor(element));
+            assertEq(set.higher(element), rs.higher(element));
+            assertEq(set.ceiling(element), rs.ceiling(element));
+        }
+
+        // Test extrema
+        if (set.size() != 0) {
+            assertEq(set.first(), rs.first());
+            assertEq(set.last(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                set.first();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                set.last();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return (i == null) ? j == -1 : i == j;
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java
new file mode 100644
index 0000000..d48b9f9
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSubMapTest.java
@@ -0,0 +1,1451 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListSubMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListSubMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static ConcurrentNavigableMap map5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(zero, "Z");
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        map.put(seven, "F");
+        assertFalse(map.isEmpty());
+        assertEquals(7, map.size());
+        return map.subMap(one, true, seven, false);
+    }
+
+    /**
+     * Returns a new map from Integers -5 to -1 to Strings "A"-"E".
+     */
+    private static ConcurrentNavigableMap dmap5() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        map.put(m1, "A");
+        map.put(m5, "E");
+        map.put(m3, "C");
+        map.put(m2, "B");
+        map.put(m4, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map.descendingMap();
+    }
+
+    private static ConcurrentNavigableMap map0() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        return map.tailMap(one, true);
+    }
+
+    private static ConcurrentNavigableMap dmap0() {
+        ConcurrentSkipListMap map = new ConcurrentSkipListMap();
+        assertTrue(map.isEmpty());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        ConcurrentNavigableMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        ConcurrentNavigableMap map1 = map5();
+        ConcurrentNavigableMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        ConcurrentNavigableMap empty = map0();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        ConcurrentNavigableMap empty = map0();
+        ConcurrentNavigableMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        ConcurrentNavigableMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testValuesToArray() {
+        ConcurrentNavigableMap map = map5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        ConcurrentNavigableMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        ConcurrentNavigableMap empty = map0();
+        ConcurrentNavigableMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testPutIfAbsent() {
+        ConcurrentNavigableMap map = map5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testPutIfAbsent2() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.putIfAbsent(one, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testReplace() {
+        ConcurrentNavigableMap map = map5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testReplace2() {
+        ConcurrentNavigableMap map = map5();
+        assertNotNull(map.replace(one, "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testReplaceValue() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.get(one));
+        assertFalse(map.replace(one, "Z", "Z"));
+        assertEquals("A", map.get(one));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testReplaceValue2() {
+        ConcurrentNavigableMap map = map5();
+        assertEquals("A", map.get(one));
+        assertTrue(map.replace(one, "A", "Z"));
+        assertEquals("Z", map.get(one));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        ConcurrentNavigableMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testRemove2() {
+        ConcurrentNavigableMap map = map5();
+        assertTrue(map.containsKey(five));
+        assertEquals("E", map.get(five));
+        map.remove(five, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+        map.remove(four, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(four));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        ConcurrentNavigableMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        ConcurrentNavigableMap map = map5();
+        ConcurrentNavigableMap empty = map0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        ConcurrentNavigableMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testContainsValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map0();
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testReplace_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testReplaceValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.replace(null, one, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testRemove2_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = map5();
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.subMap(two, four);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.subMap(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.headMap(four);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        ConcurrentNavigableMap map = map5();
+        SortedMap sm = map.tailMap(two);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(four);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testDescendingClear() {
+        ConcurrentNavigableMap map = dmap5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testDescendingEquals() {
+        ConcurrentNavigableMap map1 = dmap5();
+        ConcurrentNavigableMap map2 = dmap5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testDescendingContainsKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsKey(m1));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testDescendingContainsValue() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testDescendingGet() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", (String)map.get(m1));
+        ConcurrentNavigableMap empty = dmap0();
+        assertNull(empty.get(m1));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testDescendingIsEmpty() {
+        ConcurrentNavigableMap empty = dmap0();
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testDescendingFirstKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals(m1, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testDescendingLastKey() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals(m5, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testDescendingKeySet() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(m1));
+        assertTrue(s.contains(m2));
+        assertTrue(s.contains(m3));
+        assertTrue(s.contains(m4));
+        assertTrue(s.contains(m5));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, m1);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testDescendingValues() {
+        ConcurrentNavigableMap map = dmap5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testDescendingAscendingKeySetToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingDescendingKeySetToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testDescendingValuesToArray() {
+        ConcurrentNavigableMap map = dmap5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        ConcurrentNavigableMap map = dmap5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(m1) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(m2) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(m3) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(m4) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(m5) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testDescendingPutAll() {
+        ConcurrentNavigableMap empty = dmap0();
+        ConcurrentNavigableMap map = dmap5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(m1));
+        assertTrue(empty.containsKey(m2));
+        assertTrue(empty.containsKey(m3));
+        assertTrue(empty.containsKey(m4));
+        assertTrue(empty.containsKey(m5));
+    }
+
+    /**
+     * putIfAbsent works when the given key is not present
+     */
+    public void testDescendingPutIfAbsent() {
+        ConcurrentNavigableMap map = dmap5();
+        map.putIfAbsent(six, "Z");
+        assertTrue(map.containsKey(six));
+    }
+
+    /**
+     * putIfAbsent does not add the pair if the key is already present
+     */
+    public void testDescendingPutIfAbsent2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.putIfAbsent(m1, "Z"));
+    }
+
+    /**
+     * replace fails when the given key is not present
+     */
+    public void testDescendingReplace() {
+        ConcurrentNavigableMap map = dmap5();
+        assertNull(map.replace(six, "Z"));
+        assertFalse(map.containsKey(six));
+    }
+
+    /**
+     * replace succeeds if the key is already present
+     */
+    public void testDescendingReplace2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertNotNull(map.replace(m1, "Z"));
+        assertEquals("Z", map.get(m1));
+    }
+
+    /**
+     * replace value fails when the given key not mapped to expected value
+     */
+    public void testDescendingReplaceValue() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.get(m1));
+        assertFalse(map.replace(m1, "Z", "Z"));
+        assertEquals("A", map.get(m1));
+    }
+
+    /**
+     * replace value succeeds when the given key mapped to expected value
+     */
+    public void testDescendingReplaceValue2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertEquals("A", map.get(m1));
+        assertTrue(map.replace(m1, "A", "Z"));
+        assertEquals("Z", map.get(m1));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testDescendingRemove() {
+        ConcurrentNavigableMap map = dmap5();
+        map.remove(m5);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+    }
+
+    /**
+     * remove(key,value) removes only if pair present
+     */
+    public void testDescendingRemove2() {
+        ConcurrentNavigableMap map = dmap5();
+        assertTrue(map.containsKey(m5));
+        assertEquals("E", map.get(m5));
+        map.remove(m5, "E");
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+        map.remove(m4, "A");
+        assertEquals(4, map.size());
+        assertTrue(map.containsKey(m4));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testDescendingLowerEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.lowerEntry(m3);
+        assertEquals(m2, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(m1);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testDescendingHigherEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.higherEntry(m3);
+        assertEquals(m4, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(m5);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testDescendingFloorEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.floorEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(m1);
+        assertEquals(m1, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testDescendingCeilingEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e1 = map.ceilingEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(m5);
+        assertEquals(m5, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testDescendingPollFirstEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m2, e.getKey());
+        map.put(m1, "A");
+        e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m4);
+        e = map.pollFirstEntry();
+        assertEquals(m5, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testDescendingPollLastEntry() {
+        ConcurrentNavigableMap map = dmap5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m4, e.getKey());
+        map.put(m5, "E");
+        e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m2);
+        e = map.pollLastEntry();
+        assertEquals(m1, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testDescendingSize() {
+        ConcurrentNavigableMap map = dmap5();
+        ConcurrentNavigableMap empty = dmap0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testDescendingToString() {
+        ConcurrentNavigableMap map = dmap5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception testDescendings
+
+    /**
+     * get(null) of empty map throws NPE
+     */
+    public void testDescendingGet_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of empty map throws NPE
+     */
+    public void testDescendingContainsKey_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsValue(null) throws NPE
+     */
+    public void testDescendingContainsValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap0();
+            c.containsValue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testDescendingPut1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * putIfAbsent(null, x) throws NPE
+     */
+    public void testDescendingPutIfAbsent1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.putIfAbsent(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x) throws NPE
+     */
+    public void testDescendingReplace_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.replace(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * replace(null, x, y) throws NPE
+     */
+    public void testDescendingReplaceValue_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.replace(null, m1, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testDescendingRemove1_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null, x) throws NPE
+     */
+    public void testDescendingRemove2_NullPointerException() {
+        try {
+            ConcurrentNavigableMap c = dmap5();
+            c.remove(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableMap x = dmap5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testDescendingSubMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m4);
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals("C", sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testDescendingSubMapContents2() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m2, sm.lastKey());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertFalse(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(m3), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingHeadMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.headMap(m4);
+        assertTrue(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(m4, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingTailMapContents() {
+        ConcurrentNavigableMap map = dmap5();
+        SortedMap sm = map.tailMap(m2);
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertTrue(sm.containsKey(m4));
+        assertTrue(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(m2, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m3, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m4, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(m4);
+        assertEquals(m4, ssm.firstKey());
+        assertEquals(m5, ssm.lastKey());
+        assertEquals("D", ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java
new file mode 100644
index 0000000..042fc62
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ConcurrentSkipListSubSetTest.java
@@ -0,0 +1,1142 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ConcurrentSkipListSubSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ConcurrentSkipListSubSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private NavigableSet<Integer> populatedSet(int n) {
+        ConcurrentSkipListSet<Integer> q =
+            new ConcurrentSkipListSet<Integer>();
+        assertTrue(q.isEmpty());
+
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertTrue(q.add(new Integer(-n)));
+        assertTrue(q.add(new Integer(n)));
+        NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false);
+        assertFalse(s.isEmpty());
+        assertEquals(n, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private NavigableSet set5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        q.add(zero);
+        q.add(seven);
+        NavigableSet s = q.subSet(one, true, seven, false);
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 negative ints.
+     */
+    private NavigableSet dset5() {
+        ConcurrentSkipListSet q = new ConcurrentSkipListSet();
+        assertTrue(q.isEmpty());
+        q.add(m1);
+        q.add(m2);
+        q.add(m3);
+        q.add(m4);
+        q.add(m5);
+        NavigableSet s = q.descendingSet();
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private static NavigableSet set0() {
+        ConcurrentSkipListSet set = new ConcurrentSkipListSet();
+        assertTrue(set.isEmpty());
+        return set.tailSet(m1, true);
+    }
+
+    private static NavigableSet dset0() {
+        ConcurrentSkipListSet set = new ConcurrentSkipListSet();
+        assertTrue(set.isEmpty());
+        return set;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, set0().size());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        NavigableSet q = set0();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        NavigableSet q = set0();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+        assertFalse(q.add(six));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        NavigableSet q = set0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        NavigableSet q = set0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = set0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = set0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        NavigableSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        NavigableSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        NavigableSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        NavigableSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        NavigableSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(set0().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final NavigableSet q = set0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testDescendingSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testDescendingAddNull() {
+        NavigableSet q = dset0();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testDescendingAdd() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testDescendingAddDup() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+        assertFalse(q.add(m6));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testDescendingAddNonComparable() {
+        NavigableSet q = dset0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testDescendingAddAll1() {
+        NavigableSet q = dset0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testDescendingAddAll2() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testDescendingAddAll3() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testDescendingAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = dset0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testDescendingPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testDescendingRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.remove(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2 ) {
+            assertTrue(q.remove(new Integer(i)));
+            assertFalse(q.remove(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testDescendingContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testDescendingClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testDescendingContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = dset0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testDescendingRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testDescendingRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testDescendingLower() {
+        NavigableSet q = dset5();
+        Object e1 = q.lower(m3);
+        assertEquals(m2, e1);
+
+        Object e2 = q.lower(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.lower(m1);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testDescendingHigher() {
+        NavigableSet q = dset5();
+        Object e1 = q.higher(m3);
+        assertEquals(m4, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.higher(m5);
+        assertNull(e3);
+
+        Object e4 = q.higher(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testDescendingFloor() {
+        NavigableSet q = dset5();
+        Object e1 = q.floor(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.floor(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.floor(m1);
+        assertEquals(m1, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testDescendingCeiling() {
+        NavigableSet q = dset5();
+        Object e1 = q.ceiling(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.ceiling(m5);
+        assertEquals(m5, e3);
+
+        Object e4 = q.ceiling(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testDescendingToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertEquals(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testDescendingToArray2() {
+        NavigableSet q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testDescendingEmptyIterator() {
+        NavigableSet q = dset0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final NavigableSet q = dset0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testDescendingToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableSet x = dset5();
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testDescendingSubSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m4);
+        assertEquals(m2, sm.first());
+        assertEquals(m3, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.first());
+        assertEquals(m3, sm.last());
+        assertTrue(sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testDescendingSubSetContents2() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.first());
+        assertEquals(m2, sm.last());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertFalse(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(m3));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testDescendingHeadSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.headSet(m4);
+        assertTrue(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(m4, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testDescendingTailSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.tailSet(m2);
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertTrue(sm.contains(m4));
+        assertTrue(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(m4);
+        assertEquals(m4, ssm.first());
+        assertEquals(m5, ssm.last());
+        assertTrue(ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java b/ojluni/src/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java
new file mode 100644
index 0000000..70409c3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CopyOnWriteArrayListTest.java
@@ -0,0 +1,798 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CopyOnWriteArrayListTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return CopyOnWriteArrayList.class; }
+            public List emptyCollection() { return new CopyOnWriteArrayList(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return true; }
+        }
+        class SubListImplementation extends Implementation {
+            public List emptyCollection() {
+                return super.emptyCollection().subList(0, 0);
+            }
+        }
+        return newTestSuite(
+                CopyOnWriteArrayListTest.class,
+                CollectionTest.testSuite(new Implementation()),
+                CollectionTest.testSuite(new SubListImplementation()));
+    }
+
+    static CopyOnWriteArrayList<Integer> populatedArray(int n) {
+        CopyOnWriteArrayList<Integer> a = new CopyOnWriteArrayList<>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            a.add(i);
+        assertFalse(a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static CopyOnWriteArrayList<Integer> populatedArray(Integer[] elements) {
+        CopyOnWriteArrayList<Integer> a = new CopyOnWriteArrayList<>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            a.add(elements[i]);
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * a new list is empty
+     */
+    public void testConstructor() {
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * new list contains all elements of initializing array
+     */
+    public void testConstructor2() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList(ints);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], a.get(i));
+    }
+
+    /**
+     * new list contains all elements of initializing collection
+     */
+    public void testConstructor3() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArrayList a = new CopyOnWriteArrayList(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], a.get(i));
+    }
+
+    /**
+     * addAll adds each element from the given collection, including duplicates
+     */
+    public void testAddAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(9, full.size());
+    }
+
+    /**
+     * addAllAbsent adds each element from the given collection that did not
+     * already exist in the List
+     */
+    public void testAddAllAbsent() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        // "one" is duplicate and will not be added
+        assertEquals(2, full.addAllAbsent(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+        assertEquals(0, full.addAllAbsent(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * addIfAbsent will not add the element if it already exists in the list
+     */
+    public void testAddIfAbsent() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.addIfAbsent(one);
+        assertEquals(SIZE, full.size());
+    }
+
+    /**
+     * addIfAbsent adds the element when it does not exist in the list
+     */
+    public void testAddIfAbsent2() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.addIfAbsent(three);
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * clear removes all elements from the list
+     */
+    public void testClear() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        full.clear();
+        assertEquals(0, full.size());
+    }
+
+    /**
+     * Cloned list is equal
+     */
+    public void testClone() {
+        CopyOnWriteArrayList l1 = populatedArray(SIZE);
+        CopyOnWriteArrayList l2 = (CopyOnWriteArrayList)(l1.clone());
+        assertEquals(l1, l2);
+        l1.clear();
+        assertFalse(l1.equals(l2));
+    }
+
+    /**
+     * contains is true for added elements
+     */
+    public void testContains() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * adding at an index places it in the indicated index
+     */
+    public void testAddIndex() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(0, m1);
+        assertEquals(4, full.size());
+        assertEquals(m1, full.get(0));
+        assertEquals(zero, full.get(1));
+
+        full.add(2, m2);
+        assertEquals(5, full.size());
+        assertEquals(m2, full.get(2));
+        assertEquals(two, full.get(4));
+    }
+
+    /**
+     * lists with same elements are equal and have same hashCode
+     */
+    public void testEquals() {
+        CopyOnWriteArrayList a = populatedArray(3);
+        CopyOnWriteArrayList b = populatedArray(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertFalse(b.containsAll(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+
+        assertFalse(a.equals(null));
+    }
+
+    /**
+     * containsAll returns true for collections with subset of elements
+     */
+    public void testContainsAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.containsAll(Arrays.asList()));
+        assertTrue(full.containsAll(Arrays.asList(one)));
+        assertTrue(full.containsAll(Arrays.asList(one, two)));
+        assertFalse(full.containsAll(Arrays.asList(one, two, six)));
+        assertFalse(full.containsAll(Arrays.asList(six)));
+
+        try {
+            full.containsAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * get returns the value at the given index
+     */
+    public void testGet() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(0, full.get(0));
+    }
+
+    /**
+     * indexOf gives the index for the given object
+     */
+    public void testIndexOf() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(1, full.indexOf(one));
+        assertEquals(-1, full.indexOf("puppies"));
+    }
+
+    /**
+     * indexOf gives the index based on the given index
+     * at which to start searching
+     */
+    public void testIndexOf2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(1, full.indexOf(one, 0));
+        assertEquals(-1, full.indexOf(one, 2));
+    }
+
+    /**
+     * isEmpty returns true when empty, else false
+     */
+    public void testIsEmpty() {
+        CopyOnWriteArrayList empty = new CopyOnWriteArrayList();
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        assertTrue(empty.isEmpty());
+        assertFalse(full.isEmpty());
+    }
+
+    /**
+     * iterator() returns an iterator containing the elements of the
+     * list in insertion order
+     */
+    public void testIterator() {
+        Collection empty = new CopyOnWriteArrayList();
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedArray(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < SIZE; j++) {
+            assertTrue(it.hasNext());
+            assertEquals(elements[j], it.next());
+        }
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Collection c = new CopyOnWriteArrayList();
+        assertIteratorExhausted(c.iterator());
+    }
+
+    /**
+     * iterator.remove throws UnsupportedOperationException
+     */
+    public void testIteratorRemove() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        Iterator it = full.iterator();
+        it.next();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", new CopyOnWriteArrayList().toString());
+        CopyOnWriteArrayList full = populatedArray(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+        assertEquals(new ArrayList(full).toString(),
+                     full.toString());
+    }
+
+    /**
+     * lastIndexOf returns the index for the given object
+     */
+    public void testLastIndexOf1() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(one);
+        full.add(three);
+        assertEquals(3, full.lastIndexOf(one));
+        assertEquals(-1, full.lastIndexOf(six));
+    }
+
+    /**
+     * lastIndexOf returns the index from the given starting point
+     */
+    public void testLastIndexOf2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        full.add(one);
+        full.add(three);
+        assertEquals(3, full.lastIndexOf(one, 4));
+        assertEquals(-1, full.lastIndexOf(three, 3));
+    }
+
+    /**
+     * listIterator traverses all elements
+     */
+    public void testListIterator1() {
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        ListIterator i = full.listIterator();
+        int j;
+        for (j = 0; i.hasNext(); j++)
+            assertEquals(j, i.next());
+        assertEquals(SIZE, j);
+    }
+
+    /**
+     * listIterator only returns those elements after the given index
+     */
+    public void testListIterator2() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        ListIterator i = full.listIterator(1);
+        int j;
+        for (j = 0; i.hasNext(); j++)
+            assertEquals(j + 1, i.next());
+        assertEquals(2, j);
+    }
+
+    /**
+     * remove(int) removes and returns the object at the given index
+     */
+    public void testRemove_int() {
+        int SIZE = 3;
+        for (int i = 0; i < SIZE; i++) {
+            CopyOnWriteArrayList full = populatedArray(SIZE);
+            assertEquals(i, full.remove(i));
+            assertEquals(SIZE - 1, full.size());
+            assertFalse(full.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * remove(Object) removes the object if found and returns true
+     */
+    public void testRemove_Object() {
+        int SIZE = 3;
+        for (int i = 0; i < SIZE; i++) {
+            CopyOnWriteArrayList full = populatedArray(SIZE);
+            assertFalse(full.remove(new Integer(-42)));
+            assertTrue(full.remove(new Integer(i)));
+            assertEquals(SIZE - 1, full.size());
+            assertFalse(full.contains(new Integer(i)));
+        }
+        CopyOnWriteArrayList x = new CopyOnWriteArrayList(Arrays.asList(4, 5, 6));
+        assertTrue(x.remove(new Integer(6)));
+        assertEquals(x, Arrays.asList(4, 5));
+        assertTrue(x.remove(new Integer(4)));
+        assertEquals(x, Arrays.asList(5));
+        assertTrue(x.remove(new Integer(5)));
+        assertEquals(x, Arrays.asList());
+        assertFalse(x.remove(new Integer(5)));
+    }
+
+    /**
+     * removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertTrue(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+        assertFalse(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * set changes the element at the given index
+     */
+    public void testSet() {
+        CopyOnWriteArrayList full = populatedArray(3);
+        assertEquals(2, full.set(2, four));
+        assertEquals(4, full.get(2));
+    }
+
+    /**
+     * size returns the number of elements
+     */
+    public void testSize() {
+        CopyOnWriteArrayList empty = new CopyOnWriteArrayList();
+        CopyOnWriteArrayList full = populatedArray(SIZE);
+        assertEquals(SIZE, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * toArray() returns an Object array containing all elements from
+     * the list in insertion order
+     */
+    public void testToArray() {
+        Object[] a = new CopyOnWriteArrayList().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedArray(elements);
+
+        assertTrue(Arrays.equals(elements, full.toArray()));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the list in insertion order
+     */
+    public void testToArray2() {
+        Collection empty = new CopyOnWriteArrayList();
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[SIZE / 2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedArray(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.equals(elements, full.toArray(a)));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, a));
+
+        a = new Integer[2 * SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
+        assertNull(a[SIZE]);
+        for (int i = SIZE + 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * sublists contains elements at indexes offset from their base
+     */
+    public void testSubList() {
+        CopyOnWriteArrayList a = populatedArray(10);
+        assertTrue(a.subList(1,1).isEmpty());
+        for (int j = 0; j < 9; ++j) {
+            for (int i = j ; i < 10; ++i) {
+                List b = a.subList(j,i);
+                for (int k = j; k < i; ++k) {
+                    assertEquals(new Integer(k), b.get(k-j));
+                }
+            }
+        }
+
+        List s = a.subList(2, 5);
+        assertEquals(3, s.size());
+        s.set(2, m1);
+        assertEquals(a.get(4), m1);
+        s.clear();
+        assertEquals(7, a.size());
+    }
+
+    // Exception tests
+
+    /**
+     * toArray throws an ArrayStoreException when the given array
+     * can not store the objects inside the list
+     */
+    public void testToArray_ArrayStoreException() {
+        CopyOnWriteArrayList c = new CopyOnWriteArrayList();
+        c.add("zfasdfsdf");
+        c.add("asdadasd");
+        try {
+            c.toArray(new Long[5]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * get throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testGet1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.get(-1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * get throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testGet2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.get(list.size());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * set throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testSet1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.set(-1, "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * set throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testSet2() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.set(list.size(), "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * add throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testAdd1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.add(-1, "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * add throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testAdd2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.add(list.size() + 1, "qwerty");
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * remove throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testRemove1_IndexOutOfBounds() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.remove(-1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * remove throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testRemove2_IndexOutOfBounds() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.remove(list.size());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * addAll throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testAddAll1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.addAll(-1, new LinkedList());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * addAll throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testAddAll2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.addAll(list.size() + 1, new LinkedList());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * listIterator throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testListIterator1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.listIterator(-1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * listIterator throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testListIterator2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.listIterator(list.size() + 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * subList throws an IndexOutOfBoundsException on a negative index
+     */
+    public void testSubList1_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.subList(-1, list.size());
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * subList throws an IndexOutOfBoundsException on a too high index
+     */
+    public void testSubList2_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.subList(0, list.size() + 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * subList throws IndexOutOfBoundsException when the second index
+     * is lower then the first
+     */
+    public void testSubList3_IndexOutOfBoundsException() {
+        CopyOnWriteArrayList c = populatedArray(5);
+        List[] lists = { c, c.subList(1, c.size() - 1) };
+        for (List list : lists) {
+            try {
+                list.subList(list.size() - 1, 1);
+                shouldThrow();
+            } catch (IndexOutOfBoundsException success) {}
+        }
+    }
+
+    /**
+     * a deserialized serialized list is equal
+     */
+    public void testSerialization() throws Exception {
+        List x = populatedArray(SIZE);
+        List y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(0), y.remove(0));
+        }
+        assertTrue(y.isEmpty());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java b/ojluni/src/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java
new file mode 100644
index 0000000..d7fea36
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CopyOnWriteArraySetTest.java
@@ -0,0 +1,432 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CopyOnWriteArraySetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CopyOnWriteArraySetTest.class);
+    }
+
+    static CopyOnWriteArraySet<Integer> populatedSet(int n) {
+        CopyOnWriteArraySet<Integer> a = new CopyOnWriteArraySet<>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < n; i++)
+            a.add(i);
+        assertEquals(n == 0, a.isEmpty());
+        assertEquals(n, a.size());
+        return a;
+    }
+
+    static CopyOnWriteArraySet populatedSet(Integer[] elements) {
+        CopyOnWriteArraySet<Integer> a = new CopyOnWriteArraySet<>();
+        assertTrue(a.isEmpty());
+        for (int i = 0; i < elements.length; i++)
+            a.add(elements[i]);
+        assertFalse(a.isEmpty());
+        assertEquals(elements.length, a.size());
+        return a;
+    }
+
+    /**
+     * Default-constructed set is empty
+     */
+    public void testConstructor() {
+        CopyOnWriteArraySet a = new CopyOnWriteArraySet();
+        assertTrue(a.isEmpty());
+    }
+
+    /**
+     * Collection-constructed set holds all of its elements
+     */
+    public void testConstructor3() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        CopyOnWriteArraySet a = new CopyOnWriteArraySet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(a.contains(ints[i]));
+    }
+
+    /**
+     * addAll adds each non-duplicate element from the given collection
+     */
+    public void testAddAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, five)));
+        assertEquals(6, full.size());
+    }
+
+    /**
+     * addAll adds each non-duplicate element from the given collection
+     */
+    public void testAddAll2() {
+        Set full = populatedSet(3);
+        // "one" is duplicate and will not be added
+        assertTrue(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+        assertFalse(full.addAll(Arrays.asList(three, four, one)));
+        assertEquals(5, full.size());
+    }
+
+    /**
+     * add will not add the element if it already exists in the set
+     */
+    public void testAdd2() {
+        Set full = populatedSet(3);
+        full.add(one);
+        assertEquals(3, full.size());
+    }
+
+    /**
+     * add adds the element when it does not exist in the set
+     */
+    public void testAdd3() {
+        Set full = populatedSet(3);
+        full.add(three);
+        assertTrue(full.contains(three));
+    }
+
+    /**
+     * clear removes all elements from the set
+     */
+    public void testClear() {
+        Collection full = populatedSet(3);
+        full.clear();
+        assertEquals(0, full.size());
+        assertTrue(full.isEmpty());
+    }
+
+    /**
+     * contains returns true for added elements
+     */
+    public void testContains() {
+        Collection full = populatedSet(3);
+        assertTrue(full.contains(one));
+        assertFalse(full.contains(five));
+    }
+
+    /**
+     * Sets with equal elements are equal
+     */
+    public void testEquals() {
+        CopyOnWriteArraySet a = populatedSet(3);
+        CopyOnWriteArraySet b = populatedSet(3);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        assertEquals(a.size(), b.size());
+
+        a.add(m1);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertFalse(b.containsAll(a));
+        b.add(m1);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+
+        Object x = a.iterator().next();
+        a.remove(x);
+        assertFalse(a.equals(b));
+        assertFalse(b.equals(a));
+        assertFalse(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        a.add(x);
+        assertTrue(a.equals(b));
+        assertTrue(b.equals(a));
+        assertTrue(a.containsAll(b));
+        assertTrue(b.containsAll(a));
+        assertEquals(a.hashCode(), b.hashCode());
+        assertEquals(a.size(), b.size());
+
+        CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList());
+        CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList());
+        assertTrue(empty1.equals(empty1));
+        assertTrue(empty1.equals(empty2));
+
+        assertFalse(empty1.equals(a));
+        assertFalse(a.equals(empty1));
+
+        assertFalse(a.equals(null));
+    }
+
+    /**
+     * containsAll returns true for collections with subset of elements
+     */
+    public void testContainsAll() {
+        Collection full = populatedSet(3);
+        assertTrue(full.containsAll(full));
+        assertTrue(full.containsAll(Arrays.asList()));
+        assertTrue(full.containsAll(Arrays.asList(one)));
+        assertTrue(full.containsAll(Arrays.asList(one, two)));
+        assertFalse(full.containsAll(Arrays.asList(one, two, six)));
+        assertFalse(full.containsAll(Arrays.asList(six)));
+
+        CopyOnWriteArraySet empty1 = new CopyOnWriteArraySet(Arrays.asList());
+        CopyOnWriteArraySet empty2 = new CopyOnWriteArraySet(Arrays.asList());
+        assertTrue(empty1.containsAll(empty2));
+        assertTrue(empty1.containsAll(empty1));
+        assertFalse(empty1.containsAll(full));
+        assertTrue(full.containsAll(empty1));
+
+        try {
+            full.containsAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isEmpty is true when empty, else false
+     */
+    public void testIsEmpty() {
+        assertTrue(populatedSet(0).isEmpty());
+        assertFalse(populatedSet(3).isEmpty());
+    }
+
+    /**
+     * iterator() returns an iterator containing the elements of the
+     * set in insertion order
+     */
+    public void testIterator() {
+        Collection empty = new CopyOnWriteArraySet();
+        assertFalse(empty.iterator().hasNext());
+        try {
+            empty.iterator().next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedSet(elements);
+
+        Iterator it = full.iterator();
+        for (int j = 0; j < SIZE; j++) {
+            assertTrue(it.hasNext());
+            assertEquals(elements[j], it.next());
+        }
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new CopyOnWriteArraySet().iterator());
+    }
+
+    /**
+     * iterator remove is unsupported
+     */
+    public void testIteratorRemove() {
+        Collection full = populatedSet(3);
+        Iterator it = full.iterator();
+        it.next();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * toString holds toString of elements
+     */
+    public void testToString() {
+        assertEquals("[]", new CopyOnWriteArraySet().toString());
+        Collection full = populatedSet(3);
+        String s = full.toString();
+        for (int i = 0; i < 3; ++i)
+            assertTrue(s.contains(String.valueOf(i)));
+        assertEquals(new ArrayList(full).toString(),
+                     full.toString());
+    }
+
+    /**
+     * removeAll removes all elements from the given collection
+     */
+    public void testRemoveAll() {
+        Set full = populatedSet(3);
+        assertTrue(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+        assertFalse(full.removeAll(Arrays.asList(one, two)));
+        assertEquals(1, full.size());
+    }
+
+    /**
+     * remove removes an element
+     */
+    public void testRemove() {
+        Collection full = populatedSet(3);
+        full.remove(one);
+        assertFalse(full.contains(one));
+        assertEquals(2, full.size());
+    }
+
+    /**
+     * size returns the number of elements
+     */
+    public void testSize() {
+        Collection empty = new CopyOnWriteArraySet();
+        Collection full = populatedSet(3);
+        assertEquals(3, full.size());
+        assertEquals(0, empty.size());
+    }
+
+    /**
+     * toArray() returns an Object array containing all elements from
+     * the set in insertion order
+     */
+    public void testToArray() {
+        Object[] a = new CopyOnWriteArraySet().toArray();
+        assertTrue(Arrays.equals(new Object[0], a));
+        assertSame(Object[].class, a.getClass());
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedSet(elements);
+
+        assertTrue(Arrays.equals(elements, full.toArray()));
+        assertSame(Object[].class, full.toArray().getClass());
+    }
+
+    /**
+     * toArray(Integer array) returns an Integer array containing all
+     * elements from the set in insertion order
+     */
+    public void testToArray2() {
+        Collection empty = new CopyOnWriteArraySet();
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, empty.toArray(a));
+
+        a = new Integer[SIZE / 2];
+        Arrays.fill(a, 42);
+        assertSame(a, empty.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+
+        Integer[] elements = new Integer[SIZE];
+        for (int i = 0; i < SIZE; i++)
+            elements[i] = i;
+        shuffle(elements);
+        Collection<Integer> full = populatedSet(elements);
+
+        Arrays.fill(a, 42);
+        assertTrue(Arrays.equals(elements, full.toArray(a)));
+        for (int i = 0; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+        assertSame(Integer[].class, full.toArray(a).getClass());
+
+        a = new Integer[SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, a));
+
+        a = new Integer[2 * SIZE];
+        Arrays.fill(a, 42);
+        assertSame(a, full.toArray(a));
+        assertTrue(Arrays.equals(elements, Arrays.copyOf(a, SIZE)));
+        assertNull(a[SIZE]);
+        for (int i = SIZE + 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * toArray throws an ArrayStoreException when the given array can
+     * not store the objects inside the set
+     */
+    public void testToArray_ArrayStoreException() {
+        CopyOnWriteArraySet c = new CopyOnWriteArraySet();
+        c.add("zfasdfsdf");
+        c.add("asdadasd");
+        try {
+            c.toArray(new Long[5]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * A deserialized serialized set is equal
+     */
+    public void testSerialization() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new CopyOnWriteArraySet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CountDownLatchTest.java b/ojluni/src/test/java/util/concurrent/tck/CountDownLatchTest.java
new file mode 100644
index 0000000..e9ab578
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CountDownLatchTest.java
@@ -0,0 +1,224 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CountDownLatchTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CountDownLatchTest.class);
+    }
+
+    /**
+     * negative constructor argument throws IAE
+     */
+    public void testConstructor() {
+        try {
+            new CountDownLatch(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getCount returns initial count and decreases after countDown
+     */
+    public void testGetCount() {
+        final CountDownLatch l = new CountDownLatch(2);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+    }
+
+    /**
+     * countDown decrements count when positive and has no effect when zero
+     */
+    public void testCountDown() {
+        final CountDownLatch l = new CountDownLatch(1);
+        assertEquals(1, l.getCount());
+        l.countDown();
+        assertEquals(0, l.getCount());
+        l.countDown();
+        assertEquals(0, l.getCount());
+    }
+
+    /**
+     * await returns after countDown to zero, but not before
+     */
+    public void testAwait() {
+        final CountDownLatch l = new CountDownLatch(2);
+        final CountDownLatch pleaseCountDown = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(2, l.getCount());
+                pleaseCountDown.countDown();
+                l.await();
+                assertEquals(0, l.getCount());
+            }});
+
+        await(pleaseCountDown);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+        assertThreadStaysAlive(t);
+        l.countDown();
+        assertEquals(0, l.getCount());
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await returns after countDown to zero
+     */
+    public void testTimedAwait() {
+        final CountDownLatch l = new CountDownLatch(2);
+        final CountDownLatch pleaseCountDown = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(2, l.getCount());
+                pleaseCountDown.countDown();
+                assertTrue(l.await(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, l.getCount());
+            }});
+
+        await(pleaseCountDown);
+        assertEquals(2, l.getCount());
+        l.countDown();
+        assertEquals(1, l.getCount());
+        assertThreadStaysAlive(t);
+        l.countDown();
+        assertEquals(0, l.getCount());
+        awaitTermination(t);
+    }
+
+    /**
+     * await throws IE if interrupted before counted down
+     */
+    public void testAwait_Interruptible() {
+        final CountDownLatch l = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    l.await();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    l.await();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertEquals(1, l.getCount());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await throws IE if interrupted before counted down
+     */
+    public void testTimedAwait_Interruptible() {
+        final CountDownLatch l = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    l.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    l.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertEquals(1, l.getCount());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed await times out if not counted down before timeout
+     */
+    public void testAwaitTimeout() throws InterruptedException {
+        final CountDownLatch l = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertEquals(1, l.getCount());
+                assertFalse(l.await(timeoutMillis(), MILLISECONDS));
+                assertEquals(1, l.getCount());
+            }});
+
+        awaitTermination(t);
+        assertEquals(1, l.getCount());
+    }
+
+    /**
+     * toString indicates current count
+     */
+    public void testToString() {
+        CountDownLatch s = new CountDownLatch(2);
+        assertTrue(s.toString().contains("Count = 2"));
+        s.countDown();
+        assertTrue(s.toString().contains("Count = 1"));
+        s.countDown();
+        assertTrue(s.toString().contains("Count = 0"));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CountedCompleter8Test.java b/ojluni/src/test/java/util/concurrent/tck/CountedCompleter8Test.java
new file mode 100644
index 0000000..663c2b5
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CountedCompleter8Test.java
@@ -0,0 +1,169 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CountedCompleter8Test extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(CountedCompleter8Test.class);
+    }
+
+    /** CountedCompleter class javadoc code sample, version 1. */
+    public static <E> void forEach1(E[] array, Consumer<E> action) {
+        class Task extends CountedCompleter<Void> {
+            final int lo, hi;
+            Task(Task parent, int lo, int hi) {
+                super(parent); this.lo = lo; this.hi = hi;
+            }
+
+            public void compute() {
+                if (hi - lo >= 2) {
+                    int mid = (lo + hi) >>> 1;
+                    // must set pending count before fork
+                    setPendingCount(2);
+                    new Task(this, mid, hi).fork(); // right child
+                    new Task(this, lo, mid).fork(); // left child
+                }
+                else if (hi > lo)
+                    action.accept(array[lo]);
+                tryComplete();
+            }
+        }
+        new Task(null, 0, array.length).invoke();
+    }
+
+    /** CountedCompleter class javadoc code sample, version 2. */
+    public static <E> void forEach2(E[] array, Consumer<E> action) {
+        class Task extends CountedCompleter<Void> {
+            final int lo, hi;
+            Task(Task parent, int lo, int hi) {
+                super(parent); this.lo = lo; this.hi = hi;
+            }
+
+            public void compute() {
+                if (hi - lo >= 2) {
+                    int mid = (lo + hi) >>> 1;
+                    setPendingCount(1); // looks off by one, but correct!
+                    new Task(this, mid, hi).fork(); // right child
+                    new Task(this, lo, mid).compute(); // direct invoke
+                } else {
+                    if (hi > lo)
+                        action.accept(array[lo]);
+                    tryComplete();
+                }
+            }
+        }
+        new Task(null, 0, array.length).invoke();
+    }
+
+    /** CountedCompleter class javadoc code sample, version 3. */
+    public static <E> void forEach3(E[] array, Consumer<E> action) {
+        class Task extends CountedCompleter<Void> {
+            final int lo, hi;
+            Task(Task parent, int lo, int hi) {
+                super(parent); this.lo = lo; this.hi = hi;
+            }
+
+            public void compute() {
+                int n = hi - lo;
+                for (; n >= 2; n /= 2) {
+                    addToPendingCount(1);
+                    new Task(this, lo + n/2, lo + n).fork();
+                }
+                if (n > 0)
+                    action.accept(array[lo]);
+                propagateCompletion();
+            }
+        }
+        new Task(null, 0, array.length).invoke();
+    }
+
+    /** CountedCompleter class javadoc code sample, version 4. */
+    public static <E> void forEach4(E[] array, Consumer<E> action) {
+        class Task extends CountedCompleter<Void> {
+            final int lo, hi;
+            Task(Task parent, int lo, int hi) {
+                super(parent, 31 - Integer.numberOfLeadingZeros(hi - lo));
+                this.lo = lo; this.hi = hi;
+            }
+
+            public void compute() {
+                for (int n = hi - lo; n >= 2; n /= 2)
+                    new Task(this, lo + n/2, lo + n).fork();
+                action.accept(array[lo]);
+                propagateCompletion();
+            }
+        }
+        if (array.length > 0)
+            new Task(null, 0, array.length).invoke();
+    }
+
+    void testRecursiveDecomposition(
+        BiConsumer<Integer[], Consumer<Integer>> action) {
+        int n = ThreadLocalRandom.current().nextInt(8);
+        Integer[] a = new Integer[n];
+        for (int i = 0; i < n; i++) a[i] = i + 1;
+        AtomicInteger ai = new AtomicInteger(0);
+        action.accept(a, ai::addAndGet);
+        assertEquals(n * (n + 1) / 2, ai.get());
+    }
+
+    /**
+     * Variants of divide-by-two recursive decomposition into leaf tasks,
+     * as described in the CountedCompleter class javadoc code samples
+     */
+    public void testRecursiveDecomposition() {
+        testRecursiveDecomposition(CountedCompleter8Test::forEach1);
+        testRecursiveDecomposition(CountedCompleter8Test::forEach2);
+        testRecursiveDecomposition(CountedCompleter8Test::forEach3);
+        testRecursiveDecomposition(CountedCompleter8Test::forEach4);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CountedCompleterTest.java b/ojluni/src/test/java/util/concurrent/tck/CountedCompleterTest.java
new file mode 100644
index 0000000..241ca03
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CountedCompleterTest.java
@@ -0,0 +1,1874 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CountedCompleterTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(CountedCompleterTest.class);
+    }
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, ForkJoinTask a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        }
+    }
+
+    void checkNotDone(CountedCompleter a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(CountedCompleter<?> a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            assertNull(a.join());
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(CountedCompleter a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+        assertTrue(a.cancel(false));
+        assertTrue(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(CountedCompleter a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.invoke();
+            shouldThrow();
+        } catch (Throwable success) {
+            assertSame(t, success);
+        }
+    }
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract class CheckedCC extends CountedCompleter<Object> {
+        final AtomicInteger computeN = new AtomicInteger(0);
+        final AtomicInteger onCompletionN = new AtomicInteger(0);
+        final AtomicInteger onExceptionalCompletionN = new AtomicInteger(0);
+        final AtomicInteger setRawResultN = new AtomicInteger(0);
+        final AtomicReference<Object> rawResult = new AtomicReference<>(null);
+        int computeN() { return computeN.get(); }
+        int onCompletionN() { return onCompletionN.get(); }
+        int onExceptionalCompletionN() { return onExceptionalCompletionN.get(); }
+        int setRawResultN() { return setRawResultN.get(); }
+
+        CheckedCC() { super(); }
+        CheckedCC(CountedCompleter p) { super(p); }
+        CheckedCC(CountedCompleter p, int n) { super(p, n); }
+        abstract void realCompute();
+        public final void compute() {
+            computeN.incrementAndGet();
+            realCompute();
+        }
+        public void onCompletion(CountedCompleter caller) {
+            onCompletionN.incrementAndGet();
+            super.onCompletion(caller);
+        }
+        public boolean onExceptionalCompletion(Throwable ex,
+                                               CountedCompleter caller) {
+            onExceptionalCompletionN.incrementAndGet();
+            assertNotNull(ex);
+            assertTrue(isCompletedAbnormally());
+            assertTrue(super.onExceptionalCompletion(ex, caller));
+            return true;
+        }
+        protected void setRawResult(Object t) {
+            setRawResultN.incrementAndGet();
+            rawResult.set(t);
+            super.setRawResult(t);
+        }
+        void checkIncomplete() {
+            assertEquals(0, computeN());
+            assertEquals(0, onCompletionN());
+            assertEquals(0, onExceptionalCompletionN());
+            assertEquals(0, setRawResultN());
+            checkNotDone(this);
+        }
+        void checkCompletes(Object rawResult) {
+            checkIncomplete();
+            int pendingCount = getPendingCount();
+            complete(rawResult);
+            assertEquals(pendingCount, getPendingCount());
+            assertEquals(0, computeN());
+            assertEquals(1, onCompletionN());
+            assertEquals(0, onExceptionalCompletionN());
+            assertEquals(1, setRawResultN());
+            assertSame(rawResult, this.rawResult.get());
+            checkCompletedNormally(this);
+        }
+        void checkCompletesExceptionally(Throwable ex) {
+            checkIncomplete();
+            completeExceptionally(ex);
+            checkCompletedExceptionally(ex);
+        }
+        void checkCompletedExceptionally(Throwable ex) {
+            assertEquals(0, computeN());
+            assertEquals(0, onCompletionN());
+            assertEquals(1, onExceptionalCompletionN());
+            assertEquals(0, setRawResultN());
+            assertNull(this.rawResult.get());
+            checkCompletedAbnormally(this, ex);
+        }
+    }
+
+    final class NoopCC extends CheckedCC {
+        NoopCC() { super(); }
+        NoopCC(CountedCompleter p) { super(p); }
+        NoopCC(CountedCompleter p, int initialPendingCount) {
+            super(p, initialPendingCount);
+        }
+        protected void realCompute() {}
+    }
+
+    /**
+     * A newly constructed CountedCompleter is not completed;
+     * complete() causes completion. pendingCount is ignored.
+     */
+    public void testComplete() {
+        for (Object x : new Object[] { Boolean.TRUE, null }) {
+            for (int pendingCount : new int[] { 0, 42 }) {
+                testComplete(new NoopCC(), x, pendingCount);
+                testComplete(new NoopCC(new NoopCC()), x, pendingCount);
+            }
+        }
+    }
+    void testComplete(NoopCC cc, Object x, int pendingCount) {
+        cc.setPendingCount(pendingCount);
+        cc.checkCompletes(x);
+        assertEquals(pendingCount, cc.getPendingCount());
+    }
+
+    /**
+     * completeExceptionally completes exceptionally
+     */
+    public void testCompleteExceptionally() {
+        new NoopCC()
+            .checkCompletesExceptionally(new FJException());
+        new NoopCC(new NoopCC())
+            .checkCompletesExceptionally(new FJException());
+    }
+
+    /**
+     * completeExceptionally(null) surprisingly has the same effect as
+     * completeExceptionally(new RuntimeException())
+     */
+    public void testCompleteExceptionally_null() {
+        NoopCC a = new NoopCC();
+        a.completeExceptionally(null);
+        try {
+            a.invoke();
+            shouldThrow();
+        } catch (RuntimeException success) {
+            assertSame(success.getClass(), RuntimeException.class);
+            assertNull(success.getCause());
+            a.checkCompletedExceptionally(success);
+        }
+    }
+
+    /**
+     * setPendingCount sets the reported pending count
+     */
+    public void testSetPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int[] vals = {
+             -1, 0, 1,
+             Integer.MIN_VALUE,
+             Integer.MAX_VALUE,
+        };
+        for (int val : vals) {
+            a.setPendingCount(val);
+            assertEquals(val, a.getPendingCount());
+        }
+    }
+
+    /**
+     * addToPendingCount adds to the reported pending count
+     */
+    public void testAddToPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        a.addToPendingCount(1);
+        assertEquals(1, a.getPendingCount());
+        a.addToPendingCount(27);
+        assertEquals(28, a.getPendingCount());
+        a.addToPendingCount(-28);
+        assertEquals(0, a.getPendingCount());
+    }
+
+    /**
+     * decrementPendingCountUnlessZero decrements reported pending
+     * count unless zero
+     */
+    public void testDecrementPendingCountUnlessZero() {
+        NoopCC a = new NoopCC(null, 2);
+        assertEquals(2, a.getPendingCount());
+        assertEquals(2, a.decrementPendingCountUnlessZero());
+        assertEquals(1, a.getPendingCount());
+        assertEquals(1, a.decrementPendingCountUnlessZero());
+        assertEquals(0, a.getPendingCount());
+        assertEquals(0, a.decrementPendingCountUnlessZero());
+        assertEquals(0, a.getPendingCount());
+        a.setPendingCount(-1);
+        assertEquals(-1, a.decrementPendingCountUnlessZero());
+        assertEquals(-2, a.getPendingCount());
+    }
+
+    /**
+     * compareAndSetPendingCount compares and sets the reported
+     * pending count
+     */
+    public void testCompareAndSetPendingCount() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        assertTrue(a.compareAndSetPendingCount(0, 1));
+        assertEquals(1, a.getPendingCount());
+        assertTrue(a.compareAndSetPendingCount(1, 2));
+        assertEquals(2, a.getPendingCount());
+        assertFalse(a.compareAndSetPendingCount(1, 3));
+        assertEquals(2, a.getPendingCount());
+    }
+
+    /**
+     * getCompleter returns parent or null if at root
+     */
+    public void testGetCompleter() {
+        NoopCC a = new NoopCC();
+        assertNull(a.getCompleter());
+        CountedCompleter b = new NoopCC(a);
+        assertSame(a, b.getCompleter());
+        CountedCompleter c = new NoopCC(b);
+        assertSame(b, c.getCompleter());
+    }
+
+    /**
+     * getRoot returns self if no parent, else parent's root
+     */
+    public void testGetRoot() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        NoopCC c = new NoopCC(b);
+        assertSame(a, a.getRoot());
+        assertSame(a, b.getRoot());
+        assertSame(a, c.getRoot());
+    }
+
+    /**
+     * tryComplete decrements pending count unless zero, in which case
+     * causes completion
+     */
+    public void testTryComplete() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int n = 3;
+        a.setPendingCount(n);
+        for (; n > 0; n--) {
+            assertEquals(n, a.getPendingCount());
+            a.tryComplete();
+            a.checkIncomplete();
+            assertEquals(n - 1, a.getPendingCount());
+        }
+        a.tryComplete();
+        assertEquals(0, a.computeN());
+        assertEquals(1, a.onCompletionN());
+        assertEquals(0, a.onExceptionalCompletionN());
+        assertEquals(0, a.setRawResultN());
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * propagateCompletion decrements pending count unless zero, in
+     * which case causes completion, without invoking onCompletion
+     */
+    public void testPropagateCompletion() {
+        NoopCC a = new NoopCC();
+        assertEquals(0, a.getPendingCount());
+        int n = 3;
+        a.setPendingCount(n);
+        for (; n > 0; n--) {
+            assertEquals(n, a.getPendingCount());
+            a.propagateCompletion();
+            a.checkIncomplete();
+            assertEquals(n - 1, a.getPendingCount());
+        }
+        a.propagateCompletion();
+        assertEquals(0, a.computeN());
+        assertEquals(0, a.onCompletionN());
+        assertEquals(0, a.onExceptionalCompletionN());
+        assertEquals(0, a.setRawResultN());
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * firstComplete returns this if pending count is zero else null
+     */
+    public void testFirstComplete() {
+        NoopCC a = new NoopCC();
+        a.setPendingCount(1);
+        assertNull(a.firstComplete());
+        a.checkIncomplete();
+        assertSame(a, a.firstComplete());
+        a.checkIncomplete();
+    }
+
+    /**
+     * firstComplete.nextComplete returns parent if pending count is
+     * zero else null
+     */
+    public void testNextComplete() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        a.setPendingCount(1);
+        b.setPendingCount(1);
+        assertNull(b.firstComplete());
+        assertSame(b, b.firstComplete());
+        assertNull(b.nextComplete());
+        a.checkIncomplete();
+        b.checkIncomplete();
+        assertSame(a, b.nextComplete());
+        assertSame(a, b.nextComplete());
+        a.checkIncomplete();
+        b.checkIncomplete();
+        assertNull(a.nextComplete());
+        b.checkIncomplete();
+        checkCompletedNormally(a);
+    }
+
+    /**
+     * quietlyCompleteRoot completes root task and only root task
+     */
+    public void testQuietlyCompleteRoot() {
+        NoopCC a = new NoopCC();
+        NoopCC b = new NoopCC(a);
+        NoopCC c = new NoopCC(b);
+        a.setPendingCount(1);
+        b.setPendingCount(1);
+        c.setPendingCount(1);
+        c.quietlyCompleteRoot();
+        assertTrue(a.isDone());
+        assertFalse(b.isDone());
+        assertFalse(c.isDone());
+    }
+
+    // Invocation tests use some interdependent task classes
+    // to better test propagation etc
+
+    /**
+     * Version of Fibonacci with different classes for left vs right forks
+     */
+    abstract class CCF extends CheckedCC {
+        int number;
+        int rnumber;
+
+        public CCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        protected final void realCompute() {
+            CCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RCCF(f, n - 2).fork();
+                f = new LCCF(f, --n);
+            }
+            f.complete(null);
+        }
+    }
+
+    final class LCCF extends CCF {
+        public LCCF(int n) { this(null, n); }
+        public LCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    final class RCCF extends CCF {
+        public RCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.rnumber = n;
+            else
+                number = n;
+        }
+    }
+
+    // Version of CCF with forced failure in left completions
+    abstract class FailingCCF extends CheckedCC {
+        int number;
+        int rnumber;
+
+        public FailingCCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        protected final void realCompute() {
+            FailingCCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RFCCF(f, n - 2).fork();
+                f = new LFCCF(f, --n);
+            }
+            f.complete(null);
+        }
+    }
+
+    final class LFCCF extends FailingCCF {
+        public LFCCF(int n) { this(null, n); }
+        public LFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            FailingCCF p = (FailingCCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    final class RFCCF extends FailingCCF {
+        public RFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            super.onCompletion(caller);
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF n = new LCCF(8);
+                CCF f = new LCCF(n, 8);
+                FJException ex = new FJException();
+                f.completeExceptionally(ex);
+                f.checkCompletedExceptionally(ex);
+                n.checkCompletedExceptionally(ex);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                CCF h = new LCCF(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF h = new LCCF(7);
+                assertSame(h, h.fork());
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF g = new LCCF(9);
+                assertSame(g, g.fork());
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    // versions for singleton pools
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPESingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesceSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetSingleton() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionallySingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF n = new LCCF(8);
+                CCF f = new LCCF(n, 8);
+                FJException ex = new FJException();
+                f.completeExceptionally(ex);
+                f.checkCompletedExceptionally(ex);
+                n.checkCompletedExceptionally(ex);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPESingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3Singleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(8);
+                FailingCCF g = new LFCCF(9);
+                CCF h = new LCCF(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionSingleton() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(8);
+                CCF g = new LCCF(9);
+                CCF h = new LCCF(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/CyclicBarrierTest.java b/ojluni/src/test/java/util/concurrent/tck/CyclicBarrierTest.java
new file mode 100644
index 0000000..08258c6
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/CyclicBarrierTest.java
@@ -0,0 +1,490 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CyclicBarrierTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(CyclicBarrierTest.class);
+    }
+
+    /**
+     * Spin-waits till the number of waiters == numberOfWaiters.
+     */
+    void awaitNumberWaiting(CyclicBarrier barrier, int numberOfWaiters) {
+        long startTime = System.nanoTime();
+        while (barrier.getNumberWaiting() != numberOfWaiters) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Creating with negative parties throws IAE
+     */
+    public void testConstructor1() {
+        try {
+            new CyclicBarrier(-1, (Runnable)null);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Creating with negative parties and no action throws IAE
+     */
+    public void testConstructor2() {
+        try {
+            new CyclicBarrier(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getParties returns the number of parties given in constructor
+     */
+    public void testGetParties() {
+        CyclicBarrier b = new CyclicBarrier(2);
+        assertEquals(2, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+    }
+
+    /**
+     * A 1-party barrier triggers after single await
+     */
+    public void testSingleParty() throws Exception {
+        CyclicBarrier b = new CyclicBarrier(1);
+        assertEquals(1, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+        b.await();
+        b.await();
+        assertEquals(0, b.getNumberWaiting());
+    }
+
+    /**
+     * The supplied barrier action is run at barrier
+     */
+    public void testBarrierAction() throws Exception {
+        final AtomicInteger count = new AtomicInteger(0);
+        final Runnable incCount = new Runnable() { public void run() {
+            count.getAndIncrement(); }};
+        CyclicBarrier b = new CyclicBarrier(1, incCount);
+        assertEquals(1, b.getParties());
+        assertEquals(0, b.getNumberWaiting());
+        b.await();
+        b.await();
+        assertEquals(0, b.getNumberWaiting());
+        assertEquals(2, count.get());
+    }
+
+    /**
+     * A 2-party/thread barrier triggers after both threads invoke await
+     */
+    public void testTwoParties() throws Exception {
+        final CyclicBarrier b = new CyclicBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                b.await();
+                b.await();
+                b.await();
+                b.await();
+            }});
+
+        b.await();
+        b.await();
+        b.await();
+        b.await();
+        awaitTermination(t);
+    }
+
+    /**
+     * An interruption in one party causes others waiting in await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait1_Interrupted_BrokenBarrier() {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await();
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await();
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseInterrupt);
+        t1.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * An interruption in one party causes others waiting in timed await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait2_Interrupted_BrokenBarrier() throws Exception {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await(LONG_DELAY_MS, MILLISECONDS);
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                c.await(LONG_DELAY_MS, MILLISECONDS);
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseInterrupt);
+        t1.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A timeout in timed await throws TimeoutException
+     */
+    public void testAwait3_TimeoutException() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * A timeout in one party causes others waiting in timed await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait4_Timeout_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                try {
+                    c.await(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (BrokenBarrierException success) {}
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                awaitNumberWaiting(c, 1);
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A timeout in one party causes others waiting in await to
+     * throw BrokenBarrierException
+     */
+    public void testAwait5_Timeout_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                try {
+                    c.await();
+                    shouldThrow();
+                } catch (BrokenBarrierException success) {}
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                awaitNumberWaiting(c, 1);
+                long startTime = System.nanoTime();
+                try {
+                    c.await(timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A reset of an active barrier causes waiting threads to throw
+     * BrokenBarrierException
+     */
+    public void testReset_BrokenBarrier() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        final CountDownLatch pleaseReset = new CountDownLatch(2);
+        Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseReset.countDown();
+                c.await();
+            }};
+        Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+            public void realRun() throws Exception {
+                pleaseReset.countDown();
+                c.await();
+            }};
+
+        t1.start();
+        t2.start();
+        await(pleaseReset);
+
+        awaitNumberWaiting(c, 2);
+        c.reset();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A reset before threads enter barrier does not throw
+     * BrokenBarrierException
+     */
+    public void testReset_NoBrokenBarrier() throws Exception {
+        final CyclicBarrier c = new CyclicBarrier(3);
+        c.reset();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                c.await();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                c.await();
+            }});
+
+        c.await();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * All threads block while a barrier is broken.
+     */
+    public void testReset_Leakage() throws InterruptedException {
+        final CyclicBarrier c = new CyclicBarrier(2);
+        final AtomicBoolean done = new AtomicBoolean();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (!done.get()) {
+                    try {
+                        while (c.isBroken())
+                            c.reset();
+
+                        c.await();
+                        shouldThrow();
+                    }
+                    catch (BrokenBarrierException ok) {}
+                    catch (InterruptedException ok) {}
+                }}});
+
+        for (int i = 0; i < 4; i++) {
+            delay(timeoutMillis());
+            t.interrupt();
+        }
+        done.set(true);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * Reset of a non-broken barrier does not break barrier
+     */
+    public void testResetWithoutBreakage() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 3; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }});
+
+            Thread t2 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }});
+
+            start.await();
+            barrier.await();
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            if (i == 1) barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after interruption reinitializes it.
+     */
+    public void testResetAfterInterrupt() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 2; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = new ThreadShouldThrow(InterruptedException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            t1.start();
+            t2.start();
+            start.await();
+            t1.interrupt();
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after timeout reinitializes it.
+     */
+    public void testResetAfterTimeout() throws Exception {
+        final CyclicBarrier barrier = new CyclicBarrier(3);
+        for (int i = 0; i < 2; i++) {
+            assertEquals(0, barrier.getNumberWaiting());
+            Thread t1 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    try {
+                        barrier.await();
+                        shouldThrow();
+                    } catch (BrokenBarrierException success) {}
+                }});
+            Thread t2 = newStartedThread(new CheckedRunnable() {
+                public void realRun() throws Exception {
+                    awaitNumberWaiting(barrier, 1);
+                    long startTime = System.nanoTime();
+                    try {
+                        barrier.await(timeoutMillis(), MILLISECONDS);
+                        shouldThrow();
+                    } catch (TimeoutException success) {}
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }});
+
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertEquals(0, barrier.getNumberWaiting());
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+
+    /**
+     * Reset of a barrier after a failed command reinitializes it.
+     */
+    public void testResetAfterCommandException() throws Exception {
+        final CyclicBarrier barrier =
+            new CyclicBarrier(3, new Runnable() {
+                    public void run() {
+                        throw new NullPointerException(); }});
+        for (int i = 0; i < 2; i++) {
+            final CyclicBarrier start = new CyclicBarrier(3);
+            Thread t1 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            Thread t2 = new ThreadShouldThrow(BrokenBarrierException.class) {
+                public void realRun() throws Exception {
+                    start.await();
+                    barrier.await();
+                }};
+
+            t1.start();
+            t2.start();
+            start.await();
+            awaitNumberWaiting(barrier, 2);
+            try {
+                barrier.await();
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            awaitTermination(t1);
+            awaitTermination(t2);
+            assertTrue(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+            barrier.reset();
+            assertFalse(barrier.isBroken());
+            assertEquals(0, barrier.getNumberWaiting());
+        }
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/DelayQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/DelayQueueTest.java
new file mode 100644
index 0000000..dfaf2d0
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/DelayQueueTest.java
@@ -0,0 +1,816 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Test;
+
+public class DelayQueueTest extends JSR166TestCase {
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new DelayQueue();
+        }
+        protected PDelay makeElement(int i) {
+            return new PDelay(i);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return DelayQueue.class; }
+            public Collection emptyCollection() { return new DelayQueue(); }
+            public Object makeElement(int i) { return new PDelay(i); }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(DelayQueueTest.class,
+                            new Generic().testSuite(),
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * A fake Delayed implementation for testing.
+     * Most tests use PDelays, where delays are all elapsed
+     * (so, no blocking solely for delays) but are still ordered
+     */
+    static class PDelay implements Delayed {
+        final int pseudodelay;
+        PDelay(int pseudodelay) { this.pseudodelay = pseudodelay; }
+        public int compareTo(Delayed y) {
+            return Integer.compare(this.pseudodelay, ((PDelay)y).pseudodelay);
+        }
+        public boolean equals(Object other) {
+            return (other instanceof PDelay) &&
+                this.pseudodelay == ((PDelay)other).pseudodelay;
+        }
+        // suppress [overrides] javac warning
+        public int hashCode() { return pseudodelay; }
+        public long getDelay(TimeUnit ignore) {
+            return Integer.MIN_VALUE + pseudodelay;
+        }
+        public String toString() {
+            return String.valueOf(pseudodelay);
+        }
+    }
+
+    /**
+     * Delayed implementation that actually delays
+     */
+    static class NanoDelay implements Delayed {
+        final long trigger;
+        NanoDelay(long i) {
+            trigger = System.nanoTime() + i;
+        }
+
+        public int compareTo(Delayed y) {
+            return Long.compare(trigger, ((NanoDelay)y).trigger);
+        }
+
+        public boolean equals(Object other) {
+            return (other instanceof NanoDelay) &&
+                this.trigger == ((NanoDelay)other).trigger;
+        }
+
+        // suppress [overrides] javac warning
+        public int hashCode() { return (int) trigger; }
+
+        public long getDelay(TimeUnit unit) {
+            long n = trigger - System.nanoTime();
+            return unit.convert(n, TimeUnit.NANOSECONDS);
+        }
+
+        public long getTriggerTime() {
+            return trigger;
+        }
+
+        public String toString() {
+            return String.valueOf(trigger);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * PDelays 0 ... n - 1.
+     */
+    private DelayQueue<PDelay> populatedQueue(int n) {
+        DelayQueue<PDelay> q = new DelayQueue<>();
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.offer(new PDelay(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.offer(new PDelay(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        assertEquals(n, q.size());
+        assertEquals(new PDelay(0), q.peek());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(Integer.MAX_VALUE, new DelayQueue().remainingCapacity());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new DelayQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new DelayQueue(Arrays.asList(new PDelay[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        PDelay[] a = new PDelay[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            a[i] = new PDelay(i);
+        try {
+            new DelayQueue(Arrays.asList(a));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        PDelay[] ints = new PDelay[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new PDelay(i);
+        DelayQueue q = new DelayQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        DelayQueue q = new DelayQueue();
+        assertTrue(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(new PDelay(1));
+        assertFalse(q.isEmpty());
+        q.add(new PDelay(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * remainingCapacity() always returns Integer.MAX_VALUE
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(SIZE - i, q.size());
+            assertTrue(q.remove() instanceof PDelay);
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(i, q.size());
+            assertTrue(q.add(new PDelay(i)));
+        }
+    }
+
+    /**
+     * offer non-null succeeds
+     */
+    public void testOffer() {
+        DelayQueue q = new DelayQueue();
+        assertTrue(q.offer(new PDelay(0)));
+        assertTrue(q.offer(new PDelay(1)));
+    }
+
+    /**
+     * add succeeds
+     */
+    public void testAdd() {
+        DelayQueue q = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new PDelay(i)));
+        }
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        DelayQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        DelayQueue q = new DelayQueue();
+        PDelay[] a = new PDelay[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            a[i] = new PDelay(i);
+        try {
+            q.addAll(Arrays.asList(a));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        PDelay[] empty = new PDelay[0];
+        PDelay[] ints = new PDelay[SIZE];
+        for (int i = SIZE - 1; i >= 0; --i)
+            ints[i] = new PDelay(i);
+        DelayQueue q = new DelayQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        DelayQueue q = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            PDelay x = new PDelay(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(SIZE, q.size());
+    }
+
+    /**
+     * put doesn't block waiting for take
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final DelayQueue q = new DelayQueue();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+            }});
+
+        awaitTermination(t);
+        assertEquals(4, q.size());
+    }
+
+    /**
+     * timed offer does not time out
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final DelayQueue q = new DelayQueue();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new PDelay(0));
+                q.put(new PDelay(0));
+                assertTrue(q.offer(new PDelay(0), SHORT_DELAY_MS, MILLISECONDS));
+                assertTrue(q.offer(new PDelay(0), LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in priority order
+     */
+    public void testTake() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final DelayQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(new PDelay(i), ((PDelay)q.take()));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(new PDelay(i), q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final DelayQueue q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(new PDelay(i),
+                                 ((PDelay)q.poll(LONG_DELAY_MS, MILLISECONDS)));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.peek());
+            assertEquals(new PDelay(i), q.poll());
+            if (q.isEmpty())
+                assertNull(q.peek());
+            else
+                assertFalse(new PDelay(i).equals(q.peek()));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.element());
+            q.poll();
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(new PDelay(i), q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        DelayQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new PDelay(i)));
+            q.poll();
+            assertFalse(q.contains(new PDelay(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        DelayQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        PDelay x = new PDelay(1);
+        q.add(x);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(x));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        DelayQueue q = populatedQueue(SIZE);
+        DelayQueue p = new DelayQueue();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new PDelay(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        DelayQueue q = populatedQueue(SIZE);
+        DelayQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            DelayQueue q = populatedQueue(SIZE);
+            DelayQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                PDelay x = (PDelay)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() throws InterruptedException {
+        DelayQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.take());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() {
+        DelayQueue<PDelay> q = populatedQueue(SIZE);
+        PDelay[] ints = new PDelay[SIZE];
+        PDelay[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.remove());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        DelayQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        DelayQueue q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new DelayQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final DelayQueue q = new DelayQueue();
+        q.add(new PDelay(2));
+        q.add(new PDelay(1));
+        q.add(new PDelay(3));
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+        it = q.iterator();
+        assertEquals(new PDelay(2), it.next());
+        assertEquals(new PDelay(3), it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        DelayQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (Object e : q)
+            assertTrue(s.contains(e.toString()));
+    }
+
+    /**
+     * timed poll transfers elements across Executor tasks
+     */
+    public void testPollInExecutor() {
+        final DelayQueue q = new DelayQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertNotNull(q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(new PDelay(1));
+                }});
+        }
+    }
+
+    /**
+     * Delayed actions do not occur until their delay elapses
+     */
+    public void testDelay() throws InterruptedException {
+        DelayQueue<NanoDelay> q = new DelayQueue<>();
+        for (int i = 0; i < SIZE; ++i)
+            q.add(new NanoDelay(1000000L * (SIZE - i)));
+
+        long last = 0;
+        for (int i = 0; i < SIZE; ++i) {
+            NanoDelay e = q.take();
+            long tt = e.getTriggerTime();
+            assertTrue(System.nanoTime() - tt >= 0);
+            if (i != 0)
+                assertTrue(tt >= last);
+            last = tt;
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peek of a non-empty queue returns non-null even if not expired
+     */
+    public void testPeekDelayed() {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(Long.MAX_VALUE));
+        assertNotNull(q.peek());
+    }
+
+    /**
+     * poll of a non-empty queue returns null if no expired elements.
+     */
+    public void testPollDelayed() {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(Long.MAX_VALUE));
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll of a non-empty queue returns null if no expired elements.
+     */
+    public void testTimedPollDelayed() throws InterruptedException {
+        DelayQueue q = new DelayQueue();
+        q.add(new NanoDelay(LONG_DELAY_MS * 1000000L));
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        DelayQueue q = new DelayQueue();
+        PDelay[] elems = new PDelay[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            elems[i] = new PDelay(i);
+            q.add(elems[i]);
+        }
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(elems[i], l.get(i));
+        q.add(elems[0]);
+        q.add(elems[1]);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(elems[0]));
+        assertTrue(q.contains(elems[1]));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(elems[i], l.get(i));
+    }
+
+    /**
+     * drainTo empties queue
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final DelayQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new PDelay(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        for (int i = 0; i < SIZE + 2; ++i) {
+            DelayQueue q = populatedQueue(SIZE);
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(SIZE - k, q.size());
+            assertEquals(k, l.size());
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?> q = populatedQueue(SIZE);
+        assertFalse(q.contains(null));
+        assertFalse(q.remove(null));
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/DoubleAccumulatorTest.java b/ojluni/src/test/java/util/concurrent/tck/DoubleAccumulatorTest.java
new file mode 100644
index 0000000..ef42b44
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/DoubleAccumulatorTest.java
@@ -0,0 +1,184 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.DoubleAccumulator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class DoubleAccumulatorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(DoubleAccumulatorTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0.0, ai.get());
+    }
+
+    /**
+     * accumulate accumulates given value to current, and get returns current value
+     */
+    public void testAccumulateAndGet() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        ai.accumulate(2.0);
+        assertEquals(2.0, ai.get());
+        ai.accumulate(-4.0);
+        assertEquals(2.0, ai.get());
+        ai.accumulate(4.0);
+        assertEquals(4.0, ai.get());
+    }
+
+    /**
+     * reset() causes subsequent get() to return zero
+     */
+    public void testReset() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        ai.accumulate(2.0);
+        assertEquals(2.0, ai.get());
+        ai.reset();
+        assertEquals(0.0, ai.get());
+    }
+
+    /**
+     * getThenReset() returns current value; subsequent get() returns zero
+     */
+    public void testGetThenReset() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        ai.accumulate(2.0);
+        assertEquals(2.0, ai.get());
+        assertEquals(2.0, ai.getThenReset());
+        assertEquals(0.0, ai.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals("0.0", ai.toString());
+        ai.accumulate(1.0);
+        assertEquals(Double.toString(1.0), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0, ai.intValue());
+        ai.accumulate(1.0);
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0, ai.longValue());
+        ai.accumulate(1.0);
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0.0f, ai.floatValue());
+        ai.accumulate(1.0);
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        DoubleAccumulator ai = new DoubleAccumulator(Double::max, 0.0);
+        assertEquals(0.0, ai.doubleValue());
+        ai.accumulate(1.0);
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * accumulates by multiple threads produce correct result
+     */
+    public void testAccumulateAndGetMT() {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        DoubleAccumulator a = new DoubleAccumulator(Double::max, 0.0);
+        Phaser phaser = new Phaser(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AccTask(a, phaser, incs));
+        phaser.arriveAndAwaitAdvance();
+        phaser.arriveAndAwaitAdvance();
+        double expected = incs - 1;
+        double result = a.get();
+        assertEquals(expected, result);
+        pool.shutdown();
+    }
+
+    static final class AccTask implements Runnable {
+        final DoubleAccumulator acc;
+        final Phaser phaser;
+        final int incs;
+        volatile double result;
+        AccTask(DoubleAccumulator acc, Phaser phaser, int incs) {
+            this.acc = acc;
+            this.phaser = phaser;
+            this.incs = incs;
+        }
+
+        public void run() {
+            phaser.arriveAndAwaitAdvance();
+            DoubleAccumulator a = acc;
+            for (int i = 0; i < incs; ++i)
+                a.accumulate(i);
+            result = a.get();
+            phaser.arrive();
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/DoubleAdderTest.java b/ojluni/src/test/java/util/concurrent/tck/DoubleAdderTest.java
new file mode 100644
index 0000000..dd89b56
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/DoubleAdderTest.java
@@ -0,0 +1,198 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.DoubleAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class DoubleAdderTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(DoubleAdderTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0.0, ai.sum());
+    }
+
+    /**
+     * add adds given value to current, and sum returns current value
+     */
+    public void testAddAndSum() {
+        DoubleAdder ai = new DoubleAdder();
+        ai.add(2.0);
+        assertEquals(2.0, ai.sum());
+        ai.add(-4.0);
+        assertEquals(-2.0, ai.sum());
+    }
+
+    /**
+     * reset() causes subsequent sum() to return zero
+     */
+    public void testReset() {
+        DoubleAdder ai = new DoubleAdder();
+        ai.add(2.0);
+        assertEquals(2.0, ai.sum());
+        ai.reset();
+        assertEquals(0.0, ai.sum());
+    }
+
+    /**
+     * sumThenReset() returns sum; subsequent sum() returns zero
+     */
+    public void testSumThenReset() {
+        DoubleAdder ai = new DoubleAdder();
+        ai.add(2.0);
+        assertEquals(2.0, ai.sum());
+        assertEquals(2.0, ai.sumThenReset());
+        assertEquals(0.0, ai.sum());
+    }
+
+    /**
+     * a deserialized serialized adder holds same value
+     */
+    public void testSerialization() throws Exception {
+        DoubleAdder x = new DoubleAdder();
+        DoubleAdder y = serialClone(x);
+        assertNotSame(x, y);
+        x.add(-22.0);
+        DoubleAdder z = serialClone(x);
+        assertEquals(-22.0, x.sum());
+        assertEquals(0.0, y.sum());
+        assertEquals(-22.0, z.sum());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(Double.toString(0.0), ai.toString());
+        ai.add(1.0);
+        assertEquals(Double.toString(1.0), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0, ai.intValue());
+        ai.add(1.0);
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0, ai.longValue());
+        ai.add(1.0);
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0.0f, ai.floatValue());
+        ai.add(1.0);
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        DoubleAdder ai = new DoubleAdder();
+        assertEquals(0.0, ai.doubleValue());
+        ai.add(1.0);
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * adds by multiple threads produce correct sum
+     */
+    public void testAddAndSumMT() throws Throwable {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        DoubleAdder a = new DoubleAdder();
+        CyclicBarrier barrier = new CyclicBarrier(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AdderTask(a, barrier, incs));
+        barrier.await();
+        barrier.await();
+        double total = (long)nthreads * incs;
+        double sum = a.sum();
+        assertEquals(sum, total);
+        pool.shutdown();
+    }
+
+    static final class AdderTask implements Runnable {
+        final DoubleAdder adder;
+        final CyclicBarrier barrier;
+        final int incs;
+        volatile double result;
+        AdderTask(DoubleAdder adder, CyclicBarrier barrier, int incs) {
+            this.adder = adder;
+            this.barrier = barrier;
+            this.incs = incs;
+        }
+
+        public void run() {
+            try {
+                barrier.await();
+                DoubleAdder a = adder;
+                for (int i = 0; i < incs; ++i)
+                    a.add(1.0);
+                result = a.sum();
+                barrier.await();
+            } catch (Throwable t) { throw new Error(t); }
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/EntryTest.java b/ojluni/src/test/java/util/concurrent/tck/EntryTest.java
new file mode 100644
index 0000000..c98541d
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/EntryTest.java
@@ -0,0 +1,158 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.AbstractMap;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class EntryTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(EntryTest.class);
+    }
+
+    static final String k1 = "1";
+    static final String v1 = "a";
+    static final String k2 = "2";
+    static final String v2 = "b";
+
+    /**
+     * A new SimpleEntry(k, v) holds k, v.
+     */
+    public void testConstructor1() {
+        Map.Entry e = new AbstractMap.SimpleEntry(k1, v1);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+    }
+
+    /**
+     * A new SimpleImmutableEntry(k, v) holds k, v.
+     */
+    public void testConstructor2() {
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+    }
+
+    /**
+     * A new SimpleEntry(entry(k, v)) holds k, v.
+     */
+    public void testConstructor3() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+    }
+
+    /**
+     * A new SimpleImmutableEntry(entry(k, v)) holds k, v.
+     */
+    public void testConstructor4() {
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+    }
+
+    /**
+     * Entries with same key-value pairs are equal and have same
+     * hashcodes
+     */
+    public void testEquals() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(e2, e);
+        assertEquals(e2.hashCode(), e.hashCode());
+        assertEquals(s2, s);
+        assertEquals(s2.hashCode(), s.hashCode());
+        assertEquals(e2, s2);
+        assertEquals(e2.hashCode(), s2.hashCode());
+        assertEquals(e, s);
+        assertEquals(e.hashCode(), s.hashCode());
+    }
+
+    /**
+     * Entries with different key-value pairs are not equal
+     */
+    public void testNotEquals() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(k2, v1);
+        assertFalse(e2.equals(e));
+        e = new AbstractMap.SimpleEntry(k1, v2);
+        assertFalse(e2.equals(e));
+        e = new AbstractMap.SimpleEntry(k2, v2);
+        assertFalse(e2.equals(e));
+
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(k2, v1);
+        assertFalse(s2.equals(s));
+        s = new AbstractMap.SimpleImmutableEntry(k1, v2);
+        assertFalse(s2.equals(s));
+        s = new AbstractMap.SimpleImmutableEntry(k2, v2);
+        assertFalse(s2.equals(s));
+    }
+
+    /**
+     * getValue returns last setValue for SimpleEntry
+     */
+    public void testSetValue1() {
+        Map.Entry e2 = new AbstractMap.SimpleEntry(k1, v1);
+        Map.Entry e = new AbstractMap.SimpleEntry(e2);
+        assertEquals(k1, e.getKey());
+        assertEquals(v1, e.getValue());
+        e.setValue(k2);
+        assertEquals(k2, e.getValue());
+        assertFalse(e2.equals(e));
+    }
+
+    /**
+     * setValue for SimpleImmutableEntry throws UnsupportedOperationException
+     */
+    public void testSetValue2() {
+        Map.Entry s2 = new AbstractMap.SimpleImmutableEntry(k1, v1);
+        Map.Entry s = new AbstractMap.SimpleImmutableEntry(s2);
+        assertEquals(k1, s.getKey());
+        assertEquals(v1, s.getValue());
+        try {
+            s.setValue(k2);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ExchangerTest.java b/ojluni/src/test/java/util/concurrent/tck/ExchangerTest.java
new file mode 100644
index 0000000..95acafb
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ExchangerTest.java
@@ -0,0 +1,181 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ExchangerTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ExchangerTest.class);
+    }
+
+    /**
+     * exchange exchanges objects across two threads
+     */
+    public void testExchange() {
+        final Exchanger e = new Exchanger();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(one, e.exchange(two));
+                assertSame(two, e.exchange(one));
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(two, e.exchange(one));
+                assertSame(one, e.exchange(two));
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * timed exchange exchanges objects across two threads
+     */
+    public void testTimedExchange() {
+        final Exchanger e = new Exchanger();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS));
+                assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS));
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                assertSame(two, e.exchange(one, LONG_DELAY_MS, MILLISECONDS));
+                assertSame(one, e.exchange(two, LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * interrupt during wait for exchange throws IE
+     */
+    public void testExchange_InterruptedException() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                e.exchange(one);
+            }});
+
+        await(threadStarted);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * interrupt during wait for timed exchange throws IE
+     */
+    public void testTimedExchange_InterruptedException() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws Exception {
+                threadStarted.countDown();
+                e.exchange(null, LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        await(threadStarted);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timeout during wait for timed exchange throws TimeoutException
+     */
+    public void testExchange_TimeoutException() {
+        final Exchanger e = new Exchanger();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                long startTime = System.nanoTime();
+                try {
+                    e.exchange(null, timeoutMillis(), MILLISECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {}
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * If one exchanging thread is interrupted, another succeeds.
+     */
+    public void testReplacementAfterExchange() {
+        final Exchanger e = new Exchanger();
+        final CountDownLatch exchanged = new CountDownLatch(2);
+        final CountDownLatch interrupted = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(two, e.exchange(one));
+                exchanged.countDown();
+                e.exchange(two);
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertSame(one, e.exchange(two));
+                exchanged.countDown();
+                interrupted.await();
+                assertSame(three, e.exchange(one));
+            }});
+        Thread t3 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                interrupted.await();
+                assertSame(one, e.exchange(three));
+            }});
+
+        await(exchanged);
+        t1.interrupt();
+        awaitTermination(t1);
+        interrupted.countDown();
+        awaitTermination(t2);
+        awaitTermination(t3);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ExecutorCompletionService9Test.java b/ojluni/src/test/java/util/concurrent/tck/ExecutorCompletionService9Test.java
new file mode 100644
index 0000000..1af24cf
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ExecutorCompletionService9Test.java
@@ -0,0 +1,141 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.Future;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ExecutorCompletionService9Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ExecutorCompletionService9Test.class);
+    }
+
+    void solveAll(Executor e,
+                  Collection<Callable<Integer>> solvers)
+        throws InterruptedException, ExecutionException {
+        CompletionService<Integer> cs
+            = new ExecutorCompletionService<>(e);
+        solvers.forEach(cs::submit);
+        for (int i = solvers.size(); i > 0; i--) {
+            Integer r = cs.take().get();
+            if (r != null)
+                use(r);
+        }
+    }
+
+    void solveAny(Executor e,
+                  Collection<Callable<Integer>> solvers)
+        throws InterruptedException {
+        CompletionService<Integer> cs
+            = new ExecutorCompletionService<>(e);
+        int n = solvers.size();
+        List<Future<Integer>> futures = new ArrayList<>(n);
+        Integer result = null;
+        try {
+            solvers.forEach(solver -> futures.add(cs.submit(solver)));
+            for (int i = n; i > 0; i--) {
+                try {
+                    Integer r = cs.take().get();
+                    if (r != null) {
+                        result = r;
+                        break;
+                    }
+                } catch (ExecutionException ignore) {}
+            }
+        } finally {
+            futures.forEach(future -> future.cancel(true));
+        }
+
+        if (result != null)
+            use(result);
+    }
+
+    ArrayList<Integer> results;
+
+    void use(Integer x) {
+        if (results == null) results = new ArrayList<Integer>();
+        results.add(x);
+    }
+
+    /**
+     * The first "solvers" sample code in the class javadoc works.
+     */
+    public void testSolveAll()
+        throws InterruptedException, ExecutionException {
+        results = null;
+        Set<Callable<Integer>> solvers = Set.of(
+            () -> null,
+            () -> 1,
+            () -> 2,
+            () -> 3,
+            () -> null);
+        solveAll(cachedThreadPool, solvers);
+        results.sort(Comparator.naturalOrder());
+        assertEquals(List.of(1, 2, 3), results);
+    }
+
+    /**
+     * The second "solvers" sample code in the class javadoc works.
+     */
+    public void testSolveAny()
+        throws InterruptedException {
+        results = null;
+        Set<Callable<Integer>> solvers = Set.of(
+            () -> { throw new ArithmeticException(); },
+            () -> null,
+            () -> 1,
+            () -> 2);
+        solveAny(cachedThreadPool, solvers);
+        assertEquals(1, results.size());
+        Integer elt = results.get(0);
+        assertTrue(elt.equals(1) || elt.equals(2));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java b/ojluni/src/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java
new file mode 100644
index 0000000..877031e
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ExecutorCompletionServiceTest.java
@@ -0,0 +1,278 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ExecutorCompletionServiceTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ExecutorCompletionServiceTest.class);
+    }
+
+    /**
+     * new ExecutorCompletionService(null) throws NullPointerException
+     */
+    public void testConstructorNPE() {
+        try {
+            new ExecutorCompletionService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * new ExecutorCompletionService(e, null) throws NullPointerException
+     */
+    public void testConstructorNPE2() {
+        try {
+            new ExecutorCompletionService(cachedThreadPool, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * ecs.submit(null) throws NullPointerException
+     */
+    public void testSubmitNullCallable() {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        try {
+            cs.submit((Callable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * ecs.submit(null, val) throws NullPointerException
+     */
+    public void testSubmitNullRunnable() {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        try {
+            cs.submit((Runnable) null, Boolean.TRUE);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A taken submitted task is completed
+     */
+    public void testTake()
+        throws InterruptedException, ExecutionException {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        cs.submit(new StringTask());
+        Future f = cs.take();
+        assertTrue(f.isDone());
+        assertSame(TEST_STRING, f.get());
+    }
+
+    /**
+     * Take returns the same future object returned by submit
+     */
+    public void testTake2() throws InterruptedException {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        Future f1 = cs.submit(new StringTask());
+        Future f2 = cs.take();
+        assertSame(f1, f2);
+    }
+
+    /**
+     * poll returns non-null when the returned task is completed
+     */
+    public void testPoll1()
+        throws InterruptedException, ExecutionException {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        assertNull(cs.poll());
+        cs.submit(new StringTask());
+
+        long startTime = System.nanoTime();
+        Future f;
+        while ((f = cs.poll()) == null) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+        assertTrue(f.isDone());
+        assertSame(TEST_STRING, f.get());
+    }
+
+    /**
+     * timed poll returns non-null when the returned task is completed
+     */
+    public void testPoll2()
+        throws InterruptedException, ExecutionException {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        assertNull(cs.poll());
+        cs.submit(new StringTask());
+
+        long startTime = System.nanoTime();
+        Future f;
+        while ((f = cs.poll(SHORT_DELAY_MS, MILLISECONDS)) == null) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+        assertTrue(f.isDone());
+        assertSame(TEST_STRING, f.get());
+    }
+
+    /**
+     * poll returns null before the returned task is completed
+     */
+    public void testPollReturnsNull()
+        throws InterruptedException, ExecutionException {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        final CountDownLatch proceed = new CountDownLatch(1);
+        cs.submit(new Callable() { public String call() throws Exception {
+            proceed.await();
+            return TEST_STRING;
+        }});
+        assertNull(cs.poll());
+        assertNull(cs.poll(0L, MILLISECONDS));
+        assertNull(cs.poll(Long.MIN_VALUE, MILLISECONDS));
+        long startTime = System.nanoTime();
+        assertNull(cs.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        proceed.countDown();
+        assertSame(TEST_STRING, cs.take().get());
+    }
+
+    /**
+     * successful and failed tasks are both returned
+     */
+    public void testTaskAssortment()
+        throws InterruptedException, ExecutionException {
+        CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
+        ArithmeticException ex = new ArithmeticException();
+        for (int i = 0; i < 2; i++) {
+            cs.submit(new StringTask());
+            cs.submit(callableThrowing(ex));
+            cs.submit(runnableThrowing(ex), null);
+        }
+        int normalCompletions = 0;
+        int exceptionalCompletions = 0;
+        for (int i = 0; i < 3 * 2; i++) {
+            try {
+                if (cs.take().get() == TEST_STRING)
+                    normalCompletions++;
+            }
+            catch (ExecutionException expected) {
+                assertTrue(expected.getCause() instanceof ArithmeticException);
+                exceptionalCompletions++;
+            }
+        }
+        assertEquals(2 * 1, normalCompletions);
+        assertEquals(2 * 2, exceptionalCompletions);
+        assertNull(cs.poll());
+    }
+
+    /**
+     * Submitting to underlying AES that overrides newTaskFor(Callable)
+     * returns and eventually runs Future returned by newTaskFor.
+     */
+    public void testNewTaskForCallable() throws InterruptedException {
+        final AtomicBoolean done = new AtomicBoolean(false);
+        class MyCallableFuture<V> extends FutureTask<V> {
+            MyCallableFuture(Callable<V> c) { super(c); }
+            @Override protected void done() { done.set(true); }
+        }
+        final ExecutorService e =
+            new ThreadPoolExecutor(1, 1,
+                                   30L, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1)) {
+                protected <T> RunnableFuture<T> newTaskFor(Callable<T> c) {
+                    return new MyCallableFuture<T>(c);
+                }};
+        CompletionService<String> cs = new ExecutorCompletionService<>(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            assertNull(cs.poll());
+            Callable<String> c = new StringTask();
+            Future f1 = cs.submit(c);
+            assertTrue("submit must return MyCallableFuture",
+                       f1 instanceof MyCallableFuture);
+            Future f2 = cs.take();
+            assertSame("submit and take must return same objects", f1, f2);
+            assertTrue("completed task must have set done", done.get());
+        }
+    }
+
+    /**
+     * Submitting to underlying AES that overrides newTaskFor(Runnable,T)
+     * returns and eventually runs Future returned by newTaskFor.
+     */
+    public void testNewTaskForRunnable() throws InterruptedException {
+        final AtomicBoolean done = new AtomicBoolean(false);
+        class MyRunnableFuture<V> extends FutureTask<V> {
+            MyRunnableFuture(Runnable t, V r) { super(t, r); }
+            @Override protected void done() { done.set(true); }
+        }
+        final ExecutorService e =
+            new ThreadPoolExecutor(1, 1,
+                                   30L, TimeUnit.SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1)) {
+                protected <T> RunnableFuture<T> newTaskFor(Runnable t, T r) {
+                    return new MyRunnableFuture<T>(t, r);
+                }};
+        CompletionService<String> cs = new ExecutorCompletionService<>(e);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            assertNull(cs.poll());
+            Runnable r = new NoOpRunnable();
+            Future f1 = cs.submit(r, null);
+            assertTrue("submit must return MyRunnableFuture",
+                       f1 instanceof MyRunnableFuture);
+            Future f2 = cs.take();
+            assertSame("submit and take must return same objects", f1, f2);
+            assertTrue("completed task must have set done", done.get());
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ExecutorsTest.java b/ojluni/src/test/java/util/concurrent/tck/ExecutorsTest.java
new file mode 100644
index 0000000..fd4b7e4
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ExecutorsTest.java
@@ -0,0 +1,625 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.security.AccessControlContext;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ExecutorsTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ExecutorsTest.class);
+    }
+
+    /**
+     * A newCachedThreadPool can execute runnables
+     */
+    public void testNewCachedThreadPool1() {
+        final ExecutorService e = Executors.newCachedThreadPool();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A newCachedThreadPool with given ThreadFactory can execute runnables
+     */
+    public void testNewCachedThreadPool2() {
+        final ExecutorService e = Executors.newCachedThreadPool(new SimpleThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A newCachedThreadPool with null ThreadFactory throws NPE
+     */
+    public void testNewCachedThreadPool3() {
+        try {
+            ExecutorService e = Executors.newCachedThreadPool(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new SingleThreadExecutor can execute runnables
+     */
+    public void testNewSingleThreadExecutor1() {
+        final ExecutorService e = Executors.newSingleThreadExecutor();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new SingleThreadExecutor with given ThreadFactory can execute runnables
+     */
+    public void testNewSingleThreadExecutor2() {
+        final ExecutorService e = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new SingleThreadExecutor with null ThreadFactory throws NPE
+     */
+    public void testNewSingleThreadExecutor3() {
+        try {
+            ExecutorService e = Executors.newSingleThreadExecutor(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new SingleThreadExecutor cannot be casted to concrete implementation
+     */
+    public void testCastNewSingleThreadExecutor() {
+        final ExecutorService e = Executors.newSingleThreadExecutor();
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                ThreadPoolExecutor tpe = (ThreadPoolExecutor)e;
+                shouldThrow();
+            } catch (ClassCastException success) {}
+        }
+    }
+
+    /**
+     * A new newFixedThreadPool can execute runnables
+     */
+    public void testNewFixedThreadPool1() {
+        final ExecutorService e = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new newFixedThreadPool with given ThreadFactory can execute runnables
+     */
+    public void testNewFixedThreadPool2() {
+        final ExecutorService e = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * A new newFixedThreadPool with null ThreadFactory throws NPE
+     */
+    public void testNewFixedThreadPool3() {
+        try {
+            ExecutorService e = Executors.newFixedThreadPool(2, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A new newFixedThreadPool with 0 threads throws IAE
+     */
+    public void testNewFixedThreadPool4() {
+        try {
+            ExecutorService e = Executors.newFixedThreadPool(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * An unconfigurable newFixedThreadPool can execute runnables
+     */
+    public void testUnconfigurableExecutorService() {
+        final ExecutorService e = Executors.unconfigurableExecutorService(Executors.newFixedThreadPool(2));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+            e.execute(new NoOpRunnable());
+        }
+    }
+
+    /**
+     * unconfigurableExecutorService(null) throws NPE
+     */
+    public void testUnconfigurableExecutorServiceNPE() {
+        try {
+            ExecutorService e = Executors.unconfigurableExecutorService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * unconfigurableScheduledExecutorService(null) throws NPE
+     */
+    public void testUnconfigurableScheduledExecutorServiceNPE() {
+        try {
+            ExecutorService e = Executors.unconfigurableScheduledExecutorService(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * a newSingleThreadScheduledExecutor successfully runs delayed task
+     */
+    public void testNewSingleThreadScheduledExecutor() throws Exception {
+        final ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * a newScheduledThreadPool successfully runs delayed task
+     */
+    public void testNewScheduledThreadPool() throws Exception {
+        final ScheduledExecutorService p = Executors.newScheduledThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * an unconfigurable newScheduledThreadPool successfully runs delayed task
+     */
+    public void testUnconfigurableScheduledExecutorService() throws Exception {
+        final ScheduledExecutorService p =
+            Executors.unconfigurableScheduledExecutorService
+            (Executors.newScheduledThreadPool(2));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch proceed = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    await(proceed);
+                }};
+            long startTime = System.nanoTime();
+            Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
+                                  timeoutMillis(), MILLISECONDS);
+            assertFalse(f.isDone());
+            proceed.countDown();
+            assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(f.isDone());
+            assertFalse(f.isCancelled());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * Future.get on submitted tasks will time out if they compute too long.
+     */
+    public void testTimedCallable() throws Exception {
+        final ExecutorService[] executors = {
+            Executors.newSingleThreadExecutor(),
+            Executors.newCachedThreadPool(),
+            Executors.newFixedThreadPool(2),
+            Executors.newScheduledThreadPool(2),
+        };
+
+        final Runnable sleeper = new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                delay(LONG_DELAY_MS);
+            }};
+
+        List<Thread> threads = new ArrayList<>();
+        for (final ExecutorService executor : executors) {
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    Future future = executor.submit(sleeper);
+                    assertFutureTimesOut(future);
+                }}));
+        }
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        for (ExecutorService executor : executors)
+            joinPool(executor);
+    }
+
+    /**
+     * ThreadPoolExecutor using defaultThreadFactory has
+     * specified group, priority, daemon status, and name
+     */
+    public void testDefaultThreadFactory() throws Exception {
+        final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
+        final CountDownLatch done = new CountDownLatch(1);
+        Runnable r = new CheckedRunnable() {
+            public void realRun() {
+                try {
+                    Thread current = Thread.currentThread();
+                    assertTrue(!current.isDaemon());
+                    assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+                    ThreadGroup g = current.getThreadGroup();
+                    SecurityManager s = System.getSecurityManager();
+                    if (s != null)
+                        assertTrue(g == s.getThreadGroup());
+                    else
+                        assertTrue(g == egroup);
+                    String name = current.getName();
+                    assertTrue(name.endsWith("thread-1"));
+                } catch (SecurityException ok) {
+                    // Also pass if not allowed to change setting
+                }
+                done.countDown();
+            }};
+        ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            e.execute(r);
+            await(done);
+        }
+    }
+
+    /**
+     * ThreadPoolExecutor using privilegedThreadFactory has
+     * specified group, priority, daemon status, name,
+     * access control context and context class loader
+     */
+    public void testPrivilegedThreadFactory() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
+                final ClassLoader thisccl = Thread.currentThread().getContextClassLoader();
+                final AccessControlContext thisacc = AccessController.getContext();
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() {
+                        Thread current = Thread.currentThread();
+                        assertTrue(!current.isDaemon());
+                        assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
+                        ThreadGroup g = current.getThreadGroup();
+                        SecurityManager s = System.getSecurityManager();
+                        if (s != null)
+                            assertTrue(g == s.getThreadGroup());
+                        else
+                            assertTrue(g == egroup);
+                        String name = current.getName();
+                        assertTrue(name.endsWith("thread-1"));
+                        assertSame(thisccl, current.getContextClassLoader());
+                        // Android-removed: Android doesn't support real AccessControlContext.
+                        // assertEquals(thisacc, AccessController.getCossntext());
+                        done.countDown();
+                    }};
+                ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory());
+                try (PoolCleaner cleaner = cleaner(e)) {
+                    e.execute(r);
+                    await(done);
+                }
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"),
+                           new RuntimePermission("modifyThread"));
+    }
+
+    boolean haveCCLPermissions() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            try {
+                sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+                sm.checkPermission(new RuntimePermission("getClassLoader"));
+            } catch (AccessControlException e) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void checkCCL() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
+            sm.checkPermission(new RuntimePermission("getClassLoader"));
+        }
+    }
+
+    class CheckCCL implements Callable<Object> {
+        public Object call() {
+            checkCCL();
+            return null;
+        }
+    }
+
+    /**
+     * Without class loader permissions, creating
+     * privilegedCallableUsingCurrentClassLoader throws ACE
+     */
+    public void testCreatePrivilegedCallableUsingCCLWithNoPrivs() {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                if (System.getSecurityManager() == null)
+                    return;
+                try {
+                    Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable());
+                    shouldThrow();
+                } catch (AccessControlException success) {}
+            }};
+
+        runWithoutPermissions(r);
+    }
+
+    /**
+     * With class loader permissions, calling
+     * privilegedCallableUsingCurrentClassLoader does not throw ACE
+     */
+    public void testPrivilegedCallableUsingCCLWithPrivs() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Executors.privilegedCallableUsingCurrentClassLoader
+                    (new NoOpCallable())
+                    .call();
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * Without permissions, calling privilegedCallable throws ACE
+     */
+    public void testPrivilegedCallableWithNoPrivs() throws Exception {
+        // Avoid classloader-related SecurityExceptions in swingui.TestRunner
+        Executors.privilegedCallable(new CheckCCL());
+
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                if (System.getSecurityManager() == null)
+                    return;
+                Callable task = Executors.privilegedCallable(new CheckCCL());
+                try {
+                    task.call();
+                    shouldThrow();
+                } catch (AccessControlException success) {}
+            }};
+
+        runWithoutPermissions(r);
+
+        // It seems rather difficult to test that the
+        // AccessControlContext of the privilegedCallable is used
+        // instead of its caller.  Below is a failed attempt to do
+        // that, which does not work because the AccessController
+        // cannot capture the internal state of the current Policy.
+        // It would be much more work to differentiate based on,
+        // e.g. CodeSource.
+
+//         final AccessControlContext[] noprivAcc = new AccessControlContext[1];
+//         final Callable[] task = new Callable[1];
+
+//         runWithPermissions
+//             (new CheckedRunnable() {
+//                 public void realRun() {
+//                     if (System.getSecurityManager() == null)
+//                         return;
+//                     noprivAcc[0] = AccessController.getContext();
+//                     task[0] = Executors.privilegedCallable(new CheckCCL());
+//                     try {
+//                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+//                                                           public Void run() {
+//                                                               checkCCL();
+//                                                               return null;
+//                                                           }}, noprivAcc[0]);
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+//                 }});
+
+//         runWithPermissions
+//             (new CheckedRunnable() {
+//                 public void realRun() throws Exception {
+//                     if (System.getSecurityManager() == null)
+//                         return;
+//                     // Verify that we have an underprivileged ACC
+//                     try {
+//                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
+//                                                           public Void run() {
+//                                                               checkCCL();
+//                                                               return null;
+//                                                           }}, noprivAcc[0]);
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+
+//                     try {
+//                         task[0].call();
+//                         shouldThrow();
+//                     } catch (AccessControlException success) {}
+//                 }},
+//              new RuntimePermission("getClassLoader"),
+//              new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * With permissions, calling privilegedCallable succeeds
+     */
+    public void testPrivilegedCallableWithPrivs() throws Exception {
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Executors.privilegedCallable(new CheckCCL()).call();
+            }};
+
+        runWithPermissions(r,
+                           new RuntimePermission("getClassLoader"),
+                           new RuntimePermission("setContextClassLoader"));
+    }
+
+    /**
+     * callable(Runnable) returns null when called
+     */
+    public void testCallable1() throws Exception {
+        Callable c = Executors.callable(new NoOpRunnable());
+        assertNull(c.call());
+    }
+
+    /**
+     * callable(Runnable, result) returns result when called
+     */
+    public void testCallable2() throws Exception {
+        Callable c = Executors.callable(new NoOpRunnable(), one);
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(PrivilegedAction) returns its result when called
+     */
+    public void testCallable3() throws Exception {
+        Callable c = Executors.callable(new PrivilegedAction() {
+                public Object run() { return one; }});
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(PrivilegedExceptionAction) returns its result when called
+     */
+    public void testCallable4() throws Exception {
+        Callable c = Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { return one; }});
+        assertSame(one, c.call());
+    }
+
+    /**
+     * callable(null Runnable) throws NPE
+     */
+    public void testCallableNPE1() {
+        try {
+            Callable c = Executors.callable((Runnable) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null, result) throws NPE
+     */
+    public void testCallableNPE2() {
+        try {
+            Callable c = Executors.callable((Runnable) null, one);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null PrivilegedAction) throws NPE
+     */
+    public void testCallableNPE3() {
+        try {
+            Callable c = Executors.callable((PrivilegedAction) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * callable(null PrivilegedExceptionAction) throws NPE
+     */
+    public void testCallableNPE4() {
+        try {
+            Callable c = Executors.callable((PrivilegedExceptionAction) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ForkJoinPool8Test.java b/ojluni/src/test/java/util/concurrent/tck/ForkJoinPool8Test.java
new file mode 100644
index 0000000..a39fc71
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ForkJoinPool8Test.java
@@ -0,0 +1,1610 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinPool8Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinPool8Test.class);
+    }
+
+    /**
+     * Common pool exists and has expected parallelism.
+     */
+    public void testCommonPoolParallelism() {
+        assertEquals(ForkJoinPool.getCommonPoolParallelism(),
+                     ForkJoinPool.commonPool().getParallelism());
+    }
+
+    /**
+     * Common pool cannot be shut down
+     */
+    public void testCommonPoolShutDown() {
+        assertFalse(ForkJoinPool.commonPool().isShutdown());
+        assertFalse(ForkJoinPool.commonPool().isTerminating());
+        assertFalse(ForkJoinPool.commonPool().isTerminated());
+        ForkJoinPool.commonPool().shutdown();
+        assertFalse(ForkJoinPool.commonPool().isShutdown());
+        assertFalse(ForkJoinPool.commonPool().isTerminating());
+        assertFalse(ForkJoinPool.commonPool().isTerminated());
+        ForkJoinPool.commonPool().shutdownNow();
+        assertFalse(ForkJoinPool.commonPool().isShutdown());
+        assertFalse(ForkJoinPool.commonPool().isTerminating());
+        assertFalse(ForkJoinPool.commonPool().isTerminated());
+    }
+
+    /*
+     * All of the following test methods are adaptations of those for
+     * RecursiveAction and CountedCompleter, but with all actions
+     * executed in the common pool, generally implicitly via
+     * checkInvoke.
+     */
+
+    private void checkInvoke(ForkJoinTask a) {
+        checkNotDone(a);
+        assertNull(a.invoke());
+        checkCompletedNormally(a);
+    }
+
+    void checkNotDone(ForkJoinTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(ForkJoinTask a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+        assertNull(a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(ForkJoinTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(expected.getClass(), t.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+        public FJException(Throwable cause) { super(cause); }
+    }
+
+    // A simple recursive action for testing
+    final class FibAction extends CheckedRecursiveAction {
+        final int number;
+        int result;
+        FibAction(int n) { number = n; }
+        protected void realCompute() {
+            int n = number;
+            if (n <= 1)
+                result = n;
+            else {
+                FibAction f1 = new FibAction(n - 1);
+                FibAction f2 = new FibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    // A recursive action failing in base case
+    static final class FailingFibAction extends RecursiveAction {
+        final int number;
+        int result;
+        FailingFibAction(int n) { number = n; }
+        public void compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            else {
+                FailingFibAction f1 = new FailingFibAction(n - 1);
+                FailingFibAction f2 = new FailingFibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a RecursiveAction returns null;
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterrupts() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                final Thread currentThread = Thread.currentThread();
+
+                // test join()
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                assertNull(f.join());
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    Thread.interrupted();
+                    checkCancelled(f);
+                }
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    Thread.interrupted();
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin()
+                f = new FibAction(8);
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                f.quietlyJoin();
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCancelled(f);
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+        a.reinitialize();
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(5L, SECONDS));
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    assertNull(f.invoke());
+                    assertEquals(21, f.result);
+                    checkCompletedNormally(f);
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.complete(null);
+                assertNull(f.invoke());
+                assertEquals(0, f.result);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                FibAction h = new FibAction(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    // CountedCompleter versions
+
+    abstract static class CCF extends CountedCompleter {
+        int number;
+        int rnumber;
+
+        public CCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        public final void compute() {
+            CountedCompleter p;
+            CCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RCCF(f, n - 2).fork();
+                f = new LCCF(f, --n);
+            }
+            f.number = n;
+            f.onCompletion(f);
+            if ((p = f.getCompleter()) != null)
+                p.tryComplete();
+            else
+                f.quietlyComplete();
+        }
+    }
+
+    static final class LCCF extends CCF {
+        public LCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    static final class RCCF extends CCF {
+        public RCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            CCF p = (CCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.rnumber = n;
+            else
+                number = n;
+        }
+    }
+
+    // Version of CCF with forced failure in left completions
+    abstract static class FailingCCF extends CountedCompleter {
+        int number;
+        int rnumber;
+
+        public FailingCCF(CountedCompleter parent, int n) {
+            super(parent, 1);
+            this.number = n;
+        }
+
+        public final void compute() {
+            CountedCompleter p;
+            FailingCCF f = this;
+            int n = number;
+            while (n >= 2) {
+                new RFCCF(f, n - 2).fork();
+                f = new LFCCF(f, --n);
+            }
+            f.number = n;
+            f.onCompletion(f);
+            if ((p = f.getCompleter()) != null)
+                p.tryComplete();
+            else
+                f.quietlyComplete();
+        }
+    }
+
+    static final class LFCCF extends FailingCCF {
+        public LFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            FailingCCF p = (FailingCCF)getCompleter();
+            int n = number + rnumber;
+            if (p != null)
+                p.number = n;
+            else
+                number = n;
+        }
+    }
+    static final class RFCCF extends FailingCCF {
+        public RFCCF(CountedCompleter parent, int n) {
+            super(parent, n);
+        }
+        public final void onCompletion(CountedCompleter caller) {
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPECC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetCC() throws Exception {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResultCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPECC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                FailingCCF g = new LFCCF(null, 9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF g = new LFCCF(null, 9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3CC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                CCF f = new LCCF(null, 8);
+                FailingCCF g = new LFCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionCC() {
+        ForkJoinTask a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingCCF f = new LFCCF(null, 8);
+                CCF g = new LCCF(null, 9);
+                CCF h = new LCCF(null, 7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        checkInvoke(a);
+    }
+
+    /**
+     * awaitQuiescence by a worker is equivalent in effect to
+     * ForkJoinTask.helpQuiesce()
+     */
+    public void testAwaitQuiescence1() throws Exception {
+        final ForkJoinPool p = new ForkJoinPool();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            assertTrue(p.isQuiescent());
+            ForkJoinTask a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    FibAction f = new FibAction(8);
+                    assertSame(f, f.fork());
+                    assertSame(p, ForkJoinTask.getPool());
+                    boolean quiescent = p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS);
+                    assertTrue(quiescent);
+                    assertFalse(p.isQuiescent());
+                    while (!f.isDone()) {
+                        assertFalse(p.getAsyncMode());
+                        assertFalse(p.isShutdown());
+                        assertFalse(p.isTerminating());
+                        assertFalse(p.isTerminated());
+                        Thread.yield();
+                    }
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                    assertFalse(p.isQuiescent());
+                    assertEquals(0, ForkJoinTask.getQueuedTaskCount());
+                    assertEquals(21, f.result);
+                }};
+            p.execute(a);
+            while (!a.isDone() || !p.isQuiescent()) {
+                assertFalse(p.getAsyncMode());
+                assertFalse(p.isShutdown());
+                assertFalse(p.isTerminating());
+                assertFalse(p.isTerminated());
+                Thread.yield();
+            }
+            assertEquals(0, p.getQueuedTaskCount());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            while (p.getActiveThreadCount() != 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * awaitQuiescence returns when pool isQuiescent() or the indicated
+     * timeout elapsed
+     */
+    public void testAwaitQuiescence2() throws Exception {
+        /**
+         * """It is possible to disable or limit the use of threads in the
+         * common pool by setting the parallelism property to zero. However
+         * doing so may cause unjoined tasks to never be executed."""
+         */
+        if ("0".equals(System.getProperty(
+             "java.util.concurrent.ForkJoinPool.common.parallelism")))
+            return;
+        final ForkJoinPool p = new ForkJoinPool();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertTrue(p.isQuiescent());
+            final long startTime = System.nanoTime();
+            ForkJoinTask a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    FibAction f = new FibAction(8);
+                    assertSame(f, f.fork());
+                    while (!f.isDone()
+                           && millisElapsedSince(startTime) < LONG_DELAY_MS) {
+                        assertFalse(p.getAsyncMode());
+                        assertFalse(p.isShutdown());
+                        assertFalse(p.isTerminating());
+                        assertFalse(p.isTerminated());
+                        Thread.yield();
+                    }
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                    assertEquals(0, ForkJoinTask.getQueuedTaskCount());
+                    assertEquals(21, f.result);
+                }};
+            p.execute(a);
+            assertTrue(p.awaitQuiescence(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isQuiescent());
+            assertTrue(a.isDone());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            while (p.getActiveThreadCount() != 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ForkJoinPoolTest.java b/ojluni/src/test/java/util/concurrent/tck/ForkJoinPoolTest.java
new file mode 100644
index 0000000..d8ad1dd
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ForkJoinPoolTest.java
@@ -0,0 +1,986 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.Future;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinPoolTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinPoolTest.class);
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * 1. shutdown and related methods are tested via super.joinPool.
+     *
+     * 2. newTaskFor and adapters are tested in submit/invoke tests
+     *
+     * 3. We cannot portably test monitoring methods such as
+     * getStealCount() since they rely ultimately on random task
+     * stealing that may cause tasks not to be stolen/propagated
+     * across threads, especially on uniprocessors.
+     *
+     * 4. There are no independently testable ForkJoinWorkerThread
+     * methods, but they are covered here and in task tests.
+     */
+
+    // Some classes to test extension and factory methods
+
+    static class MyError extends Error {}
+
+    // to test handlers
+    static class FailingFJWSubclass extends ForkJoinWorkerThread {
+        public FailingFJWSubclass(ForkJoinPool p) { super(p) ; }
+        protected void onStart() { super.onStart(); throw new MyError(); }
+    }
+
+    static class FailingThreadFactory
+            implements ForkJoinPool.ForkJoinWorkerThreadFactory {
+        final AtomicInteger calls = new AtomicInteger(0);
+        public ForkJoinWorkerThread newThread(ForkJoinPool p) {
+            if (calls.incrementAndGet() > 1) return null;
+            return new FailingFJWSubclass(p);
+        }
+    }
+
+    static class SubFJP extends ForkJoinPool { // to expose protected
+        SubFJP() { super(1); }
+        public int drainTasksTo(Collection<? super ForkJoinTask<?>> c) {
+            return super.drainTasksTo(c);
+        }
+        public ForkJoinTask<?> pollSubmission() {
+            return super.pollSubmission();
+        }
+    }
+
+    static class ManagedLocker implements ForkJoinPool.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());
+        }
+    }
+
+    // A simple recursive task for testing
+    static final class FibTask extends RecursiveTask<Integer> {
+        final int number;
+        FibTask(int n) { number = n; }
+        protected Integer compute() {
+            int n = number;
+            if (n <= 1)
+                return n;
+            FibTask f1 = new FibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+    }
+
+    // A failing task for testing
+    static final class FailingTask extends ForkJoinTask<Void> {
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+        protected final boolean exec() { throw new Error(); }
+        FailingTask() {}
+    }
+
+    // Fib needlessly using locking to test ManagedBlockers
+    static final class LockingFibTask extends RecursiveTask<Integer> {
+        final int number;
+        final ManagedLocker locker;
+        final ReentrantLock lock;
+        LockingFibTask(int n, ManagedLocker locker, ReentrantLock lock) {
+            number = n;
+            this.locker = locker;
+            this.lock = lock;
+        }
+        protected Integer compute() {
+            int n;
+            LockingFibTask f1 = null;
+            LockingFibTask f2 = null;
+            locker.block();
+            n = number;
+            if (n > 1) {
+                f1 = new LockingFibTask(n - 1, locker, lock);
+                f2 = new LockingFibTask(n - 2, locker, lock);
+            }
+            lock.unlock();
+            if (n <= 1)
+                return n;
+            else {
+                f1.fork();
+                return f2.compute() + f1.join();
+            }
+        }
+    }
+
+    /**
+     * Successfully constructed pool reports default factory,
+     * parallelism and async mode policies, no active threads or
+     * tasks, and quiescent running state.
+     */
+    public void testDefaultInitialState() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                       p.getFactory());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getActiveThreadCount());
+            assertEquals(0, p.getStealCount());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+        }
+    }
+
+    /**
+     * Constructor throws if size argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new ForkJoinPool(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if factory argument is null
+     */
+    public void testConstructor2() {
+        try {
+            new ForkJoinPool(1, null, null, false);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getParallelism returns size set in constructor
+     */
+    public void testGetParallelism() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getParallelism());
+        }
+    }
+
+    /**
+     * getPoolSize returns number of started workers.
+     */
+    public void testGetPoolSize() {
+        final CountDownLatch taskStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getActiveThreadCount());
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    taskStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    assertEquals(1, p.getActiveThreadCount());
+                    done.await();
+                }};
+            Future<?> future = p.submit(task);
+            await(taskStarted);
+            assertEquals(1, p.getPoolSize());
+            assertEquals(1, p.getActiveThreadCount());
+            done.countDown();
+        }
+        assertEquals(0, p.getPoolSize());
+        assertEquals(0, p.getActiveThreadCount());
+    }
+
+    /**
+     * awaitTermination on a non-shutdown pool times out
+     */
+    public void testAwaitTermination_timesOut() throws InterruptedException {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isTerminated());
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+            assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+            assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+            assertFalse(p.awaitTermination(0L, NANOSECONDS));
+            assertFalse(p.awaitTermination(0L, MILLISECONDS));
+            long timeoutNanos = 999999L;
+            long startTime = System.nanoTime();
+            assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+            assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+            assertFalse(p.isTerminated());
+            startTime = System.nanoTime();
+            long timeoutMillis = timeoutMillis();
+            assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            assertFalse(p.isTerminated());
+            p.shutdown();
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * setUncaughtExceptionHandler changes handler for uncaught exceptions.
+     *
+     * Additionally tests: Overriding ForkJoinWorkerThread.onStart
+     * performs its defined action
+     */
+    public void testSetUncaughtExceptionHandler() throws InterruptedException {
+        final CountDownLatch uehInvoked = new CountDownLatch(1);
+        final Thread.UncaughtExceptionHandler ueh =
+            new Thread.UncaughtExceptionHandler() {
+                public void uncaughtException(Thread t, Throwable e) {
+                    threadAssertTrue(e instanceof MyError);
+                    threadAssertTrue(t instanceof FailingFJWSubclass);
+                    uehInvoked.countDown();
+                }};
+        ForkJoinPool p = new ForkJoinPool(1, new FailingThreadFactory(),
+                                          ueh, false);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(ueh, p.getUncaughtExceptionHandler());
+            try {
+                p.execute(new FibTask(8));
+                await(uehInvoked);
+            } finally {
+                p.shutdownNow(); // failure might have prevented processing task
+            }
+        }
+    }
+
+    /**
+     * After invoking a single task, isQuiescent eventually becomes
+     * true, at which time queues are empty, threads are not active,
+     * the task has completed successfully, and construction
+     * parameters continue to hold
+     */
+    public void testIsQuiescent() throws Exception {
+        ForkJoinPool p = new ForkJoinPool(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertTrue(p.isQuiescent());
+            long startTime = System.nanoTime();
+            FibTask f = new FibTask(20);
+            p.invoke(f);
+            assertSame(ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                       p.getFactory());
+            while (! p.isQuiescent()) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    throw new AssertionFailedError("timed out");
+                assertFalse(p.getAsyncMode());
+                assertFalse(p.isShutdown());
+                assertFalse(p.isTerminating());
+                assertFalse(p.isTerminated());
+                Thread.yield();
+            }
+
+            assertTrue(p.isQuiescent());
+            assertFalse(p.getAsyncMode());
+            assertEquals(0, p.getQueuedTaskCount());
+            assertEquals(0, p.getQueuedSubmissionCount());
+            assertFalse(p.hasQueuedSubmissions());
+            while (p.getActiveThreadCount() != 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertFalse(p.isShutdown());
+            assertFalse(p.isTerminating());
+            assertFalse(p.isTerminated());
+            assertTrue(f.isDone());
+            assertEquals(6765, (int) f.get());
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Completed submit(ForkJoinTask) returns result
+     */
+    public void testSubmitForkJoinTask() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ForkJoinTask<Integer> f = p.submit(new FibTask(8));
+            assertEquals(21, (int) f.get());
+        }
+    }
+
+    /**
+     * A task submitted after shutdown is rejected
+     */
+    public void testSubmitAfterShutdown() {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            p.shutdown();
+            assertTrue(p.isShutdown());
+            try {
+                ForkJoinTask<Integer> f = p.submit(new FibTask(8));
+                shouldThrow();
+            } catch (RejectedExecutionException success) {}
+        }
+    }
+
+    /**
+     * Pool maintains parallelism when using ManagedBlocker
+     */
+    public void testBlockingForkJoinTask() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(4);
+        try {
+            ReentrantLock lock = new ReentrantLock();
+            ManagedLocker locker = new ManagedLocker(lock);
+            ForkJoinTask<Integer> f = new LockingFibTask(20, locker, lock);
+            p.execute(f);
+            assertEquals(6765, (int) f.get());
+        } finally {
+            p.shutdownNow(); // don't wait out shutdown
+        }
+    }
+
+    /**
+     * pollSubmission returns unexecuted submitted task, if present
+     */
+    public void testPollSubmission() {
+        final CountDownLatch done = new CountDownLatch(1);
+        SubFJP p = new SubFJP();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ForkJoinTask a = p.submit(awaiter(done));
+            ForkJoinTask b = p.submit(awaiter(done));
+            ForkJoinTask c = p.submit(awaiter(done));
+            ForkJoinTask r = p.pollSubmission();
+            assertTrue(r == a || r == b || r == c);
+            assertFalse(r.isDone());
+            done.countDown();
+        }
+    }
+
+    /**
+     * drainTasksTo transfers unexecuted submitted tasks, if present
+     */
+    public void testDrainTasksTo() {
+        final CountDownLatch done = new CountDownLatch(1);
+        SubFJP p = new SubFJP();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ForkJoinTask a = p.submit(awaiter(done));
+            ForkJoinTask b = p.submit(awaiter(done));
+            ForkJoinTask c = p.submit(awaiter(done));
+            ArrayList<ForkJoinTask> al = new ArrayList();
+            p.drainTasksTo(al);
+            assertTrue(al.size() > 0);
+            for (ForkJoinTask r : al) {
+                assertTrue(r == a || r == b || r == c);
+                assertFalse(r.isDone());
+            }
+            done.countDown();
+        }
+    }
+
+    // FJ Versions of AbstractExecutorService tests
+
+    /**
+     * execute(runnable) runs it to completion
+     */
+    public void testExecuteRunnable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            final AtomicBoolean done = new AtomicBoolean(false);
+            Future<?> future = e.submit(new CheckedRunnable() {
+                public void realRun() {
+                    done.set(true);
+                }});
+            assertNull(future.get());
+            assertNull(future.get(0, MILLISECONDS));
+            assertTrue(done.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * Completed submit(callable) returns result
+     */
+    public void testSubmitCallable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            assertSame(TEST_STRING, future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * Completed submit(runnable) returns successfully
+     */
+    public void testSubmitRunnable() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            assertNull(future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * Completed submit(runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            assertSame(TEST_STRING, future.get());
+            assertTrue(future.isDone());
+            assertFalse(future.isCancelled());
+        }
+    }
+
+    /**
+     * A submitted privileged action runs to completion
+     */
+    public void testSubmitPrivilegedAction() throws Exception {
+        final Callable callable = Executors.callable(new PrivilegedAction() {
+                public Object run() { return TEST_STRING; }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try (PoolCleaner cleaner = cleaner(e)) {
+                Future future = e.submit(callable);
+                assertSame(TEST_STRING, future.get());
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted privileged exception action runs to completion
+     */
+    public void testSubmitPrivilegedExceptionAction() throws Exception {
+        final Callable callable =
+            Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { return TEST_STRING; }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try (PoolCleaner cleaner = cleaner(e)) {
+                Future future = e.submit(callable);
+                assertSame(TEST_STRING, future.get());
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * A submitted failed privileged exception action reports exception
+     */
+    public void testSubmitFailedPrivilegedExceptionAction() throws Exception {
+        final Callable callable =
+            Executors.callable(new PrivilegedExceptionAction() {
+                public Object run() { throw new IndexOutOfBoundsException(); }});
+        Runnable r = new CheckedRunnable() {
+        public void realRun() throws Exception {
+            ExecutorService e = new ForkJoinPool(1);
+            try (PoolCleaner cleaner = cleaner(e)) {
+                Future future = e.submit(callable);
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    assertTrue(success.getCause() instanceof IndexOutOfBoundsException);
+                }
+            }
+        }};
+
+        runWithPermissions(r, new RuntimePermission("modifyThread"));
+    }
+
+    /**
+     * execute(null runnable) throws NullPointerException
+     */
+    public void testExecuteNullRunnable() {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                Future<?> future = e.submit((Runnable) null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * submit(null callable) throws NullPointerException
+     */
+    public void testSubmitNullCallable() {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                Future<String> future = e.submit((Callable) null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * submit(callable).get() throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch submitted    = new CountDownLatch(1);
+        final CountDownLatch quittingTime = new CountDownLatch(1);
+        final Callable<Void> awaiter = new CheckedCallable<Void>() {
+            public Void realCall() throws InterruptedException {
+                assertTrue(quittingTime.await(2*LONG_DELAY_MS, MILLISECONDS));
+                return null;
+            }};
+        final ExecutorService p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p, quittingTime)) {
+            Thread t = new Thread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Future<Void> future = p.submit(awaiter);
+                    submitted.countDown();
+                    future.get();
+                }});
+            t.start();
+            await(submitted);
+            t.interrupt();
+            awaitTermination(t);
+        }
+    }
+
+    /**
+     * get of submit(callable) throws ExecutionException if callable
+     * throws exception
+     */
+    public void testSubmitEE() throws Throwable {
+        ForkJoinPool p = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.submit(new Callable() {
+                        public Object call() { throw new ArithmeticException(); }})
+                    .get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof ArithmeticException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NullPointerException
+     */
+    public void testInvokeAny1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IllegalArgumentException
+     */
+    public void testInvokeAny2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NullPointerException if c has a single null element
+     */
+    public void testInvokeAny3() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NullPointerException if c has null elements
+     */
+    public void testInvokeAny4() throws Throwable {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task in c completes
+     */
+    public void testInvokeAny5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task in c if at least one completes
+     */
+    public void testInvokeAny6() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NullPointerException
+     */
+    public void testInvokeAll1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r
+                = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NullPointerException if c has null elements
+     */
+    public void testInvokeAll3() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws
+     * ExecutionException on failed task
+     */
+    public void testInvokeAll4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testInvokeAll5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NullPointerException
+     */
+    public void testTimedInvokeAny1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(null time unit) throws NullPointerException
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IllegalArgumentException
+     */
+    public void testTimedInvokeAny2() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NullPointerException if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Throwable {
+        CountDownLatch latch = new CountDownLatch(1);
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task in c
+     */
+    public void testTimedInvokeAny5() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NullPointerException
+     */
+    public void testTimedInvokeAll1() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(null time unit) throws NullPointerException
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r
+                = e.invokeAll(new ArrayList<Callable<String>>(),
+                              MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NullPointerException if c has null elements
+     */
+    public void testTimedInvokeAll3() throws InterruptedException {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of returned element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Throwable {
+        ExecutorService e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures
+                = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks in c
+     */
+    public void testTimedInvokeAll5() throws Throwable {
+        ForkJoinPool e = new ForkJoinPool(1);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures
+                = e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ForkJoinTask8Test.java b/ojluni/src/test/java/util/concurrent/tck/ForkJoinTask8Test.java
new file mode 100644
index 0000000..bf9d17c
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ForkJoinTask8Test.java
@@ -0,0 +1,1228 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinTask8Test extends JSR166TestCase {
+
+    /*
+     * Testing notes: This differs from ForkJoinTaskTest mainly by
+     * defining a version of BinaryAsyncAction that uses JDK8 task
+     * tags for control state, thereby testing getForkJoinTaskTag,
+     * setForkJoinTaskTag, and compareAndSetForkJoinTaskTag across
+     * various contexts. Most of the test methods using it are
+     * otherwise identical, but omitting retest of those dealing with
+     * cancellation, which is not represented in this tag scheme.
+     */
+
+    static final short INITIAL_STATE = -1;
+    static final short COMPLETE_STATE = 0;
+    static final short EXCEPTION_STATE = 1;
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinTask8Test.class);
+    }
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    // Compute fib naively and efficiently
+    final int[] fib;
+    {
+        int[] fib = new int[10];
+        fib[0] = 0;
+        fib[1] = 1;
+        for (int i = 2; i < fib.length; i++)
+            fib[i] = fib[i - 1] + fib[i - 2];
+        this.fib = fib;
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        }
+    }
+
+    void checkNotDone(ForkJoinTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+        if (a instanceof BinaryAsyncAction)
+            assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == INITIAL_STATE);
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a) {
+        checkCompletedNormally(a, null);
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+        if (a instanceof BinaryAsyncAction)
+            assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() == COMPLETE_STATE);
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            assertSame(expected, a.join());
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        if (a instanceof BinaryAsyncAction)
+            assertTrue(((BinaryAsyncAction)a).getForkJoinTaskTag() != INITIAL_STATE);
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract static class BinaryAsyncAction extends ForkJoinTask<Void> {
+
+        private volatile BinaryAsyncAction parent;
+
+        private volatile BinaryAsyncAction sibling;
+
+        protected BinaryAsyncAction() {
+            setForkJoinTaskTag(INITIAL_STATE);
+        }
+
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+
+        public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            x.parent = y.parent = this;
+            x.sibling = y;
+            y.sibling = x;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            if (this.getForkJoinTaskTag() != COMPLETE_STATE ||
+                x.getForkJoinTaskTag() != COMPLETE_STATE ||
+                y.getForkJoinTaskTag() != COMPLETE_STATE) {
+                completeThisExceptionally(new FJException());
+            }
+        }
+
+        protected boolean onException() {
+            return true;
+        }
+
+        public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            linkSubtasks(x, y);
+            y.fork();
+            x.fork();
+        }
+
+        private void completeThis() {
+            setForkJoinTaskTag(COMPLETE_STATE);
+            super.complete(null);
+        }
+
+        private void completeThisExceptionally(Throwable ex) {
+            setForkJoinTaskTag(EXCEPTION_STATE);
+            super.completeExceptionally(ex);
+        }
+
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            if (super.cancel(mayInterruptIfRunning)) {
+                completeExceptionally(new FJException());
+                return true;
+            }
+            return false;
+        }
+
+        public final void complete() {
+            BinaryAsyncAction a = this;
+            for (;;) {
+                BinaryAsyncAction s = a.sibling;
+                BinaryAsyncAction p = a.parent;
+                a.sibling = null;
+                a.parent = null;
+                a.completeThis();
+                if (p == null ||
+                    p.compareAndSetForkJoinTaskTag(INITIAL_STATE, COMPLETE_STATE))
+                    break;
+                try {
+                    p.onComplete(a, s);
+                } catch (Throwable rex) {
+                    p.completeExceptionally(rex);
+                    return;
+                }
+                a = p;
+            }
+        }
+
+        public final void completeExceptionally(Throwable ex) {
+            for (BinaryAsyncAction a = this;;) {
+                a.completeThisExceptionally(ex);
+                BinaryAsyncAction s = a.sibling;
+                if (s != null && !s.isDone())
+                    s.completeExceptionally(ex);
+                if ((a = a.parent) == null)
+                    break;
+            }
+        }
+
+        public final BinaryAsyncAction getParent() {
+            return parent;
+        }
+
+        public BinaryAsyncAction getSibling() {
+            return sibling;
+        }
+
+        public void reinitialize() {
+            parent = sibling = null;
+            super.reinitialize();
+        }
+
+    }
+
+    final class AsyncFib extends BinaryAsyncAction {
+        int number;
+        int expectedResult;
+        public AsyncFib(int number) {
+            this.number = number;
+            this.expectedResult = fib[number];
+        }
+
+        public final boolean exec() {
+            try {
+                AsyncFib f = this;
+                int n = f.number;
+                while (n > 1) {
+                    AsyncFib p = f;
+                    AsyncFib r = new AsyncFib(n - 2);
+                    f = new AsyncFib(--n);
+                    p.linkSubtasks(r, f);
+                    r.fork();
+                }
+                f.complete();
+            }
+            catch (Throwable ex) {
+                compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE);
+            }
+            if (getForkJoinTaskTag() == EXCEPTION_STATE)
+                throw new FJException();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            number = ((AsyncFib)x).number + ((AsyncFib)y).number;
+            super.onComplete(x, y);
+        }
+
+        public void checkCompletedNormally() {
+            assertEquals(expectedResult, number);
+            ForkJoinTask8Test.this.checkCompletedNormally(this);
+        }
+    }
+
+    static final class FailingAsyncFib extends BinaryAsyncAction {
+        int number;
+        public FailingAsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            try {
+                FailingAsyncFib f = this;
+                int n = f.number;
+                while (n > 1) {
+                    FailingAsyncFib p = f;
+                    FailingAsyncFib r = new FailingAsyncFib(n - 2);
+                    f = new FailingAsyncFib(--n);
+                    p.linkSubtasks(r, f);
+                    r.fork();
+                }
+                f.complete();
+            }
+            catch (Throwable ex) {
+                compareAndSetForkJoinTaskTag(INITIAL_STATE, EXCEPTION_STATE);
+            }
+            if (getForkJoinTaskTag() == EXCEPTION_STATE)
+                throw new FJException();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        testInvoke(mainPool());
+    }
+    public void testInvoke_Singleton() {
+        testInvoke(singletonPool());
+    }
+    public void testInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        testQuietlyInvoke(mainPool());
+    }
+    public void testQuietlyInvoke_Singleton() {
+        testQuietlyInvoke(singletonPool());
+    }
+    public void testQuietlyInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        testForkJoin(mainPool());
+    }
+    public void testForkJoin_Singleton() {
+        testForkJoin(singletonPool());
+    }
+    public void testForkJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        testForkGet(mainPool());
+    }
+    public void testForkGet_Singleton() {
+        testForkGet(singletonPool());
+    }
+    public void testForkGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        testForkTimedGet(mainPool());
+    }
+    public void testForkTimedGet_Singleton() {
+        testForkTimedGet(singletonPool());
+    }
+    public void testForkTimedGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * timed get with null time unit throws NullPointerException
+     */
+    public void testForkTimedGetNullTimeUnit() {
+        testForkTimedGetNullTimeUnit(mainPool());
+    }
+    public void testForkTimedGetNullTimeUnit_Singleton() {
+        testForkTimedGet(singletonPool());
+    }
+    public void testForkTimedGetNullTimeUnit(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        testForkQuietlyJoin(mainPool());
+    }
+    public void testForkQuietlyJoin_Singleton() {
+        testForkQuietlyJoin(singletonPool());
+    }
+    public void testForkQuietlyJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        testForkHelpQuiesce(mainPool());
+    }
+    public void testForkHelpQuiesce_Singleton() {
+        testForkHelpQuiesce(singletonPool());
+    }
+    public void testForkHelpQuiesce(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        testAbnormalInvoke(mainPool());
+    }
+    public void testAbnormalInvoke_Singleton() {
+        testAbnormalInvoke(singletonPool());
+    }
+    public void testAbnormalInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        testAbnormalQuietlyInvoke(mainPool());
+    }
+    public void testAbnormalQuietlyInvoke_Singleton() {
+        testAbnormalQuietlyInvoke(singletonPool());
+    }
+    public void testAbnormalQuietlyInvoke(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        testAbnormalForkJoin(mainPool());
+    }
+    public void testAbnormalForkJoin_Singleton() {
+        testAbnormalForkJoin(singletonPool());
+    }
+    public void testAbnormalForkJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        testAbnormalForkGet(mainPool());
+    }
+    public void testAbnormalForkGet_Singleton() {
+        testAbnormalForkJoin(singletonPool());
+    }
+    public void testAbnormalForkGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        testAbnormalForkTimedGet(mainPool());
+    }
+    public void testAbnormalForkTimedGet_Singleton() {
+        testAbnormalForkTimedGet(singletonPool());
+    }
+    public void testAbnormalForkTimedGet(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        testAbnormalForkQuietlyJoin(mainPool());
+    }
+    public void testAbnormalForkQuietlyJoin_Singleton() {
+        testAbnormalForkQuietlyJoin(singletonPool());
+    }
+    public void testAbnormalForkQuietlyJoin(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        testGetPool(mainPool());
+    }
+    public void testGetPool_Singleton() {
+        testGetPool(singletonPool());
+    }
+    public void testGetPool(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(pool, getPool());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        testInForkJoinPool(mainPool());
+    }
+    public void testInForkJoinPool_Singleton() {
+        testInForkJoinPool(singletonPool());
+    }
+    public void testInForkJoinPool(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        testCompleteExceptionally(mainPool());
+    }
+    public void testCompleteExceptionally_Singleton() {
+        testCompleteExceptionally(singletonPool());
+    }
+    public void testCompleteExceptionally(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        testInvokeAll1(mainPool());
+    }
+    public void testInvokeAll1_Singleton() {
+        testInvokeAll1(singletonPool());
+    }
+    public void testInvokeAll1(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                f.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        testInvokeAll2(mainPool());
+    }
+    public void testInvokeAll2_Singleton() {
+        testInvokeAll2(singletonPool());
+    }
+    public void testInvokeAll2(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib[] tasks = {
+                    new AsyncFib(8),
+                    new AsyncFib(9),
+                };
+                invokeAll(tasks[0], tasks[1]);
+                for (AsyncFib task : tasks) assertTrue(task.isDone());
+                for (AsyncFib task : tasks) task.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        testInvokeAll3(mainPool());
+    }
+    public void testInvokeAll3_Singleton() {
+        testInvokeAll3(singletonPool());
+    }
+    public void testInvokeAll3(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib[] tasks = {
+                    new AsyncFib(8),
+                    new AsyncFib(9),
+                    new AsyncFib(7),
+                };
+                invokeAll(tasks[0], tasks[1], tasks[2]);
+                for (AsyncFib task : tasks) assertTrue(task.isDone());
+                for (AsyncFib task : tasks) task.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        testInvokeAllCollection(mainPool());
+    }
+    public void testInvokeAllCollection_Singleton() {
+        testInvokeAllCollection(singletonPool());
+    }
+    public void testInvokeAllCollection(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib[] tasks = {
+                    new AsyncFib(8),
+                    new AsyncFib(9),
+                    new AsyncFib(7),
+                };
+                invokeAll(Arrays.asList(tasks));
+                for (AsyncFib task : tasks) assertTrue(task.isDone());
+                for (AsyncFib task : tasks) task.checkCompletedNormally();
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NullPointerException
+     */
+    public void testInvokeAllNullTask() {
+        testInvokeAllNullTask(mainPool());
+    }
+    public void testInvokeAllNullTask_Singleton() {
+        testInvokeAllNullTask(singletonPool());
+    }
+    public void testInvokeAllNullTask(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib nul = null;
+                Runnable[] throwingActions = {
+                    () -> invokeAll(nul),
+                    () -> invokeAll(nul, nul),
+                    () -> invokeAll(new AsyncFib(8), new AsyncFib(9), nul),
+                    () -> invokeAll(new AsyncFib(8), nul, new AsyncFib(9)),
+                    () -> invokeAll(nul, new AsyncFib(8), new AsyncFib(9)),
+                };
+                assertThrows(NullPointerException.class, throwingActions);
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        testAbnormalInvokeAll1(mainPool());
+    }
+    public void testAbnormalInvokeAll1_Singleton() {
+        testAbnormalInvokeAll1(singletonPool());
+    }
+    public void testAbnormalInvokeAll1(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        testAbnormalInvokeAll2(mainPool());
+    }
+    public void testAbnormalInvokeAll2_Singleton() {
+        testAbnormalInvokeAll2(singletonPool());
+    }
+    public void testAbnormalInvokeAll2(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                ForkJoinTask[] tasks = { f, g };
+                shuffle(tasks);
+                try {
+                    invokeAll(tasks[0], tasks[1]);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        testAbnormalInvokeAll3(mainPool());
+    }
+    public void testAbnormalInvokeAll3_Singleton() {
+        testAbnormalInvokeAll3(singletonPool());
+    }
+    public void testAbnormalInvokeAll3(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                shuffle(tasks);
+                try {
+                    invokeAll(tasks[0], tasks[1], tasks[2]);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        testAbnormalInvokeAllCollection(mainPool());
+    }
+    public void testAbnormalInvokeAllCollection_Singleton() {
+        testAbnormalInvokeAllCollection(singletonPool());
+    }
+    public void testAbnormalInvokeAllCollection(ForkJoinPool pool) {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                shuffle(tasks);
+                try {
+                    invokeAll(Arrays.asList(tasks));
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(pool, a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib h = new AsyncFib(7);
+                assertSame(h, h.fork());
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                f.checkCompletedNormally();
+                g.checkCompletedNormally();
+                h.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                f.checkCompletedNormally();
+                helpQuiesce();
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                f.checkCompletedNormally();
+                g.checkCompletedNormally();
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                f.checkCompletedNormally();
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                f.checkCompletedNormally();
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * ForkJoinTask.quietlyComplete returns when task completes
+     * normally without setting a value. The most recent value
+     * established by setRawResult(V) (or null by default) is returned
+     * from invoke.
+     */
+    public void testQuietlyComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    AsyncFib f = new AsyncFib(8);
+                    f.quietlyComplete();
+                    assertEquals(8, f.number);
+                    assertTrue(f.isDone());
+                    assertFalse(f.isCancelled());
+                    assertTrue(f.isCompletedNormally());
+                    assertFalse(f.isCompletedAbnormally());
+                    assertNull(f.getException());
+                }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    // jdk9
+
+    /**
+     * pollSubmission returns unexecuted submitted task, if present
+     */
+    public void testPollSubmission() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ForkJoinTask a = ForkJoinTask.adapt(awaiter(done));
+        final ForkJoinTask b = ForkJoinTask.adapt(awaiter(done));
+        final ForkJoinTask c = ForkJoinTask.adapt(awaiter(done));
+        final ForkJoinPool p = singletonPool();
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Thread external = new Thread(new CheckedRunnable() {
+                public void realRun() {
+                    p.execute(a);
+                    p.execute(b);
+                    p.execute(c);
+                }});
+            RecursiveAction s = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    external.start();
+                    try {
+                        external.join();
+                    } catch (Exception ex) {
+                        threadUnexpectedException(ex);
+                    }
+                    assertTrue(p.hasQueuedSubmissions());
+                    assertTrue(Thread.currentThread() instanceof ForkJoinWorkerThread);
+                    ForkJoinTask r = ForkJoinTask.pollSubmission();
+                    assertTrue(r == a || r == b || r == c);
+                    assertFalse(r.isDone());
+                }};
+            p.invoke(s);
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ForkJoinTaskTest.java b/ojluni/src/test/java/util/concurrent/tck/ForkJoinTaskTest.java
new file mode 100644
index 0000000..2255fda
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ForkJoinTaskTest.java
@@ -0,0 +1,1682 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ForkJoinTaskTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ForkJoinTaskTest.class);
+    }
+
+    // Runs with "mainPool" use > 1 thread. singletonPool tests use 1
+    static final int mainPoolSize =
+        Math.max(2, Runtime.getRuntime().availableProcessors());
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool(mainPoolSize);
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            assertFalse(a.isDone());
+            assertFalse(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+
+            assertNull(pool.invoke(a));
+
+            assertTrue(a.isDone());
+            assertTrue(a.isCompletedNormally());
+            assertFalse(a.isCompletedAbnormally());
+            assertFalse(a.isCancelled());
+            assertNull(a.getException());
+            assertNull(a.getRawResult());
+        }
+    }
+
+    void checkNotDone(ForkJoinTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a) {
+        checkCompletedNormally(a, null);
+    }
+
+    <T> void checkCompletedNormally(ForkJoinTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            assertSame(expected, a.join());
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        {
+            Thread.currentThread().interrupt();
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+            Thread.interrupted();
+        }
+
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(ForkJoinTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+        assertTrue(a.cancel(false));
+        assertTrue(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(ForkJoinTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            Thread.currentThread().interrupt();
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+        Thread.interrupted();
+
+        {
+            long startTime = System.nanoTime();
+            a.quietlyJoin();        // should be no-op
+            assertTrue(millisElapsedSince(startTime) < SMALL_DELAY_MS);
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * To test extension methods and overrides, most tests use
+     * BinaryAsyncAction extension class that processes joins
+     * differently than supplied Recursive forms.
+     */
+
+    public static final class FJException extends RuntimeException {
+        FJException() { super(); }
+    }
+
+    abstract static class BinaryAsyncAction extends ForkJoinTask<Void> {
+        private volatile int controlState;
+
+        static final AtomicIntegerFieldUpdater<BinaryAsyncAction> controlStateUpdater =
+            AtomicIntegerFieldUpdater.newUpdater(BinaryAsyncAction.class,
+                                                 "controlState");
+
+        private volatile BinaryAsyncAction parent;
+
+        private volatile BinaryAsyncAction sibling;
+
+        protected BinaryAsyncAction() {
+        }
+
+        public final Void getRawResult() { return null; }
+        protected final void setRawResult(Void mustBeNull) { }
+
+        public final void linkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            x.parent = y.parent = this;
+            x.sibling = y;
+            y.sibling = x;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+        }
+
+        protected boolean onException() {
+            return true;
+        }
+
+        public void linkAndForkSubtasks(BinaryAsyncAction x, BinaryAsyncAction y) {
+            linkSubtasks(x, y);
+            y.fork();
+            x.fork();
+        }
+
+        private void completeThis() {
+            super.complete(null);
+        }
+
+        private void completeThisExceptionally(Throwable ex) {
+            super.completeExceptionally(ex);
+        }
+
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            if (super.cancel(mayInterruptIfRunning)) {
+                completeExceptionally(new FJException());
+                return true;
+            }
+            return false;
+        }
+
+        public final void complete() {
+            BinaryAsyncAction a = this;
+            for (;;) {
+                BinaryAsyncAction s = a.sibling;
+                BinaryAsyncAction p = a.parent;
+                a.sibling = null;
+                a.parent = null;
+                a.completeThis();
+                if (p == null || p.compareAndSetControlState(0, 1))
+                    break;
+                try {
+                    p.onComplete(a, s);
+                } catch (Throwable rex) {
+                    p.completeExceptionally(rex);
+                    return;
+                }
+                a = p;
+            }
+        }
+
+        public final void completeExceptionally(Throwable ex) {
+            for (BinaryAsyncAction a = this;;) {
+                a.completeThisExceptionally(ex);
+                BinaryAsyncAction s = a.sibling;
+                if (s != null && !s.isDone())
+                    s.completeExceptionally(ex);
+                if ((a = a.parent) == null)
+                    break;
+            }
+        }
+
+        public final BinaryAsyncAction getParent() {
+            return parent;
+        }
+
+        public BinaryAsyncAction getSibling() {
+            return sibling;
+        }
+
+        public void reinitialize() {
+            parent = sibling = null;
+            super.reinitialize();
+        }
+
+        protected final int getControlState() {
+            return controlState;
+        }
+
+        protected final boolean compareAndSetControlState(int expect,
+                                                          int update) {
+            return controlStateUpdater.compareAndSet(this, expect, update);
+        }
+
+        protected final void setControlState(int value) {
+            controlState = value;
+        }
+
+        protected final void incrementControlState() {
+            controlStateUpdater.incrementAndGet(this);
+        }
+
+        protected final void decrementControlState() {
+            controlStateUpdater.decrementAndGet(this);
+        }
+
+    }
+
+    static final class AsyncFib extends BinaryAsyncAction {
+        int number;
+        public AsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            AsyncFib f = this;
+            int n = f.number;
+            while (n > 1) {
+                AsyncFib p = f;
+                AsyncFib r = new AsyncFib(n - 2);
+                f = new AsyncFib(--n);
+                p.linkSubtasks(r, f);
+                r.fork();
+            }
+            f.complete();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            number = ((AsyncFib)x).number + ((AsyncFib)y).number;
+        }
+    }
+
+    static final class FailingAsyncFib extends BinaryAsyncAction {
+        int number;
+        public FailingAsyncFib(int n) {
+            this.number = n;
+        }
+
+        public final boolean exec() {
+            FailingAsyncFib f = this;
+            int n = f.number;
+            while (n > 1) {
+                FailingAsyncFib p = f;
+                FailingAsyncFib r = new FailingAsyncFib(n - 2);
+                f = new FailingAsyncFib(--n);
+                p.linkSubtasks(r, f);
+                r.fork();
+            }
+            f.complete();
+            return false;
+        }
+
+        protected void onComplete(BinaryAsyncAction x, BinaryAsyncAction y) {
+            completeExceptionally(new FJException());
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() throws Exception {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * completeExceptionally(null) surprisingly has the same effect as
+     * completeExceptionally(new RuntimeException())
+     */
+    public void testCompleteExceptionally_null() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(null);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (RuntimeException success) {
+                    assertSame(success.getClass(), RuntimeException.class);
+                    assertNull(success.getCause());
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                ForkJoinTask[] tasks = { f, g };
+                shuffle(tasks);
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                shuffle(tasks);
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                shuffle(tasks);
+                try {
+                    invokeAll(Arrays.asList(tasks));
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib h = new AsyncFib(7);
+                assertSame(h, h.fork());
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task without
+     * executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                assertEquals(34, g.number);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib g = new AsyncFib(9);
+                assertSame(g, g.fork());
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    // versions for singleton pools
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks; getRawResult returns null.
+     */
+    public void testInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPESingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesceSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                assertEquals(0, getQueuedTaskCount());
+                assertEquals(21, f.number);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvokeSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGetSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGetSingleton() throws Exception {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoinSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionallySingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                invokeAll(f, g);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.number);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                invokeAll(f, g, h);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollectionSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertEquals(21, f.number);
+                assertEquals(34, g.number);
+                assertEquals(13, h.number);
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPESingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                ForkJoinTask[] tasks = { f, g };
+                shuffle(tasks);
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3Singleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                AsyncFib f = new AsyncFib(8);
+                FailingAsyncFib g = new FailingAsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                shuffle(tasks);
+                try {
+                    invokeAll(tasks);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollectionSingleton() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingAsyncFib f = new FailingAsyncFib(8);
+                AsyncFib g = new AsyncFib(9);
+                AsyncFib h = new AsyncFib(7);
+                ForkJoinTask[] tasks = { f, g, h };
+                shuffle(tasks);
+                try {
+                    invokeAll(Arrays.asList(tasks));
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * ForkJoinTask.quietlyComplete returns when task completes
+     * normally without setting a value. The most recent value
+     * established by setRawResult(V) (or null by default) is returned
+     * from invoke.
+     */
+    public void testQuietlyComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+                protected void realCompute() {
+                    AsyncFib f = new AsyncFib(8);
+                    f.quietlyComplete();
+                    assertEquals(8, f.number);
+                    checkCompletedNormally(f);
+                }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/FutureTaskTest.java b/ojluni/src/test/java/util/concurrent/tck/FutureTaskTest.java
new file mode 100644
index 0000000..40f0a84
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/FutureTaskTest.java
@@ -0,0 +1,867 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class FutureTaskTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(FutureTaskTest.class);
+    }
+
+    void checkIsDone(Future<?> f) {
+        assertTrue(f.isDone());
+        assertFalse(f.cancel(false));
+        assertFalse(f.cancel(true));
+        if (f instanceof PublicFutureTask) {
+            PublicFutureTask pf = (PublicFutureTask) f;
+            assertEquals(1, pf.doneCount());
+            assertFalse(pf.runAndReset());
+            assertEquals(1, pf.doneCount());
+            Object r = null; Object exInfo = null;
+            try {
+                r = f.get();
+            } catch (CancellationException t) {
+                exInfo = CancellationException.class;
+            } catch (ExecutionException t) {
+                exInfo = t.getCause();
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+
+            // Check that run and runAndReset have no effect.
+            int savedRunCount = pf.runCount();
+            pf.run();
+            pf.runAndReset();
+            assertEquals(savedRunCount, pf.runCount());
+            try {
+                assertSame(r, f.get());
+            } catch (CancellationException t) {
+                assertSame(exInfo, CancellationException.class);
+            } catch (ExecutionException t) {
+                assertSame(exInfo, t.getCause());
+            } catch (Throwable t) {
+                threadUnexpectedException(t);
+            }
+            assertTrue(f.isDone());
+        }
+    }
+
+    void checkNotDone(Future<?> f) {
+        assertFalse(f.isDone());
+        assertFalse(f.isCancelled());
+        if (f instanceof PublicFutureTask) {
+            PublicFutureTask pf = (PublicFutureTask) f;
+            assertEquals(0, pf.doneCount());
+            assertEquals(0, pf.setCount());
+            assertEquals(0, pf.setExceptionCount());
+        }
+    }
+
+    void checkIsRunning(Future<?> f) {
+        checkNotDone(f);
+        if (f instanceof FutureTask) {
+            FutureTask ft = (FutureTask<?>) f;
+            // Check that run methods do nothing
+            ft.run();
+            if (f instanceof PublicFutureTask) {
+                PublicFutureTask pf = (PublicFutureTask) f;
+                int savedRunCount = pf.runCount();
+                pf.run();
+                assertFalse(pf.runAndReset());
+                assertEquals(savedRunCount, pf.runCount());
+            }
+            checkNotDone(f);
+        }
+    }
+
+    <T> void checkCompletedNormally(Future<T> f, T expected) {
+        checkIsDone(f);
+        assertFalse(f.isCancelled());
+
+        try {
+            assertSame(expected, f.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, f.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(Future<?> f) {
+        checkIsDone(f);
+        assertTrue(f.isCancelled());
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void tryToConfuseDoneTask(PublicFutureTask pf) {
+        pf.set(new Object());
+        pf.setException(new Error());
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            pf.cancel(mayInterruptIfRunning);
+        }
+    }
+
+    void checkCompletedAbnormally(Future<?> f, Throwable t) {
+        checkIsDone(f);
+        assertFalse(f.isCancelled());
+
+        try {
+            f.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            f.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t, success.getCause());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicFutureTask extends FutureTask {
+        private final AtomicInteger runCount;
+        private final AtomicInteger doneCount = new AtomicInteger(0);
+        private final AtomicInteger runAndResetCount = new AtomicInteger(0);
+        private final AtomicInteger setCount = new AtomicInteger(0);
+        private final AtomicInteger setExceptionCount = new AtomicInteger(0);
+        public int runCount() { return runCount.get(); }
+        public int doneCount() { return doneCount.get(); }
+        public int runAndResetCount() { return runAndResetCount.get(); }
+        public int setCount() { return setCount.get(); }
+        public int setExceptionCount() { return setExceptionCount.get(); }
+
+        PublicFutureTask(Runnable runnable) {
+            this(runnable, seven);
+        }
+        PublicFutureTask(Runnable runnable, Object result) {
+            this(runnable, result, new AtomicInteger(0));
+        }
+        private PublicFutureTask(final Runnable runnable, Object result,
+                                 final AtomicInteger runCount) {
+            super(new Runnable() {
+                public void run() {
+                    runCount.getAndIncrement();
+                    runnable.run();
+                }}, result);
+            this.runCount = runCount;
+        }
+        PublicFutureTask(Callable callable) {
+            this(callable, new AtomicInteger(0));
+        }
+        private PublicFutureTask(final Callable callable,
+                                 final AtomicInteger runCount) {
+            super(new Callable() {
+                public Object call() throws Exception {
+                    runCount.getAndIncrement();
+                    return callable.call();
+                }});
+            this.runCount = runCount;
+        }
+        @Override public void done() {
+            assertTrue(isDone());
+            doneCount.incrementAndGet();
+            super.done();
+        }
+        @Override public boolean runAndReset() {
+            runAndResetCount.incrementAndGet();
+            return super.runAndReset();
+        }
+        @Override public void set(Object x) {
+            setCount.incrementAndGet();
+            super.set(x);
+        }
+        @Override public void setException(Throwable t) {
+            setExceptionCount.incrementAndGet();
+            super.setException(t);
+        }
+    }
+
+    class Counter extends CheckedRunnable {
+        final AtomicInteger count = new AtomicInteger(0);
+        public int get() { return count.get(); }
+        public void realRun() {
+            count.getAndIncrement();
+        }
+    }
+
+    /**
+     * creating a future with a null callable throws NullPointerException
+     */
+    public void testConstructor() {
+        try {
+            new FutureTask(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * creating a future with null runnable throws NullPointerException
+     */
+    public void testConstructor2() {
+        try {
+            new FutureTask(null, Boolean.TRUE);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * isDone is true when a task completes
+     */
+    public void testIsDone() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertFalse(task.isDone());
+        task.run();
+        assertTrue(task.isDone());
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * runAndReset of a non-cancelled task succeeds
+     */
+    public void testRunAndReset() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        for (int i = 0; i < 3; i++) {
+            assertTrue(task.runAndReset());
+            checkNotDone(task);
+            assertEquals(i + 1, task.runCount());
+            assertEquals(i + 1, task.runAndResetCount());
+            assertEquals(0, task.setCount());
+            assertEquals(0, task.setExceptionCount());
+        }
+    }
+
+    /**
+     * runAndReset after cancellation fails
+     */
+    public void testRunAndResetAfterCancel() {
+        for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
+            PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+            assertTrue(task.cancel(mayInterruptIfRunning));
+            for (int i = 0; i < 3; i++) {
+                assertFalse(task.runAndReset());
+                assertEquals(0, task.runCount());
+                assertEquals(i + 1, task.runAndResetCount());
+                assertEquals(0, task.setCount());
+                assertEquals(0, task.setExceptionCount());
+            }
+            tryToConfuseDoneTask(task);
+            checkCancelled(task);
+        }
+    }
+
+    /**
+     * setting value causes get to return it
+     */
+    public void testSet() throws Exception {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.set(one);
+        for (int i = 0; i < 3; i++) {
+            assertSame(one, task.get());
+            assertSame(one, task.get(LONG_DELAY_MS, MILLISECONDS));
+            assertEquals(1, task.setCount());
+        }
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, one);
+        assertEquals(0, task.runCount());
+    }
+
+    /**
+     * setException causes get to throw ExecutionException
+     */
+    public void testSetException_get() throws Exception {
+        Exception nse = new NoSuchElementException();
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.setException(nse);
+
+        try {
+            task.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(nse, success.getCause());
+            checkCompletedAbnormally(task, nse);
+        }
+
+        try {
+            task.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(nse, success.getCause());
+            checkCompletedAbnormally(task, nse);
+        }
+
+        assertEquals(1, task.setExceptionCount());
+        assertEquals(0, task.setCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedAbnormally(task, nse);
+        assertEquals(0, task.runCount());
+    }
+
+    /**
+     * cancel(false) before run succeeds
+     */
+    public void testCancelBeforeRun() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertTrue(task.cancel(false));
+        task.run();
+        assertEquals(0, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        tryToConfuseDoneTask(task);
+        assertEquals(0, task.runCount());
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) before run succeeds
+     */
+    public void testCancelBeforeRun2() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        assertTrue(task.cancel(true));
+        task.run();
+        assertEquals(0, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        tryToConfuseDoneTask(task);
+        assertEquals(0, task.runCount());
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(false) of a completed task fails
+     */
+    public void testCancelAfterRun() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.run();
+        assertFalse(task.cancel(false));
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * cancel(true) of a completed task fails
+     */
+    public void testCancelAfterRun2() {
+        PublicFutureTask task = new PublicFutureTask(new NoOpCallable());
+        task.run();
+        assertFalse(task.cancel(true));
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, Boolean.TRUE);
+        assertEquals(1, task.runCount());
+    }
+
+    /**
+     * cancel(true) interrupts a running task that subsequently succeeds
+     */
+    public void testCancelInterrupt() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedRunnable() {
+                public void realRun() {
+                    pleaseCancel.countDown();
+                    try {
+                        delay(LONG_DELAY_MS);
+                        shouldThrow();
+                    } catch (InterruptedException success) {}
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(true));
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) tries to interrupt a running task, but
+     * Thread.interrupt throws (simulating a restrictive security
+     * manager)
+     */
+    public void testCancelInterrupt_ThrowsSecurityException() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedRunnable() {
+                public void realRun() {
+                    pleaseCancel.countDown();
+                    await(cancelled);
+                    assertFalse(Thread.interrupted());
+                }});
+
+        final Thread t = new Thread(task) {
+            // Simulate a restrictive security manager.
+            @Override public void interrupt() {
+                throw new SecurityException();
+            }};
+        t.setDaemon(true);
+        t.start();
+
+        await(pleaseCancel);
+        try {
+            task.cancel(true);
+            shouldThrow();
+        } catch (SecurityException expected) {}
+
+        // We failed to deliver the interrupt, but the world retains
+        // its sanity, as if we had done task.cancel(false)
+        assertTrue(task.isCancelled());
+        assertTrue(task.isDone());
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.doneCount());
+        assertEquals(0, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        cancelled.countDown();
+        awaitTermination(t);
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(true) interrupts a running task that subsequently throws
+     */
+    public void testCancelInterrupt_taskFails() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new Runnable() {
+                public void run() {
+                    pleaseCancel.countDown();
+                    try {
+                        delay(LONG_DELAY_MS);
+                        threadShouldThrow();
+                    } catch (InterruptedException success) {
+                    } catch (Throwable t) { threadUnexpectedException(t); }
+                    throw new RuntimeException();
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(true));
+        assertTrue(task.isCancelled());
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(1, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * cancel(false) does not interrupt a running task
+     */
+    public void testCancelNoInterrupt() {
+        final CountDownLatch pleaseCancel = new CountDownLatch(1);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    pleaseCancel.countDown();
+                    await(cancelled);
+                    assertFalse(Thread.interrupted());
+                    return Boolean.TRUE;
+                }});
+
+        Thread t = newStartedThread(task);
+        await(pleaseCancel);
+        assertTrue(task.cancel(false));
+        assertTrue(task.isCancelled());
+        cancelled.countDown();
+        awaitTermination(t);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * run in one thread causes get in another thread to retrieve value
+     */
+    public void testGetRun() {
+        final CountDownLatch pleaseRun = new CountDownLatch(2);
+
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Object>() {
+                public Object realCall() {
+                    return two;
+                }});
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseRun.countDown();
+                assertSame(two, task.get());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseRun.countDown();
+                assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        await(pleaseRun);
+        checkNotDone(task);
+        assertTrue(t1.isAlive());
+        assertTrue(t2.isAlive());
+        task.run();
+        checkCompletedNormally(task, two);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        awaitTermination(t1);
+        awaitTermination(t2);
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, two);
+    }
+
+    /**
+     * set in one thread causes get in another thread to retrieve value
+     */
+    public void testGetSet() {
+        final CountDownLatch pleaseSet = new CountDownLatch(2);
+
+        final PublicFutureTask task =
+            new PublicFutureTask(new CheckedCallable<Object>() {
+                public Object realCall() throws InterruptedException {
+                    return two;
+                }});
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseSet.countDown();
+                assertSame(two, task.get());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseSet.countDown();
+                assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        await(pleaseSet);
+        checkNotDone(task);
+        assertTrue(t1.isAlive());
+        assertTrue(t2.isAlive());
+        task.set(two);
+        assertEquals(0, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCompletedNormally(task, two);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Cancelling a task causes timed get in another thread to throw
+     * CancellationException
+     */
+    public void testTimedGet_Cancellation() {
+        testTimedGet_Cancellation(false);
+    }
+    public void testTimedGet_Cancellation_interrupt() {
+        testTimedGet_Cancellation(true);
+    }
+    public void testTimedGet_Cancellation(final boolean mayInterruptIfRunning) {
+        final CountDownLatch pleaseCancel = new CountDownLatch(3);
+        final CountDownLatch cancelled = new CountDownLatch(1);
+        final Callable<Object> callable =
+            new CheckedCallable<Object>() {
+            public Object realCall() throws InterruptedException {
+                pleaseCancel.countDown();
+                if (mayInterruptIfRunning) {
+                    try {
+                        delay(2*LONG_DELAY_MS);
+                    } catch (InterruptedException success) {}
+                } else {
+                    await(cancelled);
+                }
+                return two;
+            }};
+        final PublicFutureTask task = new PublicFutureTask(callable);
+
+        Thread t1 = new ThreadShouldThrow(CancellationException.class) {
+                public void realRun() throws Exception {
+                    pleaseCancel.countDown();
+                    task.get();
+                }};
+        Thread t2 = new ThreadShouldThrow(CancellationException.class) {
+                public void realRun() throws Exception {
+                    pleaseCancel.countDown();
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                }};
+        t1.start();
+        t2.start();
+        Thread t3 = newStartedThread(task);
+        await(pleaseCancel);
+        checkIsRunning(task);
+        task.cancel(mayInterruptIfRunning);
+        checkCancelled(task);
+        awaitTermination(t1);
+        awaitTermination(t2);
+        cancelled.countDown();
+        awaitTermination(t3);
+        assertEquals(1, task.runCount());
+        assertEquals(1, task.setCount());
+        assertEquals(0, task.setExceptionCount());
+        tryToConfuseDoneTask(task);
+        checkCancelled(task);
+    }
+
+    /**
+     * A runtime exception in task causes get to throw ExecutionException
+     */
+    public void testGet_ExecutionException() throws InterruptedException {
+        final ArithmeticException e = new ArithmeticException();
+        final PublicFutureTask task = new PublicFutureTask(new Callable() {
+            public Object call() {
+                throw e;
+            }});
+
+        task.run();
+        assertEquals(1, task.runCount());
+        assertEquals(0, task.setCount());
+        assertEquals(1, task.setExceptionCount());
+        try {
+            task.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(e, success.getCause());
+            tryToConfuseDoneTask(task);
+            checkCompletedAbnormally(task, success.getCause());
+        }
+    }
+
+    /**
+     * A runtime exception in task causes timed get to throw ExecutionException
+     */
+    public void testTimedGet_ExecutionException2() throws Exception {
+        final ArithmeticException e = new ArithmeticException();
+        final PublicFutureTask task = new PublicFutureTask(new Callable() {
+            public Object call() {
+                throw e;
+            }});
+
+        task.run();
+        try {
+            task.get(LONG_DELAY_MS, MILLISECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(e, success.getCause());
+            tryToConfuseDoneTask(task);
+            checkCompletedAbnormally(task, success.getCause());
+        }
+    }
+
+    /**
+     * get is interruptible
+     */
+    public void testGet_interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final FutureTask task = new FutureTask(new NoOpCallable());
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Thread.currentThread().interrupt();
+                try {
+                    task.get();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    task.get();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        awaitTermination(t);
+        checkNotDone(task);
+    }
+
+    /**
+     * timed get is interruptible
+     */
+    public void testTimedGet_interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final FutureTask task = new FutureTask(new NoOpCallable());
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                Thread.currentThread().interrupt();
+                try {
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    task.get(2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        awaitTermination(t);
+        checkNotDone(task);
+    }
+
+    /**
+     * A timed out timed get throws TimeoutException
+     */
+    public void testGet_TimeoutException() throws Exception {
+        FutureTask task = new FutureTask(new NoOpCallable());
+        long startTime = System.nanoTime();
+        try {
+            task.get(timeoutMillis(), MILLISECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * timed get with null TimeUnit throws NullPointerException
+     */
+    public void testGet_NullTimeUnit() throws Exception {
+        FutureTask task = new FutureTask(new NoOpCallable());
+        long[] timeouts = { Long.MIN_VALUE, 0L, Long.MAX_VALUE };
+
+        for (long timeout : timeouts) {
+            try {
+                task.get(timeout, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+
+        task.run();
+
+        for (long timeout : timeouts) {
+            try {
+                task.get(timeout, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed get with most negative timeout works correctly (i.e. no
+     * underflow bug)
+     */
+    public void testGet_NegativeInfinityTimeout() throws Exception {
+        final ExecutorService pool = Executors.newFixedThreadPool(10);
+        final Runnable nop = new Runnable() { public void run() {}};
+        final FutureTask<Void> task = new FutureTask<>(nop, null);
+        final List<Future<?>> futures = new ArrayList<>();
+        Runnable r = new Runnable() { public void run() {
+            for (long timeout : new long[] { 0L, -1L, Long.MIN_VALUE }) {
+                try {
+                    task.get(timeout, NANOSECONDS);
+                    shouldThrow();
+                } catch (TimeoutException success) {
+                } catch (Throwable fail) {threadUnexpectedException(fail);}}}};
+        for (int i = 0; i < 10; i++)
+            futures.add(pool.submit(r));
+        try {
+            joinPool(pool);
+            for (Future<?> future : futures)
+                checkCompletedNormally(future, null);
+        } finally {
+            task.run();         // last resort to help terminate
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/JSR166TestCase.java b/ojluni/src/test/java/util/concurrent/tck/JSR166TestCase.java
new file mode 100644
index 0000000..575a8f3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/JSR166TestCase.java
@@ -0,0 +1,2037 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+/*
+ * @test
+ * @summary JSR-166 tck tests (conformance testing mode)
+ * @build *
+ * @modules java.management
+ * @run junit/othervm/timeout=1000 JSR166TestCase
+ */
+
+/*
+ * @test
+ * @summary JSR-166 tck tests (whitebox tests allowed)
+ * @build *
+ * @modules java.base/java.util.concurrent:open
+ *          java.base/java.lang:open
+ *          java.management
+ * @run junit/othervm/timeout=1000
+ *      -Djsr166.testImplementationDetails=true
+ *      JSR166TestCase
+ * @run junit/othervm/timeout=1000
+ *      -Djsr166.testImplementationDetails=true
+ *      -Djava.util.concurrent.ForkJoinPool.common.parallelism=0
+ *      JSR166TestCase
+ * @run junit/othervm/timeout=1000
+ *      -Djsr166.testImplementationDetails=true
+ *      -Djava.util.concurrent.ForkJoinPool.common.parallelism=1
+ *      -Djava.util.secureRandomSeed=true
+ *      JSR166TestCase
+ * @run junit/othervm/timeout=1000/policy=tck.policy
+ *      -Djsr166.testImplementationDetails=true
+ *      JSR166TestCase
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+//import java.lang.management.ManagementFactory;
+//import java.lang.management.ThreadInfo;
+//import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.PropertyPermission;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.Future;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+/**
+ * Base class for JSR166 Junit TCK tests.  Defines some constants,
+ * utility methods and classes, as well as a simple framework for
+ * helping to make sure that assertions failing in generated threads
+ * cause the associated test that generated them to itself fail (which
+ * JUnit does not otherwise arrange).  The rules for creating such
+ * tests are:
+ *
+ * <ol>
+ *
+ * <li>All assertions in code running in generated threads must use
+ * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
+ * #threadAssertEquals}, or {@link #threadAssertNull}, (not
+ * {@code fail}, {@code assertTrue}, etc.) It is OK (but not
+ * particularly recommended) for other code to use these forms too.
+ * Only the most typically used JUnit assertion methods are defined
+ * this way, but enough to live with.
+ *
+ * <li>If you override {@link #setUp} or {@link #tearDown}, make sure
+ * to invoke {@code super.setUp} and {@code super.tearDown} within
+ * them. These methods are used to clear and check for thread
+ * assertion failures.
+ *
+ * <li>All delays and timeouts must use one of the constants {@code
+ * SHORT_DELAY_MS}, {@code SMALL_DELAY_MS}, {@code MEDIUM_DELAY_MS},
+ * {@code LONG_DELAY_MS}. The idea here is that a SHORT is always
+ * discriminable from zero time, and always allows enough time for the
+ * small amounts of computation (creating a thread, calling a few
+ * methods, etc) needed to reach a timeout point. Similarly, a SMALL
+ * is always discriminable as larger than SHORT and smaller than
+ * MEDIUM.  And so on. These constants are set to conservative values,
+ * but even so, if there is ever any doubt, they can all be increased
+ * in one spot to rerun tests on slower platforms.
+ *
+ * <li>All threads generated must be joined inside each test case
+ * method (or {@code fail} to do so) before returning from the
+ * method. The {@code joinPool} method can be used to do this when
+ * using Executors.
+ *
+ * </ol>
+ *
+ * <p><b>Other notes</b>
+ * <ul>
+ *
+ * <li>Usually, there is one testcase method per JSR166 method
+ * covering "normal" operation, and then as many exception-testing
+ * methods as there are exceptions the method can throw. Sometimes
+ * there are multiple tests per JSR166 method when the different
+ * "normal" behaviors differ significantly. And sometimes testcases
+ * cover multiple methods when they cannot be tested in isolation.
+ *
+ * <li>The documentation style for testcases is to provide as javadoc
+ * a simple sentence or two describing the property that the testcase
+ * method purports to test. The javadocs do not say anything about how
+ * the property is tested. To find out, read the code.
+ *
+ * <li>These tests are "conformance tests", and do not attempt to
+ * test throughput, latency, scalability or other performance factors
+ * (see the separate "jtreg" tests for a set intended to check these
+ * for the most central aspects of functionality.) So, most tests use
+ * the smallest sensible numbers of threads, collection sizes, etc
+ * needed to check basic conformance.
+ *
+ * <li>The test classes currently do not declare inclusion in
+ * any particular package to simplify things for people integrating
+ * them in TCK test suites.
+ *
+ * <li>As a convenience, the {@code main} of this class (JSR166TestCase)
+ * runs all JSR166 unit tests.
+ *
+ * </ul>
+ */
+public class JSR166TestCase extends TestCase {
+    private static final boolean useSecurityManager =
+        Boolean.getBoolean("jsr166.useSecurityManager");
+
+    protected static final boolean expensiveTests =
+        Boolean.getBoolean("jsr166.expensiveTests");
+
+    /**
+     * If true, also run tests that are not part of the official tck
+     * because they test unspecified implementation details.
+     */
+    protected static final boolean testImplementationDetails =
+        Boolean.getBoolean("jsr166.testImplementationDetails");
+
+    /**
+     * If true, report on stdout all "slow" tests, that is, ones that
+     * take more than profileThreshold milliseconds to execute.
+     */
+    private static final boolean profileTests =
+        Boolean.getBoolean("jsr166.profileTests");
+
+    /**
+     * The number of milliseconds that tests are permitted for
+     * execution without being reported, when profileTests is set.
+     */
+    private static final long profileThreshold =
+        Long.getLong("jsr166.profileThreshold", 100);
+
+    /**
+     * The number of repetitions per test (for tickling rare bugs).
+     */
+    private static final int runsPerTest =
+        Integer.getInteger("jsr166.runsPerTest", 1);
+
+    /**
+     * The number of repetitions of the test suite (for finding leaks?).
+     */
+    private static final int suiteRuns =
+        Integer.getInteger("jsr166.suiteRuns", 1);
+
+    /**
+     * Returns the value of the system property, or NaN if not defined.
+     */
+    private static float systemPropertyValue(String name) {
+        String floatString = System.getProperty(name);
+        if (floatString == null)
+            return Float.NaN;
+        try {
+            return Float.parseFloat(floatString);
+        } catch (NumberFormatException ex) {
+            throw new IllegalArgumentException(
+                String.format("Bad float value in system property %s=%s",
+                              name, floatString));
+        }
+    }
+
+    /**
+     * The scaling factor to apply to standard delays used in tests.
+     * May be initialized from any of:
+     * - the "jsr166.delay.factor" system property
+     * - the "test.timeout.factor" system property (as used by jtreg)
+     *   See: http://openjdk.java.net/jtreg/tag-spec.html
+     * - hard-coded fuzz factor when using a known slowpoke VM
+     */
+    private static final float delayFactor = delayFactor();
+
+    private static float delayFactor() {
+        float x;
+        if (!Float.isNaN(x = systemPropertyValue("jsr166.delay.factor")))
+            return x;
+        if (!Float.isNaN(x = systemPropertyValue("test.timeout.factor")))
+            return x;
+        String prop = System.getProperty("java.vm.version");
+        if (prop != null && prop.matches(".*debug.*"))
+            return 4.0f; // How much slower is fastdebug than product?!
+        return 1.0f;
+    }
+
+    public JSR166TestCase() { super(); }
+    public JSR166TestCase(String name) { super(name); }
+
+    /**
+     * A filter for tests to run, matching strings of the form
+     * methodName(className), e.g. "testInvokeAll5(ForkJoinPoolTest)"
+     * Usefully combined with jsr166.runsPerTest.
+     */
+    private static final Pattern methodFilter = methodFilter();
+
+    private static Pattern methodFilter() {
+        String regex = System.getProperty("jsr166.methodFilter");
+        return (regex == null) ? null : Pattern.compile(regex);
+    }
+
+    // Instrumentation to debug very rare, but very annoying hung test runs.
+    static volatile TestCase currentTestCase;
+    // static volatile int currentRun = 0;
+    static {
+        Runnable checkForWedgedTest = new Runnable() { public void run() {
+            // Avoid spurious reports with enormous runsPerTest.
+            // A single test case run should never take more than 1 second.
+            // But let's cap it at the high end too ...
+            final int timeoutMinutes =
+                Math.min(15, Math.max(runsPerTest / 60, 1));
+            for (TestCase lastTestCase = currentTestCase;;) {
+                try { MINUTES.sleep(timeoutMinutes); }
+                catch (InterruptedException unexpected) { break; }
+                if (lastTestCase == currentTestCase) {
+                    System.err.printf(
+                        "Looks like we're stuck running test: %s%n",
+                        lastTestCase);
+//                     System.err.printf(
+//                         "Looks like we're stuck running test: %s (%d/%d)%n",
+//                         lastTestCase, currentRun, runsPerTest);
+//                     System.err.println("availableProcessors=" +
+//                         Runtime.getRuntime().availableProcessors());
+//                     System.err.printf("cpu model = %s%n", cpuModel());
+                    dumpTestThreads();
+                    // one stack dump is probably enough; more would be spam
+                    break;
+                }
+                lastTestCase = currentTestCase;
+            }}};
+        Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest");
+        thread.setDaemon(true);
+        thread.start();
+    }
+
+//     public static String cpuModel() {
+//         try {
+//             Matcher matcher = Pattern.compile("model name\\s*: (.*)")
+//                 .matcher(new String(
+//                      Files.readAllBytes(Paths.get("/proc/cpuinfo")), "UTF-8"));
+//             matcher.find();
+//             return matcher.group(1);
+//         } catch (Exception ex) { return null; }
+//     }
+
+    public void runBare() throws Throwable {
+        currentTestCase = this;
+        if (methodFilter == null
+            || methodFilter.matcher(toString()).find())
+            super.runBare();
+    }
+
+    protected void runTest() throws Throwable {
+        for (int i = 0; i < runsPerTest; i++) {
+            // currentRun = i;
+            if (profileTests)
+                runTestProfiled();
+            else
+                super.runTest();
+        }
+    }
+
+    protected void runTestProfiled() throws Throwable {
+        for (int i = 0; i < 2; i++) {
+            long startTime = System.nanoTime();
+            super.runTest();
+            long elapsedMillis = millisElapsedSince(startTime);
+            if (elapsedMillis < profileThreshold)
+                break;
+            // Never report first run of any test; treat it as a
+            // warmup run, notably to trigger all needed classloading,
+            if (i > 0)
+                System.out.printf("%n%s: %d%n", toString(), elapsedMillis);
+        }
+    }
+
+    /**
+     * Runs all JSR166 unit tests using junit.textui.TestRunner.
+     */
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    static class PithyResultPrinter extends junit.textui.ResultPrinter {
+        PithyResultPrinter(java.io.PrintStream writer) { super(writer); }
+        long runTime;
+        public void startTest(Test test) {}
+        protected void printHeader(long runTime) {
+            this.runTime = runTime; // defer printing for later
+        }
+        protected void printFooter(TestResult result) {
+            if (result.wasSuccessful()) {
+                getWriter().println("OK (" + result.runCount() + " tests)"
+                    + "  Time: " + elapsedTimeAsString(runTime));
+            } else {
+                getWriter().println("Time: " + elapsedTimeAsString(runTime));
+                super.printFooter(result);
+            }
+        }
+    }
+
+    /**
+     * Returns a TestRunner that doesn't bother with unnecessary
+     * fluff, like printing a "." for each test case.
+     */
+    static junit.textui.TestRunner newPithyTestRunner() {
+        junit.textui.TestRunner runner = new junit.textui.TestRunner();
+        runner.setPrinter(new PithyResultPrinter(System.out));
+        return runner;
+    }
+
+    /**
+     * Runs all unit tests in the given test suite.
+     * Actual behavior influenced by jsr166.* system properties.
+     */
+    static void main(Test suite, String[] args) {
+        if (useSecurityManager) {
+            System.err.println("Setting a permissive security manager");
+            Policy.setPolicy(permissivePolicy());
+            System.setSecurityManager(new SecurityManager());
+        }
+        for (int i = 0; i < suiteRuns; i++) {
+            TestResult result = newPithyTestRunner().doRun(suite);
+            if (!result.wasSuccessful())
+                System.exit(1);
+            System.gc();
+            System.runFinalization();
+        }
+    }
+
+    public static TestSuite newTestSuite(Object... suiteOrClasses) {
+        TestSuite suite = new TestSuite();
+        for (Object suiteOrClass : suiteOrClasses) {
+            if (suiteOrClass instanceof TestSuite)
+                suite.addTest((TestSuite) suiteOrClass);
+            else if (suiteOrClass instanceof Class)
+                suite.addTest(new TestSuite((Class<?>) suiteOrClass));
+            else
+                throw new ClassCastException("not a test suite or class");
+        }
+        return suite;
+    }
+
+    public static void addNamedTestClasses(TestSuite suite,
+                                           String... testClassNames) {
+        for (String testClassName : testClassNames) {
+            try {
+                Class<?> testClass = Class.forName(testClassName);
+                Method m = testClass.getDeclaredMethod("suite",
+                                                       new Class<?>[0]);
+                suite.addTest(newTestSuite((Test)m.invoke(null)));
+            } catch (Exception e) {
+                throw new Error("Missing test class", e);
+            }
+        }
+    }
+
+    public static final double JAVA_CLASS_VERSION;
+    public static final String JAVA_SPECIFICATION_VERSION;
+    static {
+        try {
+            JAVA_CLASS_VERSION = java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction<Double>() {
+                public Double run() {
+                    return Double.valueOf(System.getProperty("java.class.version"));}});
+            JAVA_SPECIFICATION_VERSION = java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction<String>() {
+                public String run() {
+                    return System.getProperty("java.specification.version");}});
+        } catch (Throwable t) {
+            throw new Error(t);
+        }
+    }
+
+    public static boolean atLeastJava6() { return JAVA_CLASS_VERSION >= 50.0; }
+    public static boolean atLeastJava7() { return JAVA_CLASS_VERSION >= 51.0; }
+    public static boolean atLeastJava8() { return JAVA_CLASS_VERSION >= 52.0; }
+    public static boolean atLeastJava9() {
+        return JAVA_CLASS_VERSION >= 53.0
+            // As of 2015-09, java9 still uses 52.0 class file version
+            || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?(9|[0-9][0-9])$");
+    }
+    public static boolean atLeastJava10() {
+        return JAVA_CLASS_VERSION >= 54.0
+            || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?[0-9][0-9]$");
+    }
+
+    /**
+     * Collects all JSR166 unit tests as one suite.
+     */
+    public static Test suite() {
+        // Java7+ test classes
+        TestSuite suite = newTestSuite(
+            ForkJoinPoolTest.suite(),
+            ForkJoinTaskTest.suite(),
+            RecursiveActionTest.suite(),
+            RecursiveTaskTest.suite(),
+            LinkedTransferQueueTest.suite(),
+            PhaserTest.suite(),
+            ThreadLocalRandomTest.suite(),
+            AbstractExecutorServiceTest.suite(),
+            AbstractQueueTest.suite(),
+            AbstractQueuedSynchronizerTest.suite(),
+            AbstractQueuedLongSynchronizerTest.suite(),
+            ArrayBlockingQueueTest.suite(),
+            ArrayDequeTest.suite(),
+            ArrayListTest.suite(),
+            AtomicBooleanTest.suite(),
+            AtomicIntegerArrayTest.suite(),
+            AtomicIntegerFieldUpdaterTest.suite(),
+            AtomicIntegerTest.suite(),
+            AtomicLongArrayTest.suite(),
+            AtomicLongFieldUpdaterTest.suite(),
+            AtomicLongTest.suite(),
+            AtomicMarkableReferenceTest.suite(),
+            AtomicReferenceArrayTest.suite(),
+            AtomicReferenceFieldUpdaterTest.suite(),
+            AtomicReferenceTest.suite(),
+            AtomicStampedReferenceTest.suite(),
+            ConcurrentHashMapTest.suite(),
+            ConcurrentLinkedDequeTest.suite(),
+            ConcurrentLinkedQueueTest.suite(),
+            ConcurrentSkipListMapTest.suite(),
+            ConcurrentSkipListSubMapTest.suite(),
+            ConcurrentSkipListSetTest.suite(),
+            ConcurrentSkipListSubSetTest.suite(),
+            CopyOnWriteArrayListTest.suite(),
+            CopyOnWriteArraySetTest.suite(),
+            CountDownLatchTest.suite(),
+            CountedCompleterTest.suite(),
+            CyclicBarrierTest.suite(),
+            DelayQueueTest.suite(),
+            EntryTest.suite(),
+            ExchangerTest.suite(),
+            ExecutorsTest.suite(),
+            ExecutorCompletionServiceTest.suite(),
+            FutureTaskTest.suite(),
+            LinkedBlockingDequeTest.suite(),
+            LinkedBlockingQueueTest.suite(),
+            LinkedListTest.suite(),
+            LockSupportTest.suite(),
+            PriorityBlockingQueueTest.suite(),
+            PriorityQueueTest.suite(),
+            ReentrantLockTest.suite(),
+            ReentrantReadWriteLockTest.suite(),
+            ScheduledExecutorTest.suite(),
+            ScheduledExecutorSubclassTest.suite(),
+            SemaphoreTest.suite(),
+            SynchronousQueueTest.suite(),
+            SystemTest.suite(),
+            ThreadLocalTest.suite(),
+            ThreadPoolExecutorTest.suite(),
+            ThreadPoolExecutorSubclassTest.suite(),
+            ThreadTest.suite(),
+            TimeUnitTest.suite(),
+            TreeMapTest.suite(),
+            TreeSetTest.suite(),
+            TreeSubMapTest.suite(),
+            TreeSubSetTest.suite(),
+            VectorTest.suite());
+
+        // Java8+ test classes
+        if (atLeastJava8()) {
+            String[] java8TestClassNames = {
+                "ArrayDeque8Test",
+                "Atomic8Test",
+                "CompletableFutureTest",
+                "ConcurrentHashMap8Test",
+                "CountedCompleter8Test",
+                "DoubleAccumulatorTest",
+                "DoubleAdderTest",
+                "ForkJoinPool8Test",
+                "ForkJoinTask8Test",
+                "LinkedBlockingDeque8Test",
+                "LinkedBlockingQueue8Test",
+                "LongAccumulatorTest",
+                "LongAdderTest",
+                "SplittableRandomTest",
+                "StampedLockTest",
+                "SubmissionPublisherTest",
+                "ThreadLocalRandom8Test",
+                "TimeUnit8Test",
+            };
+            addNamedTestClasses(suite, java8TestClassNames);
+        }
+
+        // Java9+ test classes
+        if (atLeastJava9()) {
+            String[] java9TestClassNames = {
+                "AtomicBoolean9Test",
+                "AtomicInteger9Test",
+                "AtomicIntegerArray9Test",
+                "AtomicLong9Test",
+                "AtomicLongArray9Test",
+                "AtomicReference9Test",
+                "AtomicReferenceArray9Test",
+                "ExecutorCompletionService9Test",
+                "ForkJoinPool9Test",
+            };
+            addNamedTestClasses(suite, java9TestClassNames);
+        }
+
+        return suite;
+    }
+
+    /** Returns list of junit-style test method names in given class. */
+    public static ArrayList<String> testMethodNames(Class<?> testClass) {
+        Method[] methods = testClass.getDeclaredMethods();
+        ArrayList<String> names = new ArrayList<>(methods.length);
+        for (Method method : methods) {
+            if (method.getName().startsWith("test")
+                && Modifier.isPublic(method.getModifiers())
+                // method.getParameterCount() requires jdk8+
+                && method.getParameterTypes().length == 0) {
+                names.add(method.getName());
+            }
+        }
+        return names;
+    }
+
+    /**
+     * Returns junit-style testSuite for the given test class, but
+     * parameterized by passing extra data to each test.
+     */
+    public static <ExtraData> Test parameterizedTestSuite
+        (Class<? extends JSR166TestCase> testClass,
+         Class<ExtraData> dataClass,
+         ExtraData data) {
+        try {
+            TestSuite suite = new TestSuite();
+            Constructor c =
+                testClass.getDeclaredConstructor(dataClass, String.class);
+            for (String methodName : testMethodNames(testClass))
+                suite.addTest((Test) c.newInstance(data, methodName));
+            return suite;
+        } catch (Exception e) {
+            throw new Error(e);
+        }
+    }
+
+    /**
+     * Returns junit-style testSuite for the jdk8 extension of the
+     * given test class, but parameterized by passing extra data to
+     * each test.  Uses reflection to allow compilation in jdk7.
+     */
+    public static <ExtraData> Test jdk8ParameterizedTestSuite
+        (Class<? extends JSR166TestCase> testClass,
+         Class<ExtraData> dataClass,
+         ExtraData data) {
+        if (atLeastJava8()) {
+            String name = testClass.getName();
+            String name8 = name.replaceAll("Test$", "8Test");
+            if (name.equals(name8)) throw new Error(name);
+            try {
+                return (Test)
+                    Class.forName(name8)
+                    .getMethod("testSuite", new Class[] { dataClass })
+                    .invoke(null, data);
+            } catch (Exception e) {
+                throw new Error(e);
+            }
+        } else {
+            return new TestSuite();
+        }
+    }
+
+    // Delays for timing-dependent tests, in milliseconds.
+
+    public static long SHORT_DELAY_MS;
+    public static long SMALL_DELAY_MS;
+    public static long MEDIUM_DELAY_MS;
+    public static long LONG_DELAY_MS;
+
+    /**
+     * Returns the shortest timed delay. This can be scaled up for
+     * slow machines using the jsr166.delay.factor system property,
+     * or via jtreg's -timeoutFactor: flag.
+     * http://openjdk.java.net/jtreg/command-help.html
+     */
+    protected long getShortDelay() {
+        return (long) (50 * delayFactor);
+    }
+
+    /**
+     * Sets delays as multiples of SHORT_DELAY.
+     */
+    protected void setDelays() {
+        SHORT_DELAY_MS = getShortDelay();
+        SMALL_DELAY_MS  = SHORT_DELAY_MS * 5;
+        MEDIUM_DELAY_MS = SHORT_DELAY_MS * 10;
+        LONG_DELAY_MS   = SHORT_DELAY_MS * 200;
+    }
+
+    /**
+     * Returns a timeout in milliseconds to be used in tests that
+     * verify that operations block or time out.
+     */
+    long timeoutMillis() {
+        return SHORT_DELAY_MS / 4;
+    }
+
+    /**
+     * Returns a new Date instance representing a time at least
+     * delayMillis milliseconds in the future.
+     */
+    Date delayedDate(long delayMillis) {
+        // Add 1 because currentTimeMillis is known to round into the past.
+        return new Date(System.currentTimeMillis() + delayMillis + 1);
+    }
+
+    /**
+     * The first exception encountered if any threadAssertXXX method fails.
+     */
+    private final AtomicReference<Throwable> threadFailure
+        = new AtomicReference<>(null);
+
+    /**
+     * Records an exception so that it can be rethrown later in the test
+     * harness thread, triggering a test case failure.  Only the first
+     * failure is recorded; subsequent calls to this method from within
+     * the same test have no effect.
+     */
+    public void threadRecordFailure(Throwable t) {
+        System.err.println(t);
+        dumpTestThreads();
+        threadFailure.compareAndSet(null, t);
+    }
+
+    public void setUp() {
+        setDelays();
+    }
+
+    void tearDownFail(String format, Object... args) {
+        String msg = toString() + ": " + String.format(format, args);
+        System.err.println(msg);
+        dumpTestThreads();
+        throw new AssertionFailedError(msg);
+    }
+
+    /**
+     * Extra checks that get done for all test cases.
+     *
+     * Triggers test case failure if any thread assertions have failed,
+     * by rethrowing, in the test harness thread, any exception recorded
+     * earlier by threadRecordFailure.
+     *
+     * Triggers test case failure if interrupt status is set in the main thread.
+     */
+    public void tearDown() throws Exception {
+        Throwable t = threadFailure.getAndSet(null);
+        if (t != null) {
+            if (t instanceof Error)
+                throw (Error) t;
+            else if (t instanceof RuntimeException)
+                throw (RuntimeException) t;
+            else if (t instanceof Exception)
+                throw (Exception) t;
+            else {
+                AssertionFailedError afe =
+                    new AssertionFailedError(t.toString());
+                afe.initCause(t);
+                throw afe;
+            }
+        }
+
+        if (Thread.interrupted())
+            tearDownFail("interrupt status set in main thread");
+
+        checkForkJoinPoolThreadLeaks();
+    }
+
+    /**
+     * Finds missing PoolCleaners
+     */
+    void checkForkJoinPoolThreadLeaks() throws InterruptedException {
+        Thread[] survivors = new Thread[7];
+        int count = Thread.enumerate(survivors);
+        for (int i = 0; i < count; i++) {
+            Thread thread = survivors[i];
+            String name = thread.getName();
+            if (name.startsWith("ForkJoinPool-")) {
+                // give thread some time to terminate
+                thread.join(LONG_DELAY_MS);
+                if (thread.isAlive())
+                    tearDownFail("Found leaked ForkJoinPool thread thread=%s",
+                                 thread);
+            }
+        }
+
+        if (!ForkJoinPool.commonPool()
+            .awaitQuiescence(LONG_DELAY_MS, MILLISECONDS))
+            tearDownFail("ForkJoin common pool thread stuck");
+    }
+
+    /**
+     * Just like fail(reason), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadFail(String reason) {
+        try {
+            fail(reason);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertTrue(b), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertTrue(boolean b) {
+        try {
+            assertTrue(b);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertFalse(b), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertFalse(boolean b) {
+        try {
+            assertFalse(b);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertNull(x), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertNull(Object x) {
+        try {
+            assertNull(x);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertEquals(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertEquals(long x, long y) {
+        try {
+            assertEquals(x, y);
+        } catch (AssertionFailedError t) {
+            threadRecordFailure(t);
+            throw t;
+        }
+    }
+
+    /**
+     * Just like assertEquals(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertEquals(Object x, Object y) {
+        try {
+            assertEquals(x, y);
+        } catch (AssertionFailedError fail) {
+            threadRecordFailure(fail);
+            throw fail;
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+        }
+    }
+
+    /**
+     * Just like assertSame(x, y), but additionally recording (using
+     * threadRecordFailure) any AssertionFailedError thrown, so that
+     * the current testcase will fail.
+     */
+    public void threadAssertSame(Object x, Object y) {
+        try {
+            assertSame(x, y);
+        } catch (AssertionFailedError fail) {
+            threadRecordFailure(fail);
+            throw fail;
+        }
+    }
+
+    /**
+     * Calls threadFail with message "should throw exception".
+     */
+    public void threadShouldThrow() {
+        threadFail("should throw exception");
+    }
+
+    /**
+     * Calls threadFail with message "should throw" + exceptionName.
+     */
+    public void threadShouldThrow(String exceptionName) {
+        threadFail("should throw " + exceptionName);
+    }
+
+    /**
+     * Records the given exception using {@link #threadRecordFailure},
+     * then rethrows the exception, wrapping it in an
+     * AssertionFailedError if necessary.
+     */
+    public void threadUnexpectedException(Throwable t) {
+        threadRecordFailure(t);
+        t.printStackTrace();
+        if (t instanceof RuntimeException)
+            throw (RuntimeException) t;
+        else if (t instanceof Error)
+            throw (Error) t;
+        else {
+            AssertionFailedError afe =
+                new AssertionFailedError("unexpected exception: " + t);
+            afe.initCause(t);
+            throw afe;
+        }
+    }
+
+    /**
+     * Delays, via Thread.sleep, for the given millisecond delay, but
+     * if the sleep is shorter than specified, may re-sleep or yield
+     * until time elapses.  Ensures that the given time, as measured
+     * by System.nanoTime(), has elapsed.
+     */
+    static void delay(long millis) throws InterruptedException {
+        long nanos = millis * (1000 * 1000);
+        final long wakeupTime = System.nanoTime() + nanos;
+        do {
+            if (millis > 0L)
+                Thread.sleep(millis);
+            else // too short to sleep
+                Thread.yield();
+            nanos = wakeupTime - System.nanoTime();
+            millis = nanos / (1000 * 1000);
+        } while (nanos >= 0L);
+    }
+
+    /**
+     * Allows use of try-with-resources with per-test thread pools.
+     */
+    class PoolCleaner implements AutoCloseable {
+        private final ExecutorService pool;
+        public PoolCleaner(ExecutorService pool) { this.pool = pool; }
+        public void close() { joinPool(pool); }
+    }
+
+    /**
+     * An extension of PoolCleaner that has an action to release the pool.
+     */
+    class PoolCleanerWithReleaser extends PoolCleaner {
+        private final Runnable releaser;
+        public PoolCleanerWithReleaser(ExecutorService pool, Runnable releaser) {
+            super(pool);
+            this.releaser = releaser;
+        }
+        public void close() {
+            try {
+                releaser.run();
+            } finally {
+                super.close();
+            }
+        }
+    }
+
+    PoolCleaner cleaner(ExecutorService pool) {
+        return new PoolCleaner(pool);
+    }
+
+    PoolCleaner cleaner(ExecutorService pool, Runnable releaser) {
+        return new PoolCleanerWithReleaser(pool, releaser);
+    }
+
+    PoolCleaner cleaner(ExecutorService pool, CountDownLatch latch) {
+        return new PoolCleanerWithReleaser(pool, releaser(latch));
+    }
+
+    Runnable releaser(final CountDownLatch latch) {
+        return new Runnable() { public void run() {
+            do { latch.countDown(); }
+            while (latch.getCount() > 0);
+        }};
+    }
+
+    PoolCleaner cleaner(ExecutorService pool, AtomicBoolean flag) {
+        return new PoolCleanerWithReleaser(pool, releaser(flag));
+    }
+
+    Runnable releaser(final AtomicBoolean flag) {
+        return new Runnable() { public void run() { flag.set(true); }};
+    }
+
+    /**
+     * Waits out termination of a thread pool or fails doing so.
+     */
+    void joinPool(ExecutorService pool) {
+        try {
+            pool.shutdown();
+            if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) {
+                try {
+                    threadFail("ExecutorService " + pool +
+                               " did not terminate in a timely manner");
+                } finally {
+                    // last resort, for the benefit of subsequent tests
+                    pool.shutdownNow();
+                    pool.awaitTermination(MEDIUM_DELAY_MS, MILLISECONDS);
+                }
+            }
+        } catch (SecurityException ok) {
+            // Allowed in case test doesn't have privs
+        } catch (InterruptedException fail) {
+            threadFail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Like Runnable, but with the freedom to throw anything.
+     * junit folks had the same idea:
+     * http://junit.org/junit5/docs/snapshot/api/org/junit/gen5/api/Executable.html
+     */
+    interface Action { public void run() throws Throwable; }
+
+    /**
+     * Runs all the given actions in parallel, failing if any fail.
+     * Useful for running multiple variants of tests that are
+     * necessarily individually slow because they must block.
+     */
+    void testInParallel(Action ... actions) {
+        ExecutorService pool = Executors.newCachedThreadPool();
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            ArrayList<Future<?>> futures = new ArrayList<>(actions.length);
+            for (final Action action : actions)
+                futures.add(pool.submit(new CheckedRunnable() {
+                    public void realRun() throws Throwable { action.run();}}));
+            for (Future<?> future : futures)
+                try {
+                    assertNull(future.get(LONG_DELAY_MS, MILLISECONDS));
+                } catch (ExecutionException ex) {
+                    threadUnexpectedException(ex.getCause());
+                } catch (Exception ex) {
+                    threadUnexpectedException(ex);
+                }
+        }
+    }
+
+    /**
+     * A debugging tool to print stack traces of most threads, as jstack does.
+     * Uninteresting threads are filtered out.
+     */
+    static void dumpTestThreads() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            try {
+                System.setSecurityManager(null);
+            } catch (SecurityException giveUp) {
+                return;
+            }
+        }
+
+        // Android-removed: Android doesn't have ManagementFactory.
+        /*
+        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+        System.err.println("------ stacktrace dump start ------");
+        for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
+            final String name = info.getThreadName();
+            String lockName;
+            if ("Signal Dispatcher".equals(name))
+                continue;
+            if ("Reference Handler".equals(name)
+                && (lockName = info.getLockName()) != null
+                && lockName.startsWith("java.lang.ref.Reference$Lock"))
+                continue;
+            if ("Finalizer".equals(name)
+                && (lockName = info.getLockName()) != null
+                && lockName.startsWith("java.lang.ref.ReferenceQueue$Lock"))
+                continue;
+            if ("checkForWedgedTest".equals(name))
+                continue;
+            System.err.print(info);
+        }
+        System.err.println("------ stacktrace dump end ------");
+        */
+
+        if (sm != null) System.setSecurityManager(sm);
+    }
+
+    /**
+     * Checks that thread does not terminate within the default
+     * millisecond delay of {@code timeoutMillis()}.
+     */
+    void assertThreadStaysAlive(Thread thread) {
+        assertThreadStaysAlive(thread, timeoutMillis());
+    }
+
+    /**
+     * Checks that thread does not terminate within the given millisecond delay.
+     */
+    void assertThreadStaysAlive(Thread thread, long millis) {
+        try {
+            // No need to optimize the failing case via Thread.join.
+            delay(millis);
+            assertTrue(thread.isAlive());
+        } catch (InterruptedException fail) {
+            threadFail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Checks that the threads do not terminate within the default
+     * millisecond delay of {@code timeoutMillis()}.
+     */
+    void assertThreadsStayAlive(Thread... threads) {
+        assertThreadsStayAlive(timeoutMillis(), threads);
+    }
+
+    /**
+     * Checks that the threads do not terminate within the given millisecond delay.
+     */
+    void assertThreadsStayAlive(long millis, Thread... threads) {
+        try {
+            // No need to optimize the failing case via Thread.join.
+            delay(millis);
+            for (Thread thread : threads)
+                assertTrue(thread.isAlive());
+        } catch (InterruptedException fail) {
+            threadFail("Unexpected InterruptedException");
+        }
+    }
+
+    /**
+     * Checks that future.get times out, with the default timeout of
+     * {@code timeoutMillis()}.
+     */
+    void assertFutureTimesOut(Future future) {
+        assertFutureTimesOut(future, timeoutMillis());
+    }
+
+    /**
+     * Checks that future.get times out, with the given millisecond timeout.
+     */
+    void assertFutureTimesOut(Future future, long timeoutMillis) {
+        long startTime = System.nanoTime();
+        try {
+            future.get(timeoutMillis, MILLISECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Exception fail) {
+            threadUnexpectedException(fail);
+        } finally { future.cancel(true); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+    }
+
+    /**
+     * Fails with message "should throw exception".
+     */
+    public void shouldThrow() {
+        fail("Should throw exception");
+    }
+
+    /**
+     * Fails with message "should throw " + exceptionName.
+     */
+    public void shouldThrow(String exceptionName) {
+        fail("Should throw " + exceptionName);
+    }
+
+    /**
+     * The number of elements to place in collections, arrays, etc.
+     */
+    public static final int SIZE = 20;
+
+    // Some convenient Integer constants
+
+    public static final Integer zero  = new Integer(0);
+    public static final Integer one   = new Integer(1);
+    public static final Integer two   = new Integer(2);
+    public static final Integer three = new Integer(3);
+    public static final Integer four  = new Integer(4);
+    public static final Integer five  = new Integer(5);
+    public static final Integer six   = new Integer(6);
+    public static final Integer seven = new Integer(7);
+    public static final Integer eight = new Integer(8);
+    public static final Integer nine  = new Integer(9);
+    public static final Integer m1  = new Integer(-1);
+    public static final Integer m2  = new Integer(-2);
+    public static final Integer m3  = new Integer(-3);
+    public static final Integer m4  = new Integer(-4);
+    public static final Integer m5  = new Integer(-5);
+    public static final Integer m6  = new Integer(-6);
+    public static final Integer m10 = new Integer(-10);
+
+    /**
+     * Runs Runnable r with a security policy that permits precisely
+     * the specified permissions.  If there is no current security
+     * manager, the runnable is run twice, both with and without a
+     * security manager.  We require that any security manager permit
+     * getPolicy/setPolicy.
+     */
+    public void runWithPermissions(Runnable r, Permission... permissions) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm == null) {
+            r.run();
+        }
+        runWithSecurityManagerWithPermissions(r, permissions);
+    }
+
+    /**
+     * Runs Runnable r with a security policy that permits precisely
+     * the specified permissions.  If there is no current security
+     * manager, a temporary one is set for the duration of the
+     * Runnable.  We require that any security manager permit
+     * getPolicy/setPolicy.
+     */
+    public void runWithSecurityManagerWithPermissions(Runnable r,
+                                                      Permission... permissions) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm == null) {
+            Policy savedPolicy = Policy.getPolicy();
+            try {
+                Policy.setPolicy(permissivePolicy());
+                System.setSecurityManager(new SecurityManager());
+                runWithSecurityManagerWithPermissions(r, permissions);
+            } finally {
+                System.setSecurityManager(null);
+                Policy.setPolicy(savedPolicy);
+            }
+        } else {
+            Policy savedPolicy = Policy.getPolicy();
+            AdjustablePolicy policy = new AdjustablePolicy(permissions);
+            Policy.setPolicy(policy);
+
+            try {
+                r.run();
+            } finally {
+                policy.addPermission(new SecurityPermission("setPolicy"));
+                Policy.setPolicy(savedPolicy);
+            }
+        }
+    }
+
+    /**
+     * Runs a runnable without any permissions.
+     */
+    public void runWithoutPermissions(Runnable r) {
+        runWithPermissions(r);
+    }
+
+    /**
+     * A security policy where new permissions can be dynamically added
+     * or all cleared.
+     */
+    public static class AdjustablePolicy extends java.security.Policy {
+        Permissions perms = new Permissions();
+        AdjustablePolicy(Permission... permissions) {
+            for (Permission permission : permissions)
+                perms.add(permission);
+        }
+        void addPermission(Permission perm) { perms.add(perm); }
+        void clearPermissions() { perms = new Permissions(); }
+        public PermissionCollection getPermissions(CodeSource cs) {
+            return perms;
+        }
+        public PermissionCollection getPermissions(ProtectionDomain pd) {
+            return perms;
+        }
+        public boolean implies(ProtectionDomain pd, Permission p) {
+            return perms.implies(p);
+        }
+        public void refresh() {}
+        public String toString() {
+            List<Permission> ps = new ArrayList<>();
+            for (Enumeration<Permission> e = perms.elements(); e.hasMoreElements();)
+                ps.add(e.nextElement());
+            return "AdjustablePolicy with permissions " + ps;
+        }
+    }
+
+    /**
+     * Returns a policy containing all the permissions we ever need.
+     */
+    public static Policy permissivePolicy() {
+        return new AdjustablePolicy
+            // Permissions j.u.c. needs directly
+            (new RuntimePermission("modifyThread"),
+             new RuntimePermission("getClassLoader"),
+             new RuntimePermission("setContextClassLoader"),
+             // Permissions needed to change permissions!
+             new SecurityPermission("getPolicy"),
+             new SecurityPermission("setPolicy"),
+             new RuntimePermission("setSecurityManager"),
+             // Permissions needed by the junit test harness
+             new RuntimePermission("accessDeclaredMembers"),
+             new PropertyPermission("*", "read"),
+             new java.io.FilePermission("<<ALL FILES>>", "read"));
+    }
+
+    /**
+     * Sleeps until the given time has elapsed.
+     * Throws AssertionFailedError if interrupted.
+     */
+    static void sleep(long millis) {
+        try {
+            delay(millis);
+        } catch (InterruptedException fail) {
+            AssertionFailedError afe =
+                new AssertionFailedError("Unexpected InterruptedException");
+            afe.initCause(fail);
+            throw afe;
+        }
+    }
+
+    /**
+     * Spin-waits up to the specified number of milliseconds for the given
+     * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
+     */
+    void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) {
+        long startTime = 0L;
+        for (;;) {
+            Thread.State s = thread.getState();
+            if (s == Thread.State.BLOCKED ||
+                s == Thread.State.WAITING ||
+                s == Thread.State.TIMED_WAITING)
+                return;
+            else if (s == Thread.State.TERMINATED)
+                fail("Unexpected thread termination");
+            else if (startTime == 0L)
+                startTime = System.nanoTime();
+            else if (millisElapsedSince(startTime) > timeoutMillis) {
+                threadAssertTrue(thread.isAlive());
+                fail("timed out waiting for thread to enter wait state");
+            }
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Spin-waits up to the specified number of milliseconds for the given
+     * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+     * and additionally satisfy the given condition.
+     */
+    void waitForThreadToEnterWaitState(
+        Thread thread, long timeoutMillis, Callable<Boolean> waitingForGodot) {
+        long startTime = 0L;
+        for (;;) {
+            Thread.State s = thread.getState();
+            if (s == Thread.State.BLOCKED ||
+                s == Thread.State.WAITING ||
+                s == Thread.State.TIMED_WAITING) {
+                try {
+                    if (waitingForGodot.call())
+                        return;
+                } catch (Throwable fail) { threadUnexpectedException(fail); }
+            }
+            else if (s == Thread.State.TERMINATED)
+                fail("Unexpected thread termination");
+            else if (startTime == 0L)
+                startTime = System.nanoTime();
+            else if (millisElapsedSince(startTime) > timeoutMillis) {
+                threadAssertTrue(thread.isAlive());
+                fail("timed out waiting for thread to enter wait state");
+            }
+            Thread.yield();
+        }
+    }
+
+    /**
+     * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+     * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
+     */
+    void waitForThreadToEnterWaitState(Thread thread) {
+        waitForThreadToEnterWaitState(thread, LONG_DELAY_MS);
+    }
+
+    /**
+     * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to
+     * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING,
+     * and additionally satisfy the given condition.
+     */
+    void waitForThreadToEnterWaitState(
+        Thread thread, Callable<Boolean> waitingForGodot) {
+        waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, waitingForGodot);
+    }
+
+    /**
+     * Returns the number of milliseconds since time given by
+     * startNanoTime, which must have been previously returned from a
+     * call to {@link System#nanoTime()}.
+     */
+    static long millisElapsedSince(long startNanoTime) {
+        return NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
+    }
+
+//     void assertTerminatesPromptly(long timeoutMillis, Runnable r) {
+//         long startTime = System.nanoTime();
+//         try {
+//             r.run();
+//         } catch (Throwable fail) { threadUnexpectedException(fail); }
+//         if (millisElapsedSince(startTime) > timeoutMillis/2)
+//             throw new AssertionFailedError("did not return promptly");
+//     }
+
+//     void assertTerminatesPromptly(Runnable r) {
+//         assertTerminatesPromptly(LONG_DELAY_MS/2, r);
+//     }
+
+    /**
+     * Checks that timed f.get() returns the expected value, and does not
+     * wait for the timeout to elapse before returning.
+     */
+    <T> void checkTimedGet(Future<T> f, T expectedValue, long timeoutMillis) {
+        long startTime = System.nanoTime();
+        try {
+            assertEquals(expectedValue, f.get(timeoutMillis, MILLISECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        if (millisElapsedSince(startTime) > timeoutMillis/2)
+            throw new AssertionFailedError("timed get did not return promptly");
+    }
+
+    <T> void checkTimedGet(Future<T> f, T expectedValue) {
+        checkTimedGet(f, expectedValue, LONG_DELAY_MS);
+    }
+
+    /**
+     * Returns a new started daemon Thread running the given runnable.
+     */
+    Thread newStartedThread(Runnable runnable) {
+        Thread t = new Thread(runnable);
+        t.setDaemon(true);
+        t.start();
+        return t;
+    }
+
+    /**
+     * Waits for the specified time (in milliseconds) for the thread
+     * to terminate (using {@link Thread#join(long)}), else interrupts
+     * the thread (in the hope that it may terminate later) and fails.
+     */
+    void awaitTermination(Thread t, long timeoutMillis) {
+        try {
+            t.join(timeoutMillis);
+        } catch (InterruptedException fail) {
+            threadUnexpectedException(fail);
+        } finally {
+            if (t.getState() != Thread.State.TERMINATED) {
+                t.interrupt();
+                threadFail("timed out waiting for thread to terminate");
+            }
+        }
+    }
+
+    /**
+     * Waits for LONG_DELAY_MS milliseconds for the thread to
+     * terminate (using {@link Thread#join(long)}), else interrupts
+     * the thread (in the hope that it may terminate later) and fails.
+     */
+    void awaitTermination(Thread t) {
+        awaitTermination(t, LONG_DELAY_MS);
+    }
+
+    // Some convenient Runnable classes
+
+    public abstract class CheckedRunnable implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        public final void run() {
+            try {
+                realRun();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+        }
+    }
+
+    public abstract class RunnableShouldThrow implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        final Class<?> exceptionClass;
+
+        <T extends Throwable> RunnableShouldThrow(Class<T> exceptionClass) {
+            this.exceptionClass = exceptionClass;
+        }
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow(exceptionClass.getSimpleName());
+            } catch (Throwable t) {
+                if (! exceptionClass.isInstance(t))
+                    threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class ThreadShouldThrow extends Thread {
+        protected abstract void realRun() throws Throwable;
+
+        final Class<?> exceptionClass;
+
+        <T extends Throwable> ThreadShouldThrow(Class<T> exceptionClass) {
+            this.exceptionClass = exceptionClass;
+        }
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow(exceptionClass.getSimpleName());
+            } catch (Throwable t) {
+                if (! exceptionClass.isInstance(t))
+                    threadUnexpectedException(t);
+            }
+        }
+    }
+
+    public abstract class CheckedInterruptedRunnable implements Runnable {
+        protected abstract void realRun() throws Throwable;
+
+        public final void run() {
+            try {
+                realRun();
+                threadShouldThrow("InterruptedException");
+            } catch (InterruptedException success) {
+                threadAssertFalse(Thread.interrupted());
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+        }
+    }
+
+    public abstract class CheckedCallable<T> implements Callable<T> {
+        protected abstract T realCall() throws Throwable;
+
+        public final T call() {
+            try {
+                return realCall();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+                return null;
+            }
+        }
+    }
+
+    public abstract class CheckedInterruptedCallable<T>
+        implements Callable<T> {
+        protected abstract T realCall() throws Throwable;
+
+        public final T call() {
+            try {
+                T result = realCall();
+                threadShouldThrow("InterruptedException");
+                return result;
+            } catch (InterruptedException success) {
+                threadAssertFalse(Thread.interrupted());
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+            return null;
+        }
+    }
+
+    public static class NoOpRunnable implements Runnable {
+        public void run() {}
+    }
+
+    public static class NoOpCallable implements Callable {
+        public Object call() { return Boolean.TRUE; }
+    }
+
+    public static final String TEST_STRING = "a test string";
+
+    public static class StringTask implements Callable<String> {
+        final String value;
+        public StringTask() { this(TEST_STRING); }
+        public StringTask(String value) { this.value = value; }
+        public String call() { return value; }
+    }
+
+    public Callable<String> latchAwaitingStringTask(final CountDownLatch latch) {
+        return new CheckedCallable<String>() {
+            protected String realCall() {
+                try {
+                    latch.await();
+                } catch (InterruptedException quittingTime) {}
+                return TEST_STRING;
+            }};
+    }
+
+    public Runnable countDowner(final CountDownLatch latch) {
+        return new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                latch.countDown();
+            }};
+    }
+
+    class LatchAwaiter extends CheckedRunnable {
+        static final int NEW = 0;
+        static final int RUNNING = 1;
+        static final int DONE = 2;
+        final CountDownLatch latch;
+        int state = NEW;
+        LatchAwaiter(CountDownLatch latch) { this.latch = latch; }
+        public void realRun() throws InterruptedException {
+            state = 1;
+            await(latch);
+            state = 2;
+        }
+    }
+
+    public LatchAwaiter awaiter(CountDownLatch latch) {
+        return new LatchAwaiter(latch);
+    }
+
+    public void await(CountDownLatch latch, long timeoutMillis) {
+        try {
+            if (!latch.await(timeoutMillis, MILLISECONDS))
+                fail("timed out waiting for CountDownLatch for "
+                     + (timeoutMillis/1000) + " sec");
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+        }
+    }
+
+    public void await(CountDownLatch latch) {
+        await(latch, LONG_DELAY_MS);
+    }
+
+    public void await(Semaphore semaphore) {
+        try {
+            if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS))
+                fail("timed out waiting for Semaphore for "
+                     + (LONG_DELAY_MS/1000) + " sec");
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+        }
+    }
+
+//     /**
+//      * Spin-waits up to LONG_DELAY_MS until flag becomes true.
+//      */
+//     public void await(AtomicBoolean flag) {
+//         await(flag, LONG_DELAY_MS);
+//     }
+
+//     /**
+//      * Spin-waits up to the specified timeout until flag becomes true.
+//      */
+//     public void await(AtomicBoolean flag, long timeoutMillis) {
+//         long startTime = System.nanoTime();
+//         while (!flag.get()) {
+//             if (millisElapsedSince(startTime) > timeoutMillis)
+//                 throw new AssertionFailedError("timed out");
+//             Thread.yield();
+//         }
+//     }
+
+    public static class NPETask implements Callable<String> {
+        public String call() { throw new NullPointerException(); }
+    }
+
+    public static class CallableOne implements Callable<Integer> {
+        public Integer call() { return one; }
+    }
+
+    public class ShortRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(SHORT_DELAY_MS);
+        }
+    }
+
+    public class ShortInterruptedRunnable extends CheckedInterruptedRunnable {
+        protected void realRun() throws InterruptedException {
+            delay(SHORT_DELAY_MS);
+        }
+    }
+
+    public class SmallRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(SMALL_DELAY_MS);
+        }
+    }
+
+    public class SmallPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(SMALL_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public class SmallCallable extends CheckedCallable {
+        protected Object realCall() throws InterruptedException {
+            delay(SMALL_DELAY_MS);
+            return Boolean.TRUE;
+        }
+    }
+
+    public class MediumRunnable extends CheckedRunnable {
+        protected void realRun() throws Throwable {
+            delay(MEDIUM_DELAY_MS);
+        }
+    }
+
+    public class MediumInterruptedRunnable extends CheckedInterruptedRunnable {
+        protected void realRun() throws InterruptedException {
+            delay(MEDIUM_DELAY_MS);
+        }
+    }
+
+    public Runnable possiblyInterruptedRunnable(final long timeoutMillis) {
+        return new CheckedRunnable() {
+            protected void realRun() {
+                try {
+                    delay(timeoutMillis);
+                } catch (InterruptedException ok) {}
+            }};
+    }
+
+    public class MediumPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(MEDIUM_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public class LongPossiblyInterruptedRunnable extends CheckedRunnable {
+        protected void realRun() {
+            try {
+                delay(LONG_DELAY_MS);
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    /**
+     * For use as ThreadFactory in constructors
+     */
+    public static class SimpleThreadFactory implements ThreadFactory {
+        public Thread newThread(Runnable r) {
+            return new Thread(r);
+        }
+    }
+
+    public interface TrackedRunnable extends Runnable {
+        boolean isDone();
+    }
+
+    public static TrackedRunnable trackedRunnable(final long timeoutMillis) {
+        return new TrackedRunnable() {
+                private volatile boolean done = false;
+                public boolean isDone() { return done; }
+                public void run() {
+                    try {
+                        delay(timeoutMillis);
+                        done = true;
+                    } catch (InterruptedException ok) {}
+                }
+            };
+    }
+
+    public static class TrackedShortRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(SHORT_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedSmallRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(SMALL_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedMediumRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(MEDIUM_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedLongRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            try {
+                delay(LONG_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+        }
+    }
+
+    public static class TrackedNoOpRunnable implements Runnable {
+        public volatile boolean done = false;
+        public void run() {
+            done = true;
+        }
+    }
+
+    public static class TrackedCallable implements Callable {
+        public volatile boolean done = false;
+        public Object call() {
+            try {
+                delay(SMALL_DELAY_MS);
+                done = true;
+            } catch (InterruptedException ok) {}
+            return Boolean.TRUE;
+        }
+    }
+
+    /**
+     * Analog of CheckedRunnable for RecursiveAction
+     */
+    public abstract class CheckedRecursiveAction extends RecursiveAction {
+        protected abstract void realCompute() throws Throwable;
+
+        @Override protected final void compute() {
+            try {
+                realCompute();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+            }
+        }
+    }
+
+    /**
+     * Analog of CheckedCallable for RecursiveTask
+     */
+    public abstract class CheckedRecursiveTask<T> extends RecursiveTask<T> {
+        protected abstract T realCompute() throws Throwable;
+
+        @Override protected final T compute() {
+            try {
+                return realCompute();
+            } catch (Throwable fail) {
+                threadUnexpectedException(fail);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * For use as RejectedExecutionHandler in constructors
+     */
+    public static class NoOpREHandler implements RejectedExecutionHandler {
+        public void rejectedExecution(Runnable r,
+                                      ThreadPoolExecutor executor) {}
+    }
+
+    /**
+     * A CyclicBarrier that uses timed await and fails with
+     * AssertionFailedErrors instead of throwing checked exceptions.
+     */
+    public static class CheckedBarrier extends CyclicBarrier {
+        public CheckedBarrier(int parties) { super(parties); }
+
+        public int await() {
+            try {
+                return super.await(2 * LONG_DELAY_MS, MILLISECONDS);
+            } catch (TimeoutException timedOut) {
+                throw new AssertionFailedError("timed out");
+            } catch (Exception fail) {
+                AssertionFailedError afe =
+                    new AssertionFailedError("Unexpected exception: " + fail);
+                afe.initCause(fail);
+                throw afe;
+            }
+        }
+    }
+
+    void checkEmpty(BlockingQueue q) {
+        try {
+            assertTrue(q.isEmpty());
+            assertEquals(0, q.size());
+            assertNull(q.peek());
+            assertNull(q.poll());
+            assertNull(q.poll(0, MILLISECONDS));
+            assertEquals(q.toString(), "[]");
+            assertTrue(Arrays.equals(q.toArray(), new Object[0]));
+            assertFalse(q.iterator().hasNext());
+            try {
+                q.element();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                q.iterator().next();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                q.remove();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+    }
+
+    void assertSerialEquals(Object x, Object y) {
+        assertTrue(Arrays.equals(serialBytes(x), serialBytes(y)));
+    }
+
+    void assertNotSerialEquals(Object x, Object y) {
+        assertFalse(Arrays.equals(serialBytes(x), serialBytes(y)));
+    }
+
+    byte[] serialBytes(Object o) {
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(o);
+            oos.flush();
+            oos.close();
+            return bos.toByteArray();
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+            return new byte[0];
+        }
+    }
+
+    void assertImmutable(final Object o) {
+        if (o instanceof Collection) {
+            assertThrows(
+                UnsupportedOperationException.class,
+                new Runnable() { public void run() {
+                        ((Collection) o).add(null);}});
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    <T> T serialClone(T o) {
+        try {
+            ObjectInputStream ois = new ObjectInputStream
+                (new ByteArrayInputStream(serialBytes(o)));
+            T clone = (T) ois.readObject();
+            if (o == clone) assertImmutable(o);
+            assertSame(o.getClass(), clone.getClass());
+            return clone;
+        } catch (Throwable fail) {
+            threadUnexpectedException(fail);
+            return null;
+        }
+    }
+
+    /**
+     * A version of serialClone that leaves error handling (for
+     * e.g. NotSerializableException) up to the caller.
+     */
+    @SuppressWarnings("unchecked")
+    <T> T serialClonePossiblyFailing(T o)
+        throws ReflectiveOperationException, java.io.IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(bos);
+        oos.writeObject(o);
+        oos.flush();
+        oos.close();
+        ObjectInputStream ois = new ObjectInputStream
+            (new ByteArrayInputStream(bos.toByteArray()));
+        T clone = (T) ois.readObject();
+        if (o == clone) assertImmutable(o);
+        assertSame(o.getClass(), clone.getClass());
+        return clone;
+    }
+
+    /**
+     * If o implements Cloneable and has a public clone method,
+     * returns a clone of o, else null.
+     */
+    @SuppressWarnings("unchecked")
+    <T> T cloneableClone(T o) {
+        if (!(o instanceof Cloneable)) return null;
+        final T clone;
+        try {
+            clone = (T) o.getClass().getMethod("clone").invoke(o);
+        } catch (NoSuchMethodException ok) {
+            return null;
+        } catch (ReflectiveOperationException unexpected) {
+            throw new Error(unexpected);
+        }
+        assertNotSame(o, clone); // not 100% guaranteed by spec
+        assertSame(o.getClass(), clone.getClass());
+        return clone;
+    }
+
+    public void assertThrows(Class<? extends Throwable> expectedExceptionClass,
+                             Runnable... throwingActions) {
+        for (Runnable throwingAction : throwingActions) {
+            boolean threw = false;
+            try { throwingAction.run(); }
+            catch (Throwable t) {
+                threw = true;
+                if (!expectedExceptionClass.isInstance(t)) {
+                    AssertionFailedError afe =
+                        new AssertionFailedError
+                        ("Expected " + expectedExceptionClass.getName() +
+                         ", got " + t.getClass().getName());
+                    afe.initCause(t);
+                    threadUnexpectedException(afe);
+                }
+            }
+            if (!threw)
+                shouldThrow(expectedExceptionClass.getName());
+        }
+    }
+
+    public void assertIteratorExhausted(Iterator<?> it) {
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertFalse(it.hasNext());
+    }
+
+    public <T> Callable<T> callableThrowing(final Exception ex) {
+        return new Callable<T>() { public T call() throws Exception { throw ex; }};
+    }
+
+    public Runnable runnableThrowing(final RuntimeException ex) {
+        return new Runnable() { public void run() { throw ex; }};
+    }
+
+    /** A reusable thread pool to be shared by tests. */
+    static final ExecutorService cachedThreadPool =
+        new ThreadPoolExecutor(0, Integer.MAX_VALUE,
+                               1000L, MILLISECONDS,
+                               new SynchronousQueue<Runnable>());
+
+    static <T> void shuffle(T[] array) {
+        Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current());
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingDeque8Test.java b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingDeque8Test.java
new file mode 100644
index 0000000..29b2704
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingDeque8Test.java
@@ -0,0 +1,77 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.Spliterator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LinkedBlockingDeque8Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(LinkedBlockingDeque8Test.class);
+    }
+
+    /**
+     * Spliterator.getComparator always throws IllegalStateException
+     */
+    public void testSpliterator_getComparator() {
+        assertThrows(IllegalStateException.class,
+                     () -> new LinkedBlockingDeque().spliterator().getComparator());
+    }
+
+    /**
+     * Spliterator characteristics are as advertised
+     */
+    public void testSpliterator_characteristics() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        Spliterator s = q.spliterator();
+        int characteristics = s.characteristics();
+        int required = Spliterator.CONCURRENT
+            | Spliterator.NONNULL
+            | Spliterator.ORDERED;
+        assertEquals(required, characteristics & required);
+        assertTrue(s.hasCharacteristics(required));
+        assertEquals(0, characteristics
+                     & (Spliterator.DISTINCT
+                        | Spliterator.IMMUTABLE
+                        | Spliterator.SORTED));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java
new file mode 100644
index 0000000..c8cc4a1
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingDequeTest.java
@@ -0,0 +1,1859 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import junit.framework.Test;
+
+public class LinkedBlockingDequeTest extends JSR166TestCase {
+
+    public static class Unbounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingDeque();
+        }
+    }
+
+    public static class Bounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingDeque(SIZE);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return LinkedBlockingDeque.class; }
+            public Collection emptyCollection() { return new LinkedBlockingDeque(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(LinkedBlockingDequeTest.class,
+                            new Unbounded().testSuite(),
+                            new Bounded().testSuite(),
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * Returns a new deque of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private LinkedBlockingDeque<Integer> populatedDeque(int n) {
+        LinkedBlockingDeque<Integer> q =
+            new LinkedBlockingDeque<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        assertEquals((Integer) 0, q.peekFirst());
+        assertEquals((Integer) (n - 1), q.peekLast());
+        return q;
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.removeFirst();
+        q.removeFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.removeFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offerFirst(null) throws NullPointerException
+     */
+    public void testOfferFirstNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        try {
+            q.offerFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * offerLast(null) throws NullPointerException
+     */
+    public void testOfferLastNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        try {
+            q.offerLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * OfferFirst succeeds
+     */
+    public void testOfferFirst() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.offerFirst(new Integer(0)));
+        assertTrue(q.offerFirst(new Integer(1)));
+    }
+
+    /**
+     * OfferLast succeeds
+     */
+    public void testOfferLast() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        assertTrue(q.offerLast(new Integer(0)));
+        assertTrue(q.offerLast(new Integer(1)));
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * peekFirst returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * peekLast returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * getFirst() returns first element, or throws NSEE if empty
+     */
+    public void testFirstElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * getLast() returns last element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirst() removes first element, or throws NSEE if empty
+     */
+    public void testRemoveFirst() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.removeFirst());
+        }
+        try {
+            q.removeFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * removeLast() removes last element, or throws NSEE if empty
+     */
+    public void testRemoveLast() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.removeLast());
+        }
+        try {
+            q.removeLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * peekFirst returns element inserted with addFirst
+     */
+    public void testAddFirst() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.addFirst(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * peekLast returns element inserted with addLast
+     */
+    public void testAddLast() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.addLast(four);
+        assertSame(four, q.peekLast());
+    }
+
+    /**
+     * A new deque has the indicated capacity, or Integer.MAX_VALUE if
+     * none given
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new LinkedBlockingDeque(SIZE).remainingCapacity());
+        assertEquals(Integer.MAX_VALUE, new LinkedBlockingDeque().remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IllegalArgumentException if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedBlockingDeque(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NullPointerException
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedBlockingDeque(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NullPointerException
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedBlockingDeque(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws
+     * NullPointerException
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedBlockingDeque(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Deque contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedBlockingDeque q = new LinkedBlockingDeque(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Deque transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        assertTrue(q.isEmpty());
+        assertEquals("should have room for 2", 2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * push(null) throws NPE
+     */
+    public void testPushNull() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(1);
+        try {
+            q.push(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * push succeeds if not full; throws ISE if full
+     */
+    public void testPush() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.push(x);
+            assertEquals(x, q.peek());
+        }
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.push(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * peekFirst returns element inserted with push
+     */
+    public void testPushWithPeek() {
+        LinkedBlockingDeque q = populatedDeque(3);
+        q.pollLast();
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws ISE if full
+     */
+    public void testAdd() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(q.add(new Integer(i)));
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws IllegalStateException if not enough room
+     */
+    public void testAddAll4() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE - 1);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Deque contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedDeque(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * putFirst(null) throws NPE
+     */
+    public void testPutFirstNull() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        try {
+            q.putFirst(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * all elements successfully putFirst are contained
+     */
+    public void testPutFirst() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.putFirst(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putFirst blocks interruptibly if full
+     */
+    public void testBlockingPutFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.putFirst(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putFirst blocks interruptibly waiting for take when full
+     */
+    public void testPutFirstWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.putFirst(i);
+                pleaseTake.countDown();
+                q.putFirst(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putFirst(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(capacity - 1, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offerFirst times out if full and elements not taken
+     */
+    public void testTimedOfferFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.putFirst(new Object());
+                q.putFirst(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offerFirst(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offerFirst(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTakeFirst() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.takeFirst());
+        }
+    }
+
+    /**
+     * takeFirst() blocks interruptibly when empty
+     */
+    public void testTakeFirstFromEmptyBlocksInterruptibly() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeFirst() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeFirstFromEmptyAfterInterrupt() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast() blocks interruptibly when empty
+     */
+    public void testTakeLastFromEmptyBlocksInterruptibly() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadStarted.countDown();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(threadStarted);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast() throws InterruptedException immediately if interrupted
+     * before waiting
+     */
+    public void testTakeLastFromEmptyAfterInterrupt() {
+        final BlockingDeque q = new LinkedBlockingDeque();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * takeFirst removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTakeFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.takeFirst());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.takeFirst();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollFirst with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollFirst0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst(0, MILLISECONDS));
+        }
+        assertNull(q.pollFirst(0, MILLISECONDS));
+    }
+
+    /**
+     * timed pollFirst with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollFirst() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed pollFirst throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPollFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollFirst before a delayed offerFirst fails; after offerFirst succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollFirstWithOfferFirst() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.pollFirst(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.pollFirst(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                barrier.await();
+                try {
+                    q.pollFirst(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offerFirst(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * putLast(null) throws NPE
+     */
+    public void testPutLastNull() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        try {
+            q.putLast(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * all elements successfully putLast are contained
+     */
+    public void testPutLast() throws InterruptedException {
+        LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.putLast(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putLast blocks interruptibly if full
+     */
+    public void testBlockingPutLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.putLast(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * putLast blocks interruptibly waiting for take when full
+     */
+    public void testPutLastWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(capacity);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.putLast(i);
+                pleaseTake.countDown();
+                q.putLast(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.putLast(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offerLast times out if full and elements not taken
+     */
+    public void testTimedOfferLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.putLast(new Object());
+                q.putLast(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offerLast(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offerLast(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * takeLast retrieves elements in FIFO order
+     */
+    public void testTakeLast() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i - 1, q.takeLast());
+        }
+    }
+
+    /**
+     * takeLast removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTakeLast() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(SIZE - i - 1, q.takeLast());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.takeLast();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timed pollLast with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollLast0() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i - 1, q.pollLast(0, MILLISECONDS));
+        }
+        assertNull(q.pollLast(0, MILLISECONDS));
+    }
+
+    /**
+     * timed pollLast with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPollLast() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(SIZE - i - 1, q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.pollLast(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed pollLast throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPollLast() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(SIZE - i - 1,
+                                 q.pollLast(LONG_DELAY_MS, MILLISECONDS));
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.pollLast(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll before a delayed offerLast fails; after offerLast succeeds;
+     * on interruption throws
+     */
+    public void testTimedPollWithOfferLast() throws InterruptedException {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier barrier = new CheckedBarrier(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                barrier.await();
+
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                barrier.await();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        barrier.await();
+        long startTime = System.nanoTime();
+        assertTrue(q.offerLast(zero, LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        barrier.await();
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            q.poll();
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        LinkedBlockingDeque p = new LinkedBlockingDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        LinkedBlockingDeque p = populatedDeque(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedBlockingDeque q = populatedDeque(SIZE);
+            LinkedBlockingDeque p = populatedDeque(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedBlockingDeque<Integer> q = populatedDeque(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.remove());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        Deque c = new LinkedBlockingDeque();
+        assertIteratorExhausted(c.iterator());
+        assertIteratorExhausted(c.descendingIterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(0, q.remainingCapacity());
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            int k = 0;
+            for (Iterator it = q.descendingIterator(); it.hasNext();) {
+                assertEquals(++k, it.next());
+            }
+
+            assertEquals(3, k);
+            q.remove();
+            q.remove();
+            q.remove();
+        }
+    }
+
+    /**
+     * descendingIterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int iters = 0; iters < 100; ++iters) {
+            q.add(new Integer(3));
+            q.add(new Integer(2));
+            q.add(new Integer(1));
+            Iterator it = q.descendingIterator();
+            assertEquals(it.next(), new Integer(1));
+            it.remove();
+            assertEquals(it.next(), new Integer(2));
+            it = q.descendingIterator();
+            assertEquals(it.next(), new Integer(2));
+            assertEquals(it.next(), new Integer(3));
+            it.remove();
+            assertFalse(it.hasNext());
+            q.remove();
+        }
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        q.add(one);
+        q.add(two);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(three));
+                    threadsStarted.await();
+                    assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedBlockingDeque q = new LinkedBlockingDeque(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized deque has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedDeque(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties deque into another collection c
+     */
+    public void testDrainTo() {
+        LinkedBlockingDeque q = populatedDeque(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full deque, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedBlockingDeque q = populatedDeque(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedBlockingDeque q = new LinkedBlockingDeque();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Deque<?>[] qs = {
+            new LinkedBlockingDeque<Object>(),
+            populatedDeque(2),
+        };
+
+        for (Deque<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+            assertFalse(q.removeFirstOccurrence(null));
+            assertFalse(q.removeLastOccurrence(null));
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingQueue8Test.java b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingQueue8Test.java
new file mode 100644
index 0000000..4a6e42a
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingQueue8Test.java
@@ -0,0 +1,77 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.Spliterator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LinkedBlockingQueue8Test extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(LinkedBlockingQueue8Test.class);
+    }
+
+    /**
+     * Spliterator.getComparator always throws IllegalStateException
+     */
+    public void testSpliterator_getComparator() {
+        assertThrows(IllegalStateException.class,
+                     () -> new LinkedBlockingQueue().spliterator().getComparator());
+    }
+
+    /**
+     * Spliterator characteristics are as advertised
+     */
+    public void testSpliterator_characteristics() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue();
+        Spliterator s = q.spliterator();
+        int characteristics = s.characteristics();
+        int required = Spliterator.CONCURRENT
+            | Spliterator.NONNULL
+            | Spliterator.ORDERED;
+        assertEquals(required, characteristics & required);
+        assertTrue(s.hasCharacteristics(required));
+        assertEquals(0, characteristics
+                     & (Spliterator.DISTINCT
+                        | Spliterator.IMMUTABLE
+                        | Spliterator.SORTED));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java
new file mode 100644
index 0000000..4580f86
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LinkedBlockingQueueTest.java
@@ -0,0 +1,899 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import junit.framework.Test;
+
+public class LinkedBlockingQueueTest extends JSR166TestCase {
+
+    public static class Unbounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingQueue();
+        }
+    }
+
+    public static class Bounded extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedBlockingQueue(SIZE);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return LinkedBlockingQueue.class; }
+            public Collection emptyCollection() { return new LinkedBlockingQueue(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(LinkedBlockingQueueTest.class,
+                            new Unbounded().testSuite(),
+                            new Bounded().testSuite(),
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private LinkedBlockingQueue<Integer> populatedQueue(int n) {
+        LinkedBlockingQueue<Integer> q =
+            new LinkedBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; i++)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(n, q.size());
+        assertEquals((Integer) 0, q.peek());
+        return q;
+    }
+
+    /**
+     * A new queue has the indicated capacity, or Integer.MAX_VALUE if
+     * none given
+     */
+    public void testConstructor1() {
+        assertEquals(SIZE, new LinkedBlockingQueue(SIZE).remainingCapacity());
+        assertEquals(Integer.MAX_VALUE, new LinkedBlockingQueue().remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IllegalArgumentException if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NullPointerException
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedBlockingQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NullPointerException
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws
+     * NullPointerException
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingQueue q = new LinkedBlockingQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * Queue transitions from empty to full when elements added
+     */
+    public void testEmptyFull() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals("should have room for 2", 2, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        assertFalse(q.isEmpty());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(three));
+    }
+
+    /**
+     * remainingCapacity decreases on add, increases on remove
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.remainingCapacity());
+            assertEquals(SIZE, q.size() + q.remainingCapacity());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * Offer succeeds if not full; fails if full
+     */
+    public void testOffer() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add succeeds if not full; throws IllegalStateException if full
+     */
+    public void testAdd() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertTrue(q.add(new Integer(i)));
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(new Integer(SIZE));
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAllSelf() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll throws IllegalStateException if not enough room
+     */
+    public void testAddAll4() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE - 1);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            q.addAll(elements);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() throws InterruptedException {
+        LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly if full
+     */
+    public void testBlockingPut() throws InterruptedException {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i)
+                    q.put(i);
+                assertEquals(SIZE, q.size());
+                assertEquals(0, q.remainingCapacity());
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(SIZE, q.size());
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take when full
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final int capacity = 2;
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < capacity; i++)
+                    q.put(i);
+                pleaseTake.countDown();
+                q.put(86);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        assertEquals(0, q.take());
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if full and elements not taken
+     */
+    public void testTimedOffer() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Object());
+                q.put(new Object());
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final BlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedBlockingQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        await(aboutToWait);
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * An add following remove(x) succeeds
+     */
+    public void testRemoveElementAndAdd() throws InterruptedException {
+        LinkedBlockingQueue q = new LinkedBlockingQueue();
+        assertTrue(q.add(new Integer(1)));
+        assertTrue(q.add(new Integer(2)));
+        assertTrue(q.remove(new Integer(1)));
+        assertTrue(q.remove(new Integer(2)));
+        assertTrue(q.add(new Integer(3)));
+        assertNotNull(q.take());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(SIZE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        LinkedBlockingQueue p = new LinkedBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        LinkedBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedBlockingQueue q = populatedQueue(SIZE);
+            LinkedBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() throws InterruptedException {
+        LinkedBlockingQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new LinkedBlockingQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(0, q.remainingCapacity());
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(3);
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        q.add(one);
+        q.add(two);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(three));
+                    threadsStarted.await();
+                    assertTrue(q.offer(three, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedBlockingQueue q = new LinkedBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        LinkedBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedBlockingQueue q = new LinkedBlockingQueue();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new LinkedBlockingQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LinkedListTest.java b/ojluni/src/test/java/util/concurrent/tck/LinkedListTest.java
new file mode 100644
index 0000000..b0afd4f
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LinkedListTest.java
@@ -0,0 +1,687 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LinkedListTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return LinkedList.class; }
+            public Collection emptyCollection() { return new LinkedList(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return false; }
+            public boolean permitsNulls() { return true; }
+        }
+        class SubListImplementation extends Implementation {
+            public Collection emptyCollection() {
+                return new LinkedList().subList(0, 0);
+            }
+        }
+        return newTestSuite(
+                LinkedListTest.class,
+                CollectionTest.testSuite(new Implementation()),
+                CollectionTest.testSuite(new SubListImplementation()));
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private LinkedList<Integer> populatedQueue(int n) {
+        LinkedList<Integer> q = new LinkedList<>();
+        assertTrue(q.isEmpty());
+        for (int i = 0; i < n; ++i)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        assertEquals((Integer) 0, q.peekFirst());
+        assertEquals((Integer) (n - 1), q.peekLast());
+        return q;
+    }
+
+    /**
+     * new queue is empty
+     */
+    public void testConstructor1() {
+        assertEquals(0, new LinkedList().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new LinkedList((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedList q = new LinkedList(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) succeeds
+     */
+    public void testOfferNull() {
+        LinkedList q = new LinkedList();
+        q.offer(null);
+        assertNull(q.get(0));
+        assertTrue(q.contains(null));
+    }
+
+    /**
+     * Offer succeeds
+     */
+    public void testOffer() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offer(new Integer(0)));
+        assertTrue(q.offer(new Integer(1)));
+    }
+
+    /**
+     * add succeeds
+     */
+    public void testAdd() {
+        LinkedList q = new LinkedList();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        LinkedList q = new LinkedList();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        LinkedList q = new LinkedList();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * addAll with too large an index throws IOOBE
+     */
+    public void testAddAll2_IndexOutOfBoundsException() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        LinkedList m = new LinkedList();
+        m.add(new Object());
+        try {
+            l.addAll(4,m);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * addAll with negative index throws IOOBE
+     */
+    public void testAddAll4_BadIndex() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        LinkedList m = new LinkedList();
+        m.add(new Object());
+        try {
+            l.addAll(-1,m);
+            shouldThrow();
+        } catch (IndexOutOfBoundsException success) {}
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove((Integer)i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove((Integer)i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove((Integer)(i + 1)));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        LinkedList q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedList q = populatedQueue(SIZE);
+        LinkedList p = new LinkedList();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            assertTrue(p.add(new Integer(i)));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        LinkedList q = populatedQueue(SIZE);
+        LinkedList p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedList q = populatedQueue(SIZE);
+            LinkedList p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedList q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedList<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * toArray(null) throws NullPointerException
+     */
+    public void testToArray_NullArg() {
+        LinkedList l = new LinkedList();
+        l.add(new Object());
+        try {
+            l.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedList l = new LinkedList();
+        l.add(new Integer(5));
+        try {
+            l.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        LinkedList q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new LinkedList().iterator());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(1));
+        q.add(new Integer(2));
+        q.add(new Integer(3));
+        int k = 0;
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(1));
+        q.add(new Integer(2));
+        q.add(new Integer(3));
+        Iterator it = q.iterator();
+        assertEquals(1, it.next());
+        it.remove();
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * Descending iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        LinkedList q = populatedQueue(SIZE);
+        int i = 0;
+        Iterator it = q.descendingIterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+        assertFalse(it.hasNext());
+        try {
+            it.next();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * Descending iterator ordering is reverse FIFO
+     */
+    public void testDescendingIteratorOrdering() {
+        final LinkedList q = new LinkedList();
+        q.add(new Integer(3));
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        int k = 0;
+        for (Iterator it = q.descendingIterator(); it.hasNext();) {
+            assertEquals(++k, it.next());
+        }
+
+        assertEquals(3, k);
+    }
+
+    /**
+     * descendingIterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final LinkedList q = new LinkedList();
+        q.add(three);
+        q.add(two);
+        q.add(one);
+        Iterator it = q.descendingIterator();
+        it.next();
+        it.remove();
+        it = q.descendingIterator();
+        assertSame(it.next(), two);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedList q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * peek returns element inserted with addFirst
+     */
+    public void testAddFirst() {
+        LinkedList q = populatedQueue(3);
+        q.addFirst(four);
+        assertSame(four, q.peek());
+    }
+
+    /**
+     * peekFirst returns element inserted with push
+     */
+    public void testPush() {
+        LinkedList q = populatedQueue(3);
+        q.push(four);
+        assertSame(four, q.peekFirst());
+    }
+
+    /**
+     * pop removes next element, or throws NSEE if empty
+     */
+    public void testPop() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pop());
+        }
+        try {
+            q.pop();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * OfferFirst succeeds
+     */
+    public void testOfferFirst() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offerFirst(new Integer(0)));
+        assertTrue(q.offerFirst(new Integer(1)));
+    }
+
+    /**
+     * OfferLast succeeds
+     */
+    public void testOfferLast() {
+        LinkedList q = new LinkedList();
+        assertTrue(q.offerLast(new Integer(0)));
+        assertTrue(q.offerLast(new Integer(1)));
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollLast());
+    }
+
+    /**
+     * peekFirst returns next element, or null if empty
+     */
+    public void testPeekFirst() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peekFirst());
+            assertEquals(i, q.pollFirst());
+            assertTrue(q.peekFirst() == null ||
+                       !q.peekFirst().equals(i));
+        }
+        assertNull(q.peekFirst());
+    }
+
+    /**
+     * peekLast returns next element, or null if empty
+     */
+    public void testPeekLast() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.peekLast());
+            assertEquals(i, q.pollLast());
+            assertTrue(q.peekLast() == null ||
+                       !q.peekLast().equals(i));
+        }
+        assertNull(q.peekLast());
+    }
+
+    public void testFirstElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.getFirst());
+            assertEquals(i, q.pollFirst());
+        }
+        try {
+            q.getFirst();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * getLast returns next element, or throws NSEE if empty
+     */
+    public void testLastElement() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.getLast());
+            assertEquals(i, q.pollLast());
+        }
+        try {
+            q.getLast();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        assertNull(q.peekLast());
+    }
+
+    /**
+     * removeFirstOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveFirstOccurrence() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeFirstOccurrence(new Integer(i)));
+            assertFalse(q.removeFirstOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * removeLastOccurrence(x) removes x and returns true if present
+     */
+    public void testRemoveLastOccurrence() {
+        LinkedList q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.removeLastOccurrence(new Integer(i)));
+            assertFalse(q.removeLastOccurrence(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LinkedTransferQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/LinkedTransferQueueTest.java
new file mode 100644
index 0000000..ee53cf9
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LinkedTransferQueueTest.java
@@ -0,0 +1,1087 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include John Vint
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedTransferQueue;
+
+import junit.framework.Test;
+
+@SuppressWarnings({"unchecked", "rawtypes"})
+public class LinkedTransferQueueTest extends JSR166TestCase {
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new LinkedTransferQueue();
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return LinkedTransferQueue.class; }
+            public Collection emptyCollection() { return new LinkedTransferQueue(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(LinkedTransferQueueTest.class,
+                            new Generic().testSuite(),
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /**
+     * Constructor builds new queue with size being zero and empty
+     * being true
+     */
+    public void testConstructor1() {
+        assertEquals(0, new LinkedTransferQueue().size());
+        assertTrue(new LinkedTransferQueue().isEmpty());
+    }
+
+    /**
+     * Initializing constructor with null collection throws
+     * NullPointerException
+     */
+    public void testConstructor2() {
+        try {
+            new LinkedTransferQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws
+     * NullPointerException
+     */
+    public void testConstructor3() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new LinkedTransferQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing constructor with a collection containing some null elements
+     * throws NullPointerException
+     */
+    public void testConstructor4() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new LinkedTransferQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of the collection it is initialized by
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            ints[i] = i;
+        }
+        List intList = Arrays.asList(ints);
+        LinkedTransferQueue q
+            = new LinkedTransferQueue(intList);
+        assertEquals(q.size(), intList.size());
+        assertEquals(q.toString(), intList.toString());
+        assertTrue(Arrays.equals(q.toArray(),
+                                     intList.toArray()));
+        assertTrue(Arrays.equals(q.toArray(new Object[0]),
+                                 intList.toArray(new Object[0])));
+        assertTrue(Arrays.equals(q.toArray(new Object[SIZE]),
+                                 intList.toArray(new Object[SIZE])));
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * remainingCapacity() always returns Integer.MAX_VALUE
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(SIZE - i, q.size());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(i, q.size());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAllSelf() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws
+     * NullPointerException after possibly adding some elements
+     */
+    public void testAddAll3() {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements, in traversal order, of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i) {
+            ints[i] = i;
+        }
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.put(i);
+            assertTrue(q.contains(i));
+        }
+    }
+
+    /**
+     * take retrieves elements in FIFO order
+     */
+    public void testTake() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.take());
+        }
+    }
+
+    /**
+     * take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final BlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.poll());
+        }
+        assertNull(q.poll());
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        long startTime = System.nanoTime();
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i)
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * timed poll after thread interrupted throws InterruptedException
+     * instead of returning timeout status
+     */
+    public void testTimedPollAfterInterrupt() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                Thread.currentThread().interrupt();
+                for (int i = 0; i < SIZE; ++i)
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.peek());
+            assertEquals(i, (int) q.poll());
+            assertTrue(q.peek() == null ||
+                       i != (int) q.peek());
+        }
+        assertNull(q.peek());
+        checkEmpty(q);
+    }
+
+    /**
+     * element returns next element, or throws NoSuchElementException if empty
+     */
+    public void testElement() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.element());
+            assertEquals(i, (int) q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        checkEmpty(q);
+    }
+
+    /**
+     * remove removes next element, or throws NoSuchElementException if empty
+     */
+    public void testRemove() throws InterruptedException {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, (int) q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+        checkEmpty(q);
+    }
+
+    /**
+     * An add following remove(x) succeeds
+     */
+    public void testRemoveElementAndAdd() throws InterruptedException {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.add(one));
+        assertTrue(q.add(two));
+        assertTrue(q.remove(one));
+        assertTrue(q.remove(two));
+        assertTrue(q.add(three));
+        assertSame(q.take(), three);
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(i));
+            assertEquals(i, (int) q.poll());
+            assertFalse(q.contains(i));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() throws InterruptedException {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        q.clear();
+        checkEmpty(q);
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertTrue(q.contains(one));
+        q.clear();
+        checkEmpty(q);
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        LinkedTransferQueue<Integer> p = new LinkedTransferQueue<>();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(i);
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true
+     * if changed
+     */
+    public void testRetainAll() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        LinkedTransferQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0) {
+                assertFalse(changed);
+            } else {
+                assertTrue(changed);
+            }
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true
+     * if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            LinkedTransferQueue q = populatedQueue(SIZE);
+            LinkedTransferQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                assertFalse(q.contains(p.remove()));
+            }
+        }
+    }
+
+    /**
+     * toArray() contains all elements in FIFO order
+     */
+    public void testToArray() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++) {
+            assertSame(o[i], q.poll());
+        }
+    }
+
+    /**
+     * toArray(a) contains all elements in FIFO order
+     */
+    public void testToArray2() {
+        LinkedTransferQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++) {
+            assertSame(ints[i], q.poll());
+        }
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() throws InterruptedException {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+
+        it = q.iterator();
+        for (i = 0; it.hasNext(); i++)
+            assertEquals(it.next(), q.take());
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new LinkedTransferQueue().iterator());
+    }
+
+    /**
+     * iterator.remove() removes current element
+     */
+    public void testIteratorRemove() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        q.add(two);
+        q.add(one);
+        q.add(three);
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertSame(it.next(), one);
+        assertSame(it.next(), three);
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * iterator ordering is FIFO
+     */
+    public void testIteratorOrdering() {
+        final LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        int k = 0;
+        for (Integer n : q) {
+            assertEquals(++k, (int) n);
+        }
+        assertEquals(3, k);
+    }
+
+    /**
+     * Modifications do not cause iterators to fail
+     */
+    public void testWeaklyConsistentIteration() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        for (Iterator it = q.iterator(); it.hasNext();) {
+            q.remove();
+            it.next();
+        }
+        assertEquals(0, q.size());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    long startTime = System.nanoTime();
+                    assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                    checkEmpty(q);
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    long startTime = System.nanoTime();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements in same order
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(y, x);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertTrue(Arrays.equals(x.toArray(), y.toArray()));
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        LinkedTransferQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, l.get(i));
+        }
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i) {
+            assertEquals(i, l.get(i));
+        }
+    }
+
+    /**
+     * drainTo(c) empties full queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final LinkedTransferQueue q = populatedQueue(SIZE);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(SIZE + 1);
+            }});
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(i, l.get(i));
+        awaitTermination(t);
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        LinkedTransferQueue q = new LinkedTransferQueue();
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++) {
+                assertTrue(q.offer(j));
+            }
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(j, l.get(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * timed poll() or take() increments the waiting consumer count;
+     * offer(e) decrements the waiting consumer count
+     */
+    public void testWaitingConsumer() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertEquals(0, q.getWaitingConsumerCount());
+        assertFalse(q.hasWaitingConsumer());
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                long startTime = System.nanoTime();
+                assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                assertEquals(0, q.getWaitingConsumerCount());
+                assertFalse(q.hasWaitingConsumer());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        threadStarted.await();
+        Callable<Boolean> oneConsumer
+            = new Callable<Boolean>() { public Boolean call() {
+                return q.hasWaitingConsumer()
+                && q.getWaitingConsumerCount() == 1; }};
+        waitForThreadToEnterWaitState(t, oneConsumer);
+
+        assertTrue(q.offer(one));
+        assertEquals(0, q.getWaitingConsumerCount());
+        assertFalse(q.hasWaitingConsumer());
+
+        awaitTermination(t);
+    }
+
+    /**
+     * transfer(null) throws NullPointerException
+     */
+    public void testTransfer1() throws InterruptedException {
+        try {
+            LinkedTransferQueue q = new LinkedTransferQueue();
+            q.transfer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * transfer waits until a poll occurs. The transfered element
+     * is returned by the associated poll.
+     */
+    public void testTransfer2() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                threadStarted.countDown();
+                q.transfer(five);
+                checkEmpty(q);
+            }});
+
+        threadStarted.await();
+        Callable<Boolean> oneElement
+            = new Callable<Boolean>() { public Boolean call() {
+                return !q.isEmpty() && q.size() == 1; }};
+        waitForThreadToEnterWaitState(t, oneElement);
+
+        assertSame(five, q.poll());
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * transfer waits until a poll occurs, and then transfers in fifo order
+     */
+    public void testTransfer3() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
+
+        Thread first = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                assertTrue(!q.contains(four));
+                assertEquals(1, q.size());
+            }});
+
+        Thread interruptedThread = newStartedThread(
+            new CheckedInterruptedRunnable() {
+                public void realRun() throws InterruptedException {
+                    while (q.isEmpty())
+                        Thread.yield();
+                    q.transfer(five);
+                }});
+
+        while (q.size() < 2)
+            Thread.yield();
+        assertEquals(2, q.size());
+        assertSame(four, q.poll());
+        first.join();
+        assertEquals(1, q.size());
+        interruptedThread.interrupt();
+        interruptedThread.join();
+        checkEmpty(q);
+    }
+
+    /**
+     * transfer waits until a poll occurs, at which point the polling
+     * thread returns the element
+     */
+    public void testTransfer4() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                assertFalse(q.contains(four));
+                assertSame(three, q.poll());
+            }});
+
+        while (q.isEmpty())
+            Thread.yield();
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertTrue(q.offer(three));
+        assertSame(four, q.poll());
+        awaitTermination(t);
+    }
+
+    /**
+     * transfer waits until a take occurs. The transfered element
+     * is returned by the associated take.
+     */
+    public void testTransfer5() throws InterruptedException {
+        final LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.transfer(four);
+                checkEmpty(q);
+            }});
+
+        while (q.isEmpty())
+            Thread.yield();
+        assertFalse(q.isEmpty());
+        assertEquals(1, q.size());
+        assertSame(four, q.take());
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * tryTransfer(null) throws NullPointerException
+     */
+    public void testTryTransfer1() {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        try {
+            q.tryTransfer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * tryTransfer returns false and does not enqueue if there are no
+     * consumers waiting to poll or take.
+     */
+    public void testTryTransfer2() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertFalse(q.tryTransfer(new Object()));
+        assertFalse(q.hasWaitingConsumer());
+        checkEmpty(q);
+    }
+
+    /**
+     * If there is a consumer waiting in timed poll, tryTransfer
+     * returns true while successfully transfering object.
+     */
+    public void testTryTransfer3() throws InterruptedException {
+        final Object hotPotato = new Object();
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (! q.hasWaitingConsumer())
+                    Thread.yield();
+                assertTrue(q.hasWaitingConsumer());
+                checkEmpty(q);
+                assertTrue(q.tryTransfer(hotPotato));
+            }});
+
+        long startTime = System.nanoTime();
+        assertSame(hotPotato, q.poll(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * If there is a consumer waiting in take, tryTransfer returns
+     * true while successfully transfering object.
+     */
+    public void testTryTransfer4() throws InterruptedException {
+        final Object hotPotato = new Object();
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                while (! q.hasWaitingConsumer())
+                    Thread.yield();
+                assertTrue(q.hasWaitingConsumer());
+                checkEmpty(q);
+                assertTrue(q.tryTransfer(hotPotato));
+            }});
+
+        assertSame(q.take(), hotPotato);
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * tryTransfer blocks interruptibly if no takers
+     */
+    public void testTryTransfer5() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        assertTrue(q.isEmpty());
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                Thread.currentThread().interrupt();
+                try {
+                    q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.tryTransfer(new Object(), LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * tryTransfer gives up after the timeout and returns false
+     */
+    public void testTryTransfer6() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertFalse(q.tryTransfer(new Object(),
+                                          timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                checkEmpty(q);
+            }});
+
+        awaitTermination(t);
+        checkEmpty(q);
+    }
+
+    /**
+     * tryTransfer waits for any elements previously in to be removed
+     * before transfering to a poll or take
+     */
+    public void testTryTransfer7() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.offer(four));
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertTrue(q.tryTransfer(five, LONG_DELAY_MS, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                checkEmpty(q);
+            }});
+
+        while (q.size() != 2)
+            Thread.yield();
+        assertEquals(2, q.size());
+        assertSame(four, q.poll());
+        assertSame(five, q.poll());
+        checkEmpty(q);
+        awaitTermination(t);
+    }
+
+    /**
+     * tryTransfer attempts to enqueue into the queue and fails
+     * returning false not enqueueing and the successive poll is null
+     */
+    public void testTryTransfer8() throws InterruptedException {
+        final LinkedTransferQueue q = new LinkedTransferQueue();
+        assertTrue(q.offer(four));
+        assertEquals(1, q.size());
+        long startTime = System.nanoTime();
+        assertFalse(q.tryTransfer(five, timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        assertEquals(1, q.size());
+        assertSame(four, q.poll());
+        assertNull(q.poll());
+        checkEmpty(q);
+    }
+
+    private LinkedTransferQueue<Integer> populatedQueue(int n) {
+        LinkedTransferQueue<Integer> q = new LinkedTransferQueue<>();
+        checkEmpty(q);
+        for (int i = 0; i < n; i++) {
+            assertEquals(i, q.size());
+            assertTrue(q.offer(i));
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        }
+        assertFalse(q.isEmpty());
+        return q;
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new LinkedTransferQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LockSupportTest.java b/ojluni/src/test/java/util/concurrent/tck/LockSupportTest.java
new file mode 100644
index 0000000..1a31cbd
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LockSupportTest.java
@@ -0,0 +1,404 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.LockSupport;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LockSupportTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(LockSupportTest.class);
+    }
+
+    static {
+        // Reduce the risk of rare disastrous classloading in first call to
+        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
+        Class<?> ensureLoaded = LockSupport.class;
+    }
+
+    /**
+     * Returns the blocker object used by tests in this file.
+     * Any old object will do; we'll return a convenient one.
+     */
+    static Object theBlocker() {
+        return LockSupportTest.class;
+    }
+
+    enum ParkMethod {
+        park() {
+            void park() {
+                LockSupport.park();
+            }
+            void park(long millis) {
+                throw new UnsupportedOperationException();
+            }
+        },
+        parkUntil() {
+            void park(long millis) {
+                LockSupport.parkUntil(deadline(millis));
+            }
+        },
+        parkNanos() {
+            void park(long millis) {
+                LockSupport.parkNanos(MILLISECONDS.toNanos(millis));
+            }
+        },
+        parkBlocker() {
+            void park() {
+                LockSupport.park(theBlocker());
+            }
+            void park(long millis) {
+                throw new UnsupportedOperationException();
+            }
+        },
+        parkUntilBlocker() {
+            void park(long millis) {
+                LockSupport.parkUntil(theBlocker(), deadline(millis));
+            }
+        },
+        parkNanosBlocker() {
+            void park(long millis) {
+                LockSupport.parkNanos(theBlocker(),
+                                      MILLISECONDS.toNanos(millis));
+            }
+        };
+
+        void park() { park(2 * LONG_DELAY_MS); }
+        abstract void park(long millis);
+
+        /** Returns a deadline to use with parkUntil. */
+        long deadline(long millis) {
+            // beware of rounding
+            return System.currentTimeMillis() + millis + 1;
+        }
+    }
+
+    /**
+     * park is released by subsequent unpark
+     */
+    public void testParkBeforeUnpark_park() {
+        testParkBeforeUnpark(ParkMethod.park);
+    }
+    public void testParkBeforeUnpark_parkNanos() {
+        testParkBeforeUnpark(ParkMethod.parkNanos);
+    }
+    public void testParkBeforeUnpark_parkUntil() {
+        testParkBeforeUnpark(ParkMethod.parkUntil);
+    }
+    public void testParkBeforeUnpark_parkBlocker() {
+        testParkBeforeUnpark(ParkMethod.parkBlocker);
+    }
+    public void testParkBeforeUnpark_parkNanosBlocker() {
+        testParkBeforeUnpark(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkBeforeUnpark_parkUntilBlocker() {
+        testParkBeforeUnpark(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkBeforeUnpark(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseUnpark = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                pleaseUnpark.countDown();
+                parkMethod.park();
+            }});
+
+        await(pleaseUnpark);
+        LockSupport.unpark(t);
+        awaitTermination(t);
+    }
+
+    /**
+     * park is released by preceding unpark
+     */
+    public void testParkAfterUnpark_park() {
+        testParkAfterUnpark(ParkMethod.park);
+    }
+    public void testParkAfterUnpark_parkNanos() {
+        testParkAfterUnpark(ParkMethod.parkNanos);
+    }
+    public void testParkAfterUnpark_parkUntil() {
+        testParkAfterUnpark(ParkMethod.parkUntil);
+    }
+    public void testParkAfterUnpark_parkBlocker() {
+        testParkAfterUnpark(ParkMethod.parkBlocker);
+    }
+    public void testParkAfterUnpark_parkNanosBlocker() {
+        testParkAfterUnpark(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkAfterUnpark_parkUntilBlocker() {
+        testParkAfterUnpark(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkAfterUnpark(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseUnpark = new CountDownLatch(1);
+        final AtomicBoolean pleasePark = new AtomicBoolean(false);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                pleaseUnpark.countDown();
+                while (!pleasePark.get())
+                    Thread.yield();
+                parkMethod.park();
+            }});
+
+        await(pleaseUnpark);
+        LockSupport.unpark(t);
+        pleasePark.set(true);
+        awaitTermination(t);
+    }
+
+    /**
+     * park is released by subsequent interrupt
+     */
+    public void testParkBeforeInterrupt_park() {
+        testParkBeforeInterrupt(ParkMethod.park);
+    }
+    public void testParkBeforeInterrupt_parkNanos() {
+        testParkBeforeInterrupt(ParkMethod.parkNanos);
+    }
+    public void testParkBeforeInterrupt_parkUntil() {
+        testParkBeforeInterrupt(ParkMethod.parkUntil);
+    }
+    public void testParkBeforeInterrupt_parkBlocker() {
+        testParkBeforeInterrupt(ParkMethod.parkBlocker);
+    }
+    public void testParkBeforeInterrupt_parkNanosBlocker() {
+        testParkBeforeInterrupt(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkBeforeInterrupt_parkUntilBlocker() {
+        testParkBeforeInterrupt(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkBeforeInterrupt(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                pleaseInterrupt.countDown();
+                do {
+                    parkMethod.park();
+                    // park may return spuriously
+                } while (! Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * park is released by preceding interrupt
+     */
+    public void testParkAfterInterrupt_park() {
+        testParkAfterInterrupt(ParkMethod.park);
+    }
+    public void testParkAfterInterrupt_parkNanos() {
+        testParkAfterInterrupt(ParkMethod.parkNanos);
+    }
+    public void testParkAfterInterrupt_parkUntil() {
+        testParkAfterInterrupt(ParkMethod.parkUntil);
+    }
+    public void testParkAfterInterrupt_parkBlocker() {
+        testParkAfterInterrupt(ParkMethod.parkBlocker);
+    }
+    public void testParkAfterInterrupt_parkNanosBlocker() {
+        testParkAfterInterrupt(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkAfterInterrupt_parkUntilBlocker() {
+        testParkAfterInterrupt(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkAfterInterrupt(final ParkMethod parkMethod) {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final AtomicBoolean pleasePark = new AtomicBoolean(false);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws Exception {
+                pleaseInterrupt.countDown();
+                while (!pleasePark.get())
+                    Thread.yield();
+                assertTrue(Thread.currentThread().isInterrupted());
+                parkMethod.park();
+                assertTrue(Thread.currentThread().isInterrupted());
+            }});
+
+        await(pleaseInterrupt);
+        t.interrupt();
+        pleasePark.set(true);
+        awaitTermination(t);
+    }
+
+    /**
+     * timed park times out if not unparked
+     */
+    public void testParkTimesOut_parkNanos() {
+        testParkTimesOut(ParkMethod.parkNanos);
+    }
+    public void testParkTimesOut_parkUntil() {
+        testParkTimesOut(ParkMethod.parkUntil);
+    }
+    public void testParkTimesOut_parkNanosBlocker() {
+        testParkTimesOut(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkTimesOut_parkUntilBlocker() {
+        testParkTimesOut(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkTimesOut(final ParkMethod parkMethod) {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                for (;;) {
+                    long startTime = System.nanoTime();
+                    parkMethod.park(timeoutMillis());
+                    // park may return spuriously
+                    if (millisElapsedSince(startTime) >= timeoutMillis())
+                        return;
+                }
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * getBlocker(null) throws NullPointerException
+     */
+    public void testGetBlockerNull() {
+        try {
+            LockSupport.getBlocker(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getBlocker returns the blocker object passed to park
+     */
+    public void testGetBlocker_parkBlocker() {
+        testGetBlocker(ParkMethod.parkBlocker);
+    }
+    public void testGetBlocker_parkNanosBlocker() {
+        testGetBlocker(ParkMethod.parkNanosBlocker);
+    }
+    public void testGetBlocker_parkUntilBlocker() {
+        testGetBlocker(ParkMethod.parkUntilBlocker);
+    }
+    public void testGetBlocker(final ParkMethod parkMethod) {
+        final CountDownLatch started = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread t = Thread.currentThread();
+                started.countDown();
+                do {
+                    assertNull(LockSupport.getBlocker(t));
+                    parkMethod.park();
+                    assertNull(LockSupport.getBlocker(t));
+                    // park may return spuriously
+                } while (! Thread.currentThread().isInterrupted());
+            }});
+
+        long startTime = System.nanoTime();
+        await(started);
+        for (;;) {
+            Object x = LockSupport.getBlocker(t);
+            if (x == theBlocker()) { // success
+                t.interrupt();
+                awaitTermination(t);
+                assertNull(LockSupport.getBlocker(t));
+                return;
+            } else {
+                assertNull(x);  // ok
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * timed park(0) returns immediately.
+     *
+     * Requires hotspot fix for:
+     * 6763959 java.util.concurrent.locks.LockSupport.parkUntil(0) blocks forever
+     * which is in jdk7-b118 and 6u25.
+     */
+    public void testPark0_parkNanos() {
+        testPark0(ParkMethod.parkNanos);
+    }
+    public void testPark0_parkUntil() {
+        testPark0(ParkMethod.parkUntil);
+    }
+    public void testPark0_parkNanosBlocker() {
+        testPark0(ParkMethod.parkNanosBlocker);
+    }
+    public void testPark0_parkUntilBlocker() {
+        testPark0(ParkMethod.parkUntilBlocker);
+    }
+    public void testPark0(final ParkMethod parkMethod) {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                parkMethod.park(0L);
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timed park(Long.MIN_VALUE) returns immediately.
+     */
+    public void testParkNeg_parkNanos() {
+        testParkNeg(ParkMethod.parkNanos);
+    }
+    public void testParkNeg_parkUntil() {
+        testParkNeg(ParkMethod.parkUntil);
+    }
+    public void testParkNeg_parkNanosBlocker() {
+        testParkNeg(ParkMethod.parkNanosBlocker);
+    }
+    public void testParkNeg_parkUntilBlocker() {
+        testParkNeg(ParkMethod.parkUntilBlocker);
+    }
+    public void testParkNeg(final ParkMethod parkMethod) {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                parkMethod.park(Long.MIN_VALUE);
+            }});
+
+        awaitTermination(t);
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LongAccumulatorTest.java b/ojluni/src/test/java/util/concurrent/tck/LongAccumulatorTest.java
new file mode 100644
index 0000000..36386fc
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LongAccumulatorTest.java
@@ -0,0 +1,184 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.LongAccumulator;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LongAccumulatorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(LongAccumulatorTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * accumulate accumulates given value to current, and get returns current value
+     */
+    public void testAccumulateAndGet() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        ai.accumulate(2);
+        assertEquals(2, ai.get());
+        ai.accumulate(-4);
+        assertEquals(2, ai.get());
+        ai.accumulate(4);
+        assertEquals(4, ai.get());
+    }
+
+    /**
+     * reset() causes subsequent get() to return zero
+     */
+    public void testReset() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        ai.accumulate(2);
+        assertEquals(2, ai.get());
+        ai.reset();
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * getThenReset() returns current value; subsequent get() returns zero
+     */
+    public void testGetThenReset() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        ai.accumulate(2);
+        assertEquals(2, ai.get());
+        assertEquals(2, ai.getThenReset());
+        assertEquals(0, ai.get());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals("0", ai.toString());
+        ai.accumulate(1);
+        assertEquals(Long.toString(1), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0, ai.intValue());
+        ai.accumulate(1);
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0, ai.longValue());
+        ai.accumulate(1);
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0.0f, ai.floatValue());
+        ai.accumulate(1);
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        LongAccumulator ai = new LongAccumulator(Long::max, 0L);
+        assertEquals(0.0, ai.doubleValue());
+        ai.accumulate(1);
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * accumulates by multiple threads produce correct result
+     */
+    public void testAccumulateAndGetMT() {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        LongAccumulator a = new LongAccumulator(Long::max, 0L);
+        Phaser phaser = new Phaser(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AccTask(a, phaser, incs));
+        phaser.arriveAndAwaitAdvance();
+        phaser.arriveAndAwaitAdvance();
+        long expected = incs - 1;
+        long result = a.get();
+        assertEquals(expected, result);
+        pool.shutdown();
+    }
+
+    static final class AccTask implements Runnable {
+        final LongAccumulator acc;
+        final Phaser phaser;
+        final int incs;
+        volatile long result;
+        AccTask(LongAccumulator acc, Phaser phaser, int incs) {
+            this.acc = acc;
+            this.phaser = phaser;
+            this.incs = incs;
+        }
+
+        public void run() {
+            phaser.arriveAndAwaitAdvance();
+            LongAccumulator a = acc;
+            for (int i = 0; i < incs; ++i)
+                a.accumulate(i);
+            result = a.get();
+            phaser.arrive();
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/LongAdderTest.java b/ojluni/src/test/java/util/concurrent/tck/LongAdderTest.java
new file mode 100644
index 0000000..d980185
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/LongAdderTest.java
@@ -0,0 +1,221 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class LongAdderTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(LongAdderTest.class);
+    }
+
+    /**
+     * default constructed initializes to zero
+     */
+    public void testConstructor() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0, ai.sum());
+    }
+
+    /**
+     * add adds given value to current, and sum returns current value
+     */
+    public void testAddAndSum() {
+        LongAdder ai = new LongAdder();
+        ai.add(2);
+        assertEquals(2, ai.sum());
+        ai.add(-4);
+        assertEquals(-2, ai.sum());
+    }
+
+    /**
+     * decrement decrements and sum returns current value
+     */
+    public void testDecrementAndsum() {
+        LongAdder ai = new LongAdder();
+        ai.decrement();
+        assertEquals(-1, ai.sum());
+        ai.decrement();
+        assertEquals(-2, ai.sum());
+    }
+
+    /**
+     * incrementAndGet increments and returns current value
+     */
+    public void testIncrementAndsum() {
+        LongAdder ai = new LongAdder();
+        ai.increment();
+        assertEquals(1, ai.sum());
+        ai.increment();
+        assertEquals(2, ai.sum());
+    }
+
+    /**
+     * reset() causes subsequent sum() to return zero
+     */
+    public void testReset() {
+        LongAdder ai = new LongAdder();
+        ai.add(2);
+        assertEquals(2, ai.sum());
+        ai.reset();
+        assertEquals(0, ai.sum());
+    }
+
+    /**
+     * sumThenReset() returns sum; subsequent sum() returns zero
+     */
+    public void testSumThenReset() {
+        LongAdder ai = new LongAdder();
+        ai.add(2);
+        assertEquals(2, ai.sum());
+        assertEquals(2, ai.sumThenReset());
+        assertEquals(0, ai.sum());
+    }
+
+    /**
+     * a deserialized serialized adder holds same value
+     */
+    public void testSerialization() throws Exception {
+        LongAdder x = new LongAdder();
+        LongAdder y = serialClone(x);
+        assertNotSame(x, y);
+        x.add(-22);
+        LongAdder z = serialClone(x);
+        assertNotSame(y, z);
+        assertEquals(-22, x.sum());
+        assertEquals(0, y.sum());
+        assertEquals(-22, z.sum());
+    }
+
+    /**
+     * toString returns current value.
+     */
+    public void testToString() {
+        LongAdder ai = new LongAdder();
+        assertEquals("0", ai.toString());
+        ai.increment();
+        assertEquals(Long.toString(1), ai.toString());
+    }
+
+    /**
+     * intValue returns current value.
+     */
+    public void testIntValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0, ai.intValue());
+        ai.increment();
+        assertEquals(1, ai.intValue());
+    }
+
+    /**
+     * longValue returns current value.
+     */
+    public void testLongValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0, ai.longValue());
+        ai.increment();
+        assertEquals(1, ai.longValue());
+    }
+
+    /**
+     * floatValue returns current value.
+     */
+    public void testFloatValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0.0f, ai.floatValue());
+        ai.increment();
+        assertEquals(1.0f, ai.floatValue());
+    }
+
+    /**
+     * doubleValue returns current value.
+     */
+    public void testDoubleValue() {
+        LongAdder ai = new LongAdder();
+        assertEquals(0.0, ai.doubleValue());
+        ai.increment();
+        assertEquals(1.0, ai.doubleValue());
+    }
+
+    /**
+     * adds by multiple threads produce correct sum
+     */
+    public void testAddAndSumMT() throws Throwable {
+        final int incs = 1000000;
+        final int nthreads = 4;
+        final ExecutorService pool = Executors.newCachedThreadPool();
+        LongAdder a = new LongAdder();
+        CyclicBarrier barrier = new CyclicBarrier(nthreads + 1);
+        for (int i = 0; i < nthreads; ++i)
+            pool.execute(new AdderTask(a, barrier, incs));
+        barrier.await();
+        barrier.await();
+        long total = (long)nthreads * incs;
+        long sum = a.sum();
+        assertEquals(sum, total);
+        pool.shutdown();
+    }
+
+    static final class AdderTask implements Runnable {
+        final LongAdder adder;
+        final CyclicBarrier barrier;
+        final int incs;
+        volatile long result;
+        AdderTask(LongAdder adder, CyclicBarrier barrier, int incs) {
+            this.adder = adder;
+            this.barrier = barrier;
+            this.incs = incs;
+        }
+
+        public void run() {
+            try {
+                barrier.await();
+                LongAdder a = adder;
+                for (int i = 0; i < incs; ++i)
+                    a.add(1L);
+                result = a.sum();
+                barrier.await();
+            } catch (Throwable t) { throw new Error(t); }
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/PhaserTest.java b/ojluni/src/test/java/util/concurrent/tck/PhaserTest.java
new file mode 100644
index 0000000..f6764c8
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/PhaserTest.java
@@ -0,0 +1,821 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include John Vint
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class PhaserTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(PhaserTest.class);
+    }
+
+    private static final int maxParties = 65535;
+
+    /** Checks state of unterminated phaser. */
+    protected void assertState(Phaser phaser,
+                               int phase, int parties, int unarrived) {
+        assertEquals(phase, phaser.getPhase());
+        assertEquals(parties, phaser.getRegisteredParties());
+        assertEquals(unarrived, phaser.getUnarrivedParties());
+        assertEquals(parties - unarrived, phaser.getArrivedParties());
+        assertFalse(phaser.isTerminated());
+    }
+
+    /** Checks state of terminated phaser. */
+    protected void assertTerminated(Phaser phaser, int maxPhase, int parties) {
+        assertTrue(phaser.isTerminated());
+        int expectedPhase = maxPhase + Integer.MIN_VALUE;
+        assertEquals(expectedPhase, phaser.getPhase());
+        assertEquals(parties, phaser.getRegisteredParties());
+        assertEquals(expectedPhase, phaser.register());
+        assertEquals(expectedPhase, phaser.arrive());
+        assertEquals(expectedPhase, phaser.arriveAndDeregister());
+    }
+
+    protected void assertTerminated(Phaser phaser, int maxPhase) {
+        assertTerminated(phaser, maxPhase, 0);
+    }
+
+    /**
+     * Empty constructor builds a new Phaser with no parent, no registered
+     * parties and initial phase number of 0
+     */
+    public void testConstructorDefaultValues() {
+        Phaser phaser = new Phaser();
+        assertNull(phaser.getParent());
+        assertEquals(0, phaser.getRegisteredParties());
+        assertEquals(0, phaser.getArrivedParties());
+        assertEquals(0, phaser.getUnarrivedParties());
+        assertEquals(0, phaser.getPhase());
+    }
+
+    /**
+     * Constructing with a negative number of parties throws
+     * IllegalArgumentException
+     */
+    public void testConstructorNegativeParties() {
+        try {
+            new Phaser(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructing with a negative number of parties throws
+     * IllegalArgumentException
+     */
+    public void testConstructorNegativeParties2() {
+        try {
+            new Phaser(new Phaser(), -1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructing with a number of parties > 65535 throws
+     * IllegalArgumentException
+     */
+    public void testConstructorPartiesExceedsLimit() {
+        new Phaser(maxParties);
+        try {
+            new Phaser(maxParties + 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+
+        new Phaser(new Phaser(), maxParties);
+        try {
+            new Phaser(new Phaser(), maxParties + 1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * The parent provided to the constructor should be returned from
+     * a later call to getParent
+     */
+    public void testConstructor3() {
+        Phaser parent = new Phaser();
+        assertSame(parent, new Phaser(parent).getParent());
+        assertNull(new Phaser(null).getParent());
+    }
+
+    /**
+     * The parent being input into the parameter should equal the original
+     * parent when being returned
+     */
+    public void testConstructor5() {
+        Phaser parent = new Phaser();
+        assertSame(parent, new Phaser(parent, 0).getParent());
+        assertNull(new Phaser(null, 0).getParent());
+    }
+
+    /**
+     * register() will increment the number of unarrived parties by
+     * one and not affect its arrived parties
+     */
+    public void testRegister1() {
+        Phaser phaser = new Phaser();
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.register());
+        assertState(phaser, 0, 1, 1);
+    }
+
+    /**
+     * Registering more than 65536 parties causes IllegalStateException
+     */
+    public void testRegister2() {
+        Phaser phaser = new Phaser(0);
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.bulkRegister(maxParties - 10));
+        assertState(phaser, 0, maxParties - 10, maxParties - 10);
+        for (int i = 0; i < 10; i++) {
+            assertState(phaser, 0, maxParties - 10 + i, maxParties - 10 + i);
+            assertEquals(0, phaser.register());
+        }
+        assertState(phaser, 0, maxParties, maxParties);
+        try {
+            phaser.register();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        try {
+            phaser.bulkRegister(Integer.MAX_VALUE);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        assertEquals(0, phaser.bulkRegister(0));
+        assertState(phaser, 0, maxParties, maxParties);
+    }
+
+    /**
+     * register() correctly returns the current barrier phase number
+     * when invoked
+     */
+    public void testRegister3() {
+        Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.register());
+        assertState(phaser, 1, 2, 2);
+    }
+
+    /**
+     * register causes the next arrive to not increment the phase
+     * rather retain the phase number
+     */
+    public void testRegister4() {
+        Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.register());
+        assertEquals(1, phaser.arrive());
+        assertState(phaser, 1, 2, 1);
+    }
+
+    /**
+     * register on a subphaser that is currently empty succeeds, even
+     * in the presence of another non-empty subphaser
+     */
+    public void testRegisterEmptySubPhaser() {
+        Phaser root = new Phaser();
+        Phaser child1 = new Phaser(root, 1);
+        Phaser child2 = new Phaser(root, 0);
+        assertEquals(0, child2.register());
+        assertState(root, 0, 2, 2);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 1, 1);
+        assertEquals(0, child2.arriveAndDeregister());
+        assertState(root, 0, 1, 1);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 0, 0);
+        assertEquals(0, child2.register());
+        assertEquals(0, child2.arriveAndDeregister());
+        assertState(root, 0, 1, 1);
+        assertState(child1, 0, 1, 1);
+        assertState(child2, 0, 0, 0);
+        assertEquals(0, child1.arriveAndDeregister());
+        assertTerminated(root, 1);
+        assertTerminated(child1, 1);
+        assertTerminated(child2, 1);
+    }
+
+    /**
+     * Invoking bulkRegister with a negative parameter throws an
+     * IllegalArgumentException
+     */
+    public void testBulkRegister1() {
+        try {
+            new Phaser().bulkRegister(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * bulkRegister should correctly record the number of unarrived
+     * parties with the number of parties being registered
+     */
+    public void testBulkRegister2() {
+        Phaser phaser = new Phaser();
+        assertEquals(0, phaser.bulkRegister(0));
+        assertState(phaser, 0, 0, 0);
+        assertEquals(0, phaser.bulkRegister(20));
+        assertState(phaser, 0, 20, 20);
+    }
+
+    /**
+     * Registering with a number of parties greater than or equal to 1<<16
+     * throws IllegalStateException.
+     */
+    public void testBulkRegister3() {
+        assertEquals(0, new Phaser().bulkRegister((1 << 16) - 1));
+
+        try {
+            new Phaser().bulkRegister(1 << 16);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+
+        try {
+            new Phaser(2).bulkRegister((1 << 16) - 2);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * the phase number increments correctly when tripping the barrier
+     */
+    public void testPhaseIncrement1() {
+        for (int size = 1; size < nine; size++) {
+            final Phaser phaser = new Phaser(size);
+            for (int index = 0; index <= (1 << size); index++) {
+                int phase = phaser.arrive();
+                assertTrue(index % size == 0 ? (index / size) == phase : index - (phase * size) > 0);
+            }
+        }
+    }
+
+    /**
+     * arrive() on a registered phaser increments phase.
+     */
+    public void testArrive1() {
+        Phaser phaser = new Phaser(1);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister does not wait for others to arrive at barrier
+     */
+    public void testArriveAndDeregister() {
+        final Phaser phaser = new Phaser(1);
+        for (int i = 0; i < 10; i++) {
+            assertState(phaser, 0, 1, 1);
+            assertEquals(0, phaser.register());
+            assertState(phaser, 0, 2, 2);
+            assertEquals(0, phaser.arriveAndDeregister());
+            assertState(phaser, 0, 1, 1);
+        }
+        assertEquals(0, phaser.arriveAndDeregister());
+        assertTerminated(phaser, 1);
+    }
+
+    /**
+     * arriveAndDeregister does not wait for others to arrive at barrier
+     */
+    public void testArrive2() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        List<Thread> threads = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            assertEquals(0, phaser.register());
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.arriveAndDeregister());
+                }}));
+        }
+
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arrive() returns a negative number if the Phaser is terminated
+     */
+    public void testArrive3() {
+        Phaser phaser = new Phaser(1);
+        phaser.forceTermination();
+        assertTerminated(phaser, 0, 1);
+        assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+        assertTrue(phaser.arrive() < 0);
+        assertTrue(phaser.register() < 0);
+        assertTrue(phaser.arriveAndDeregister() < 0);
+        assertTrue(phaser.awaitAdvance(1) < 0);
+        assertTrue(phaser.getPhase() < 0);
+    }
+
+    /**
+     * arriveAndDeregister() throws IllegalStateException if number of
+     * registered or unarrived parties would become negative
+     */
+    public void testArriveAndDeregister1() {
+        Phaser phaser = new Phaser();
+        try {
+            phaser.arriveAndDeregister();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * arriveAndDeregister reduces the number of arrived parties
+     */
+    public void testArriveAndDeregister2() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.register());
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 0, 2, 1);
+        assertEquals(0, phaser.arriveAndDeregister());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister arrives at the barrier on a phaser with a parent and
+     * when a deregistration occurs and causes the phaser to have zero parties
+     * its parent will be deregistered as well
+     */
+    public void testArriveAndDeregister3() {
+        Phaser parent = new Phaser();
+        Phaser child = new Phaser(parent);
+        assertState(child, 0, 0, 0);
+        assertState(parent, 0, 0, 0);
+        assertEquals(0, child.register());
+        assertState(child, 0, 1, 1);
+        assertState(parent, 0, 1, 1);
+        assertEquals(0, child.arriveAndDeregister());
+        assertTerminated(child, 1);
+        assertTerminated(parent, 1);
+    }
+
+    /**
+     * arriveAndDeregister deregisters one party from its parent when
+     * the number of parties of child is zero after deregistration
+     */
+    public void testArriveAndDeregister4() {
+        Phaser parent = new Phaser();
+        Phaser child = new Phaser(parent);
+        assertEquals(0, parent.register());
+        assertEquals(0, child.register());
+        assertState(child, 0, 1, 1);
+        assertState(parent, 0, 2, 2);
+        assertEquals(0, child.arriveAndDeregister());
+        assertState(child, 0, 0, 0);
+        assertState(parent, 0, 1, 1);
+    }
+
+    /**
+     * arriveAndDeregister deregisters one party from its parent when
+     * the number of parties of root is nonzero after deregistration.
+     */
+    public void testArriveAndDeregister5() {
+        Phaser root = new Phaser();
+        Phaser parent = new Phaser(root);
+        Phaser child = new Phaser(parent);
+        assertState(root, 0, 0, 0);
+        assertState(parent, 0, 0, 0);
+        assertState(child, 0, 0, 0);
+        assertEquals(0, child.register());
+        assertState(root, 0, 1, 1);
+        assertState(parent, 0, 1, 1);
+        assertState(child, 0, 1, 1);
+        assertEquals(0, child.arriveAndDeregister());
+        assertTerminated(child, 1);
+        assertTerminated(parent, 1);
+        assertTerminated(root, 1);
+    }
+
+    /**
+     * arriveAndDeregister returns the phase in which it leaves the
+     * phaser in after deregistration
+     */
+    public void testArriveAndDeregister6() {
+        final Phaser phaser = new Phaser(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.arrive());
+            }});
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertState(phaser, 1, 2, 2);
+        assertEquals(1, phaser.arriveAndDeregister());
+        assertState(phaser, 1, 1, 1);
+        assertEquals(1, phaser.arriveAndDeregister());
+        assertTerminated(phaser, 2);
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitAdvance succeeds upon advance
+     */
+    public void testAwaitAdvance1() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(0, phaser.arrive());
+        assertEquals(1, phaser.awaitAdvance(0));
+    }
+
+    /**
+     * awaitAdvance with a negative parameter will return without affecting the
+     * phaser
+     */
+    public void testAwaitAdvance2() {
+        Phaser phaser = new Phaser();
+        assertTrue(phaser.awaitAdvance(-1) < 0);
+        assertState(phaser, 0, 0, 0);
+    }
+
+    /**
+     * awaitAdvanceInterruptibly blocks interruptibly
+     */
+    public void testAwaitAdvanceInterruptibly_interruptible() throws InterruptedException {
+        final Phaser phaser = new Phaser(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws TimeoutException {
+                Thread.currentThread().interrupt();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    phaser.awaitAdvanceInterruptibly(0, 2*LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertState(phaser, 0, 1, 1);
+        assertThreadsStayAlive(t1, t2);
+        t1.interrupt();
+        t2.interrupt();
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertState(phaser, 0, 1, 1);
+        assertEquals(0, phaser.arrive());
+        assertState(phaser, 1, 1, 1);
+    }
+
+    /**
+     * awaitAdvance continues waiting if interrupted before waiting
+     */
+    public void testAwaitAdvanceAfterInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                assertEquals(0, phaser.register());
+                assertEquals(0, phaser.arrive());
+                pleaseArrive.countDown();
+                assertTrue(Thread.currentThread().isInterrupted());
+                assertEquals(1, phaser.awaitAdvance(0));
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t);
+        assertEquals(0, phaser.arrive());
+        awaitTermination(t);
+
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.awaitAdvance(0));
+        assertTrue(Thread.interrupted());
+    }
+
+    /**
+     *  awaitAdvance continues waiting if interrupted while waiting
+     */
+    public void testAwaitAdvanceBeforeInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.register());
+                assertEquals(0, phaser.arrive());
+                assertFalse(Thread.currentThread().isInterrupted());
+                pleaseArrive.countDown();
+                assertEquals(1, phaser.awaitAdvance(0));
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        assertEquals(0, phaser.arrive());
+        awaitTermination(t);
+
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.awaitAdvance(0));
+        assertTrue(Thread.interrupted());
+    }
+
+    /**
+     * arriveAndAwaitAdvance continues waiting if interrupted before waiting
+     */
+    public void testArriveAndAwaitAdvanceAfterInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseArrive = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                Thread.currentThread().interrupt();
+                assertEquals(0, phaser.register());
+                pleaseArrive.countDown();
+                assertTrue(Thread.currentThread().isInterrupted());
+                assertEquals(1, phaser.arriveAndAwaitAdvance());
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseArrive);
+        waitForThreadToEnterWaitState(t);
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertTrue(Thread.interrupted());
+        awaitTermination(t);
+    }
+
+    /**
+     * arriveAndAwaitAdvance continues waiting if interrupted while waiting
+     */
+    public void testArriveAndAwaitAdvanceBeforeInterrupt() {
+        final Phaser phaser = new Phaser();
+        assertEquals(0, phaser.register());
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0, phaser.register());
+                assertFalse(Thread.currentThread().isInterrupted());
+                pleaseInterrupt.countDown();
+                assertEquals(1, phaser.arriveAndAwaitAdvance());
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        Thread.currentThread().interrupt();
+        assertEquals(1, phaser.arriveAndAwaitAdvance());
+        assertTrue(Thread.interrupted());
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitAdvance atomically waits for all parties within the same phase to
+     * complete before continuing
+     */
+    public void testAwaitAdvance4() {
+        final Phaser phaser = new Phaser(4);
+        final AtomicInteger count = new AtomicInteger(0);
+        List<Thread> threads = new ArrayList<>();
+        for (int i = 0; i < 4; i++)
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    for (int k = 0; k < 3; k++) {
+                        assertEquals(2 * k + 1, phaser.arriveAndAwaitAdvance());
+                        count.incrementAndGet();
+                        assertEquals(2 * k + 1, phaser.arrive());
+                        assertEquals(2 * k + 2, phaser.awaitAdvance(2 * k + 1));
+                        assertEquals(4 * (k + 1), count.get());
+                    }}}));
+
+        for (Thread thread : threads)
+            awaitTermination(thread);
+    }
+
+    /**
+     * awaitAdvance returns the current phase
+     */
+    public void testAwaitAdvance5() {
+        final Phaser phaser = new Phaser(1);
+        assertEquals(1, phaser.awaitAdvance(phaser.arrive()));
+        assertEquals(1, phaser.getPhase());
+        assertEquals(1, phaser.register());
+        List<Thread> threads = new ArrayList<>();
+        for (int i = 0; i < 8; i++) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final boolean goesFirst = ((i & 1) == 0);
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    if (goesFirst)
+                        latch.countDown();
+                    else
+                        await(latch);
+                    phaser.arrive();
+                }}));
+            if (goesFirst)
+                await(latch);
+            else
+                latch.countDown();
+            assertEquals(i + 2, phaser.awaitAdvance(phaser.arrive()));
+            assertEquals(i + 2, phaser.getPhase());
+        }
+        for (Thread thread : threads)
+            awaitTermination(thread);
+    }
+
+    /**
+     * awaitAdvance returns the current phase in child phasers
+     */
+    public void testAwaitAdvanceTieredPhaser() throws Exception {
+        final Phaser parent = new Phaser();
+        final List<Phaser> zeroPartyChildren = new ArrayList<>(3);
+        final List<Phaser> onePartyChildren = new ArrayList<>(3);
+        for (int i = 0; i < 3; i++) {
+            zeroPartyChildren.add(new Phaser(parent, 0));
+            onePartyChildren.add(new Phaser(parent, 1));
+        }
+        final List<Phaser> phasers = new ArrayList<>();
+        phasers.addAll(zeroPartyChildren);
+        phasers.addAll(onePartyChildren);
+        phasers.add(parent);
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
+        }
+
+        for (Phaser child : onePartyChildren)
+            assertEquals(0, child.arrive());
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
+            assertEquals(1, phaser.awaitAdvance(0));
+            assertEquals(1, phaser.awaitAdvanceInterruptibly(0));
+            assertEquals(1, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS));
+        }
+
+        for (Phaser child : onePartyChildren)
+            assertEquals(1, child.arrive());
+        for (Phaser phaser : phasers) {
+            assertEquals(-42, phaser.awaitAdvance(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42));
+            assertEquals(-42, phaser.awaitAdvanceInterruptibly(-42, MEDIUM_DELAY_MS, MILLISECONDS));
+            assertEquals(2, phaser.awaitAdvance(0));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(0));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(0, MEDIUM_DELAY_MS, MILLISECONDS));
+            assertEquals(2, phaser.awaitAdvance(1));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(1));
+            assertEquals(2, phaser.awaitAdvanceInterruptibly(1, MEDIUM_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * awaitAdvance returns when the phaser is externally terminated
+     */
+    public void testAwaitAdvance6() {
+        final Phaser phaser = new Phaser(3);
+        final CountDownLatch pleaseForceTermination = new CountDownLatch(2);
+        final List<Thread> threads = new ArrayList<>();
+        for (int i = 0; i < 2; i++) {
+            Runnable r = new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.arrive());
+                    pleaseForceTermination.countDown();
+                    assertTrue(phaser.awaitAdvance(0) < 0);
+                    assertTrue(phaser.isTerminated());
+                    assertTrue(phaser.getPhase() < 0);
+                    assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+                    assertEquals(3, phaser.getRegisteredParties());
+                }};
+            threads.add(newStartedThread(r));
+        }
+        await(pleaseForceTermination);
+        phaser.forceTermination();
+        assertTrue(phaser.isTerminated());
+        assertEquals(0, phaser.getPhase() + Integer.MIN_VALUE);
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertEquals(3, phaser.getRegisteredParties());
+    }
+
+    /**
+     * arriveAndAwaitAdvance throws IllegalStateException with no
+     * unarrived parties
+     */
+    public void testArriveAndAwaitAdvance1() {
+        Phaser phaser = new Phaser();
+        try {
+            phaser.arriveAndAwaitAdvance();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * arriveAndAwaitAdvance waits for all threads to arrive, the
+     * number of arrived parties is the same number that is accounted
+     * for when the main thread awaitsAdvance
+     */
+    public void testArriveAndAwaitAdvance3() {
+        final Phaser phaser = new Phaser(1);
+        final int THREADS = 3;
+        final CountDownLatch pleaseArrive = new CountDownLatch(THREADS);
+        final List<Thread> threads = new ArrayList<>();
+        for (int i = 0; i < THREADS; i++)
+            threads.add(newStartedThread(new CheckedRunnable() {
+                public void realRun() {
+                    assertEquals(0, phaser.register());
+                    pleaseArrive.countDown();
+                    assertEquals(1, phaser.arriveAndAwaitAdvance());
+                }}));
+
+        await(pleaseArrive);
+        long startTime = System.nanoTime();
+        while (phaser.getArrivedParties() < THREADS)
+            Thread.yield();
+        assertEquals(THREADS, phaser.getArrivedParties());
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        for (Thread thread : threads)
+            waitForThreadToEnterWaitState(thread);
+        for (Thread thread : threads)
+            assertTrue(thread.isAlive());
+        assertState(phaser, 0, THREADS + 1, 1);
+        phaser.arriveAndAwaitAdvance();
+        for (Thread thread : threads)
+            awaitTermination(thread);
+        assertState(phaser, 1, THREADS + 1, THREADS + 1);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java
new file mode 100644
index 0000000..9aacce3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/PriorityBlockingQueueTest.java
@@ -0,0 +1,777 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.PriorityBlockingQueue;
+
+import junit.framework.Test;
+
+public class PriorityBlockingQueueTest extends JSR166TestCase {
+
+    public static class Generic extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new PriorityBlockingQueue();
+        }
+    }
+
+    public static class InitialCapacity extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new PriorityBlockingQueue(SIZE);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return PriorityBlockingQueue.class; }
+            public Collection emptyCollection() { return new PriorityBlockingQueue(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return true; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(PriorityBlockingQueueTest.class,
+                            new Generic().testSuite(),
+                            new InitialCapacity().testSuite(),
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    /** Sample Comparator */
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private PriorityBlockingQueue<Integer> populatedQueue(int n) {
+        PriorityBlockingQueue<Integer> q =
+            new PriorityBlockingQueue<Integer>(n);
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.offer(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        assertEquals(n, q.size());
+        assertEquals((Integer) 0, q.peek());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(Integer.MAX_VALUE,
+                     new PriorityBlockingQueue(SIZE).remainingCapacity());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new PriorityBlockingQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new PriorityBlockingQueue(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        Collection<Integer> elements = Arrays.asList(new Integer[SIZE]);
+        try {
+            new PriorityBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = i;
+        Collection<Integer> elements = Arrays.asList(ints);
+        try {
+            new PriorityBlockingQueue(elements);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = i;
+        PriorityBlockingQueue q = new PriorityBlockingQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE, cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        assertTrue(q.isEmpty());
+        assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        q.add(two);
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * remainingCapacity() always returns Integer.MAX_VALUE
+     */
+    public void testRemainingCapacity() {
+        BlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(SIZE - i, q.size());
+            assertEquals(i, q.remove());
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(Integer.MAX_VALUE, q.remainingCapacity());
+            assertEquals(i, q.size());
+            assertTrue(q.add(i));
+        }
+    }
+
+    /**
+     * Offer of comparable element succeeds
+     */
+    public void testOffer() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(1);
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * Offer of non-Comparable throws CCE
+     */
+    public void testOfferNonComparable() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(1);
+        try {
+            q.offer(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {
+            assertTrue(q.isEmpty());
+            assertEquals(0, q.size());
+            assertNull(q.poll());
+        }
+    }
+
+    /**
+     * add of comparable succeeds
+     */
+    public void testAdd() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(this) throws IAE
+     */
+    public void testAddAllSelf() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = SIZE - 1; i >= 0; --i)
+            ints[i] = new Integer(i);
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * all elements successfully put are contained
+     */
+    public void testPut() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            Integer x = new Integer(i);
+            q.put(x);
+            assertTrue(q.contains(x));
+        }
+        assertEquals(SIZE, q.size());
+    }
+
+    /**
+     * put doesn't block waiting for take
+     */
+    public void testPutWithTake() throws InterruptedException {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        final int size = 4;
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                for (int i = 0; i < size; i++)
+                    q.put(new Integer(0));
+            }});
+
+        awaitTermination(t);
+        assertEquals(size, q.size());
+        q.take();
+    }
+
+    /**
+     * timed offer does not time out
+     */
+    public void testTimedOffer() throws InterruptedException {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new Integer(0));
+                q.put(new Integer(0));
+                assertTrue(q.offer(new Integer(0), SHORT_DELAY_MS, MILLISECONDS));
+                assertTrue(q.offer(new Integer(0), LONG_DELAY_MS, MILLISECONDS));
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * take retrieves elements in priority order
+     */
+    public void testTake() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.take());
+        }
+    }
+
+    /**
+     * Take removes existing elements until empty, then blocks interruptibly
+     */
+    public void testBlockingTake() throws InterruptedException {
+        final PriorityBlockingQueue q = populatedQueue(SIZE);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, q.take());
+                }
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.take();
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll0() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll(0, MILLISECONDS));
+        }
+        assertNull(q.poll(0, MILLISECONDS));
+    }
+
+    /**
+     * timed poll with nonzero timeout succeeds when non-empty, else times out
+     */
+    public void testTimedPoll() throws InterruptedException {
+        PriorityBlockingQueue<Integer> q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            long startTime = System.nanoTime();
+            assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+        long startTime = System.nanoTime();
+        assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        checkEmpty(q);
+    }
+
+    /**
+     * Interrupted timed poll throws InterruptedException instead of
+     * returning timeout status
+     */
+    public void testInterruptedTimedPoll() throws InterruptedException {
+        final BlockingQueue<Integer> q = populatedQueue(SIZE);
+        final CountDownLatch aboutToWait = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                for (int i = 0; i < SIZE; ++i) {
+                    assertEquals(i, (int) q.poll(LONG_DELAY_MS, MILLISECONDS));
+                }
+                aboutToWait.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {
+                    assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+                }
+            }});
+
+        aboutToWait.await();
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(one));
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        PriorityBlockingQueue p = new PriorityBlockingQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        PriorityBlockingQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            PriorityBlockingQueue q = populatedQueue(SIZE);
+            PriorityBlockingQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() throws InterruptedException {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.take());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() throws InterruptedException {
+        PriorityBlockingQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.take());
+    }
+
+    /**
+     * toArray(incompatible array type) throws ArrayStoreException
+     */
+    public void testToArray1_BadArg() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        try {
+            q.toArray(new String[10]);
+            shouldThrow();
+        } catch (ArrayStoreException success) {}
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new PriorityBlockingQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(3);
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * timed poll transfers elements across Executor tasks
+     */
+    public void testPollInExecutor() {
+        final PriorityBlockingQueue q = new PriorityBlockingQueue(2);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    checkEmpty(q);
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * drainTo(c) empties queue into another collection c
+     */
+    public void testDrainTo() {
+        PriorityBlockingQueue q = populatedQueue(SIZE);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(SIZE, l.size());
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        q.add(zero);
+        q.add(one);
+        assertFalse(q.isEmpty());
+        assertTrue(q.contains(zero));
+        assertTrue(q.contains(one));
+        l.clear();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(2, l.size());
+        for (int i = 0; i < 2; ++i)
+            assertEquals(l.get(i), new Integer(i));
+    }
+
+    /**
+     * drainTo empties queue
+     */
+    public void testDrainToWithActivePut() throws InterruptedException {
+        final PriorityBlockingQueue q = populatedQueue(SIZE);
+        Thread t = new Thread(new CheckedRunnable() {
+            public void realRun() {
+                q.put(new Integer(SIZE + 1));
+            }});
+
+        t.start();
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertTrue(l.size() >= SIZE);
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(l.get(i), new Integer(i));
+        t.join();
+        assertTrue(q.size() + l.size() >= SIZE);
+    }
+
+    /**
+     * drainTo(c, n) empties first min(n, size) elements of queue into c
+     */
+    public void testDrainToN() {
+        PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE * 2);
+        for (int i = 0; i < SIZE + 2; ++i) {
+            for (int j = 0; j < SIZE; j++)
+                assertTrue(q.offer(new Integer(j)));
+            ArrayList l = new ArrayList();
+            q.drainTo(l, i);
+            int k = (i < SIZE) ? i : SIZE;
+            assertEquals(k, l.size());
+            assertEquals(SIZE - k, q.size());
+            for (int j = 0; j < k; ++j)
+                assertEquals(l.get(j), new Integer(j));
+            do {} while (q.poll() != null);
+        }
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?>[] qs = {
+            new PriorityBlockingQueue<Object>(),
+            populatedQueue(2),
+        };
+
+        for (Collection<?> q : qs) {
+            assertFalse(q.contains(null));
+            assertFalse(q.remove(null));
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/PriorityQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/PriorityQueueTest.java
new file mode 100644
index 0000000..072abac
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/PriorityQueueTest.java
@@ -0,0 +1,541 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.PriorityQueue;
+import java.util.Queue;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class PriorityQueueTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return PriorityQueue.class; }
+            public Collection emptyCollection() { return new PriorityQueue(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return false; }
+            public boolean permitsNulls() { return false; }
+        }
+        return newTestSuite(PriorityQueueTest.class,
+                            CollectionTest.testSuite(new Implementation()));
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new queue of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private PriorityQueue<Integer> populatedQueue(int n) {
+        PriorityQueue<Integer> q = new PriorityQueue<>(n);
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.offer(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.offer(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        assertEquals((Integer) 0, q.peek());
+        return q;
+    }
+
+    /**
+     * A new queue has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new PriorityQueue(SIZE).size());
+    }
+
+    /**
+     * Constructor throws IAE if capacity argument nonpositive
+     */
+    public void testConstructor2() {
+        try {
+            new PriorityQueue(0);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new PriorityQueue((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new PriorityQueue(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new PriorityQueue(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        PriorityQueue q = new PriorityQueue(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        PriorityQueue q = new PriorityQueue(SIZE, cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.poll());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        PriorityQueue q = new PriorityQueue(2);
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.remove();
+        q.remove();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.remove();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * offer(null) throws NPE
+     */
+    public void testOfferNull() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.offer(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Offer of comparable element succeeds
+     */
+    public void testOffer() {
+        PriorityQueue q = new PriorityQueue(1);
+        assertTrue(q.offer(zero));
+        assertTrue(q.offer(one));
+    }
+
+    /**
+     * Offer of non-Comparable throws CCE
+     */
+    public void testOfferNonComparable() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.offer(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {
+            assertTrue(q.isEmpty());
+            assertEquals(0, q.size());
+            assertNull(q.poll());
+        }
+    }
+
+    /**
+     * add of comparable succeeds
+     */
+    public void testAdd() {
+        PriorityQueue q = new PriorityQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            assertTrue(q.add(new Integer(i)));
+        }
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        PriorityQueue q = new PriorityQueue(1);
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        PriorityQueue q = new PriorityQueue(SIZE);
+        try {
+            q.addAll(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        PriorityQueue q = new PriorityQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Queue contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        PriorityQueue q = new PriorityQueue(SIZE);
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.poll());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.poll());
+        }
+        assertNull(q.poll());
+    }
+
+    /**
+     * peek returns next element, or null if empty
+     */
+    public void testPeek() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.peek());
+            assertEquals(i, q.poll());
+            assertTrue(q.peek() == null ||
+                       !q.peek().equals(i));
+        }
+        assertNull(q.peek());
+    }
+
+    /**
+     * element returns next element, or throws NSEE if empty
+     */
+    public void testElement() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.element());
+            assertEquals(i, q.poll());
+        }
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove removes next element, or throws NSEE if empty
+     */
+    public void testRemove() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.remove());
+        }
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        PriorityQueue q = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.poll();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        PriorityQueue q = populatedQueue(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        PriorityQueue p = new PriorityQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        PriorityQueue q = populatedQueue(SIZE);
+        PriorityQueue p = populatedQueue(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.remove();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            PriorityQueue q = populatedQueue(SIZE);
+            PriorityQueue p = populatedQueue(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.remove());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testToArray() {
+        PriorityQueue q = populatedQueue(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.poll());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testToArray2() {
+        PriorityQueue<Integer> q = populatedQueue(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.poll());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        PriorityQueue q = populatedQueue(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty collection has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new PriorityQueue().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final PriorityQueue q = new PriorityQueue(3);
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        PriorityQueue q = populatedQueue(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized queue has same elements
+     */
+    public void testSerialization() throws Exception {
+        Queue x = populatedQueue(SIZE);
+        Queue y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.remove(), y.remove());
+        }
+        assertTrue(y.isEmpty());
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/RecursiveActionTest.java b/ojluni/src/test/java/util/concurrent/tck/RecursiveActionTest.java
new file mode 100644
index 0000000..7ec6cc8
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/RecursiveActionTest.java
@@ -0,0 +1,1261 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
+import java.util.concurrent.RecursiveAction;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class RecursiveActionTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(RecursiveActionTest.class);
+    }
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool();
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private void testInvokeOnPool(ForkJoinPool pool, RecursiveAction a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            checkNotDone(a);
+
+            assertNull(pool.invoke(a));
+
+            checkCompletedNormally(a);
+        }
+    }
+
+    void checkNotDone(RecursiveAction a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedNormally(RecursiveAction a) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+        assertNull(a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertNull(a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertNull(a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCancelled(RecursiveAction a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(RecursiveAction a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(expected.getClass(), t.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+        public FJException(Throwable cause) { super(cause); }
+    }
+
+    // A simple recursive action for testing
+    final class FibAction extends CheckedRecursiveAction {
+        final int number;
+        int result;
+        FibAction(int n) { number = n; }
+        protected void realCompute() {
+            int n = number;
+            if (n <= 1)
+                result = n;
+            else {
+                FibAction f1 = new FibAction(n - 1);
+                FibAction f2 = new FibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    // A recursive action failing in base case
+    static final class FailingFibAction extends RecursiveAction {
+        final int number;
+        int result;
+        FailingFibAction(int n) { number = n; }
+        public void compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            else {
+                FailingFibAction f1 = new FailingFibAction(n - 1);
+                FailingFibAction f2 = new FailingFibAction(n - 2);
+                invokeAll(f1, f2);
+                result = f1.result + f2.result;
+            }
+        }
+    }
+
+    /**
+     * invoke returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a RecursiveAction returns null;
+     */
+    public void testInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertNull(f.invoke());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.quietlyInvoke();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.join());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterrupts() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                final Thread currentThread = Thread.currentThread();
+
+                // test join()
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                assertNull(f.join());
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    Thread.interrupted();
+                    checkCancelled(f);
+                }
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    Thread.interrupted();
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin()
+                f = new FibAction(8);
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                f.quietlyJoin();
+                Thread.interrupted();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = new FibAction(8);
+                f.cancel(true);
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCancelled(f);
+
+                f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                assertSame(f, f.fork());
+                currentThread.interrupt();
+                f.quietlyJoin();
+                Thread.interrupted();
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+        a.reinitialize();
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * join/quietlyJoin of a forked task when not in ForkJoinPool
+     * succeeds in the presence of interrupts
+     */
+    public void testJoinIgnoresInterruptsOutsideForkJoinPool() {
+        final SynchronousQueue<FibAction[]> sq =
+            new SynchronousQueue<FibAction[]>();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws InterruptedException {
+                FibAction[] fibActions = new FibAction[6];
+                for (int i = 0; i < fibActions.length; i++)
+                    fibActions[i] = new FibAction(8);
+
+                fibActions[1].cancel(false);
+                fibActions[2].completeExceptionally(new FJException());
+                fibActions[4].cancel(true);
+                fibActions[5].completeExceptionally(new FJException());
+
+                for (int i = 0; i < fibActions.length; i++)
+                    fibActions[i].fork();
+
+                sq.put(fibActions);
+
+                helpQuiesce();
+            }};
+
+        Runnable r = new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                FibAction[] fibActions = sq.take();
+                FibAction f;
+                final Thread currentThread = Thread.currentThread();
+
+                // test join() ------------
+
+                f = fibActions[0];
+                assertFalse(ForkJoinTask.inForkJoinPool());
+                currentThread.interrupt();
+                assertNull(f.join());
+                assertTrue(Thread.interrupted());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = fibActions[1];
+                currentThread.interrupt();
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    assertTrue(Thread.interrupted());
+                    checkCancelled(f);
+                }
+
+                f = fibActions[2];
+                currentThread.interrupt();
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    assertTrue(Thread.interrupted());
+                    checkCompletedAbnormally(f, success);
+                }
+
+                // test quietlyJoin() ---------
+
+                f = fibActions[3];
+                currentThread.interrupt();
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+
+                f = fibActions[4];
+                currentThread.interrupt();
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                checkCancelled(f);
+
+                f = fibActions[5];
+                currentThread.interrupt();
+                f.quietlyJoin();
+                assertTrue(Thread.interrupted());
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+
+        Thread t;
+
+        t = newStartedThread(r);
+        testInvokeOnPool(mainPool(), a);
+        awaitTermination(t);
+
+        a.reinitialize();
+        t = newStartedThread(r);
+        testInvokeOnPool(singletonPool(), a);
+        awaitTermination(t);
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get());
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertNull(f.get(5L, SECONDS));
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get with null time unit throws NPE
+     */
+    public void testForkTimedGetNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, null);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertEquals(21, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                while (!f.isDone()) // wait out race
+                    ;
+                assertEquals(21, f.result);
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() throws Exception {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertSame(mainPool, getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertNull(getPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertTrue(inForkJoinPool());
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                assertFalse(inForkJoinPool());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * getPool of current thread in pool returns its pool
+     */
+    public void testWorkerGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                ForkJoinWorkerThread w =
+                    (ForkJoinWorkerThread) Thread.currentThread();
+                assertSame(mainPool, w.getPool());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * getPoolIndex of current thread in pool returns 0 <= value < poolSize
+     */
+    public void testWorkerGetPoolIndex() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                ForkJoinWorkerThread w =
+                    (ForkJoinWorkerThread) Thread.currentThread();
+                assertTrue(w.getPoolIndex() >= 0);
+                // pool size can shrink after assigning index, so cannot check
+                // assertTrue(w.getPoolIndex() < mainPool.getPoolSize());
+            }};
+        testInvokeOnPool(mainPool, a);
+    }
+
+    /**
+     * setRawResult(null) succeeds
+     */
+    public void testSetRawResult() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                setRawResult(null);
+                assertNull(getRawResult());
+            }};
+        assertNull(a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    assertNull(f.invoke());
+                    assertEquals(21, f.result);
+                    checkCompletedNormally(f);
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                f.complete(null);
+                assertNull(f.invoke());
+                assertEquals(0, f.result);
+                checkCompletedNormally(f);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                invokeAll(f);
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f);
+                assertEquals(21, f.result);
+                checkCompletedNormally(g);
+                assertEquals(34, g.result);
+                checkCompletedNormally(g);
+                assertEquals(13, h.result);
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction g = new FailingFibAction(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction f = new FibAction(8);
+                FailingFibAction g = new FailingFibAction(9);
+                FibAction h = new FibAction(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FailingFibAction f = new FailingFibAction(8);
+                FibAction g = new FibAction(9);
+                FibAction h = new FibAction(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+            }};
+        testInvokeOnPool(mainPool(), a);
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction h = new FibAction(7);
+                assertSame(h, h.fork());
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+                checkCompletedNormally(h);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                assertNull(f.join());
+                checkCompletedNormally(f);
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task
+     * without executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(singletonPool(), a);
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertNull(f.join());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkCompletedNormally(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveAction a = new CheckedRecursiveAction() {
+            protected void realCompute() {
+                FibAction g = new FibAction(9);
+                assertSame(g, g.fork());
+                FibAction f = new FibAction(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                checkCompletedNormally(f);
+                checkNotDone(g);
+            }};
+        testInvokeOnPool(asyncSingletonPool(), a);
+    }
+
+    /** Demo from RecursiveAction javadoc */
+    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:
+        static final int THRESHOLD = 100;
+        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++];
+        }
+    }
+
+    /**
+     * SortTask demo works as advertised
+     */
+    public void testSortTaskDemo() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        long[] array = new long[1007];
+        for (int i = 0; i < array.length; i++)
+            array[i] = rnd.nextLong();
+        long[] arrayClone = array.clone();
+        testInvokeOnPool(mainPool(), new SortTask(array));
+        Arrays.sort(arrayClone);
+        assertTrue(Arrays.equals(array, arrayClone));
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/RecursiveTaskTest.java b/ojluni/src/test/java/util/concurrent/tck/RecursiveTaskTest.java
new file mode 100644
index 0000000..39b3010
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/RecursiveTaskTest.java
@@ -0,0 +1,1054 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.HashSet;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveTask;
+import java.util.concurrent.TimeoutException;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class RecursiveTaskTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(RecursiveTaskTest.class);
+    }
+
+    private static ForkJoinPool mainPool() {
+        return new ForkJoinPool();
+    }
+
+    private static ForkJoinPool singletonPool() {
+        return new ForkJoinPool(1);
+    }
+
+    private static ForkJoinPool asyncSingletonPool() {
+        return new ForkJoinPool(1,
+                                ForkJoinPool.defaultForkJoinWorkerThreadFactory,
+                                null, true);
+    }
+
+    private <T> T testInvokeOnPool(ForkJoinPool pool, RecursiveTask<T> a) {
+        try (PoolCleaner cleaner = cleaner(pool)) {
+            checkNotDone(a);
+
+            T result = pool.invoke(a);
+
+            checkCompletedNormally(a, result);
+            return result;
+        }
+    }
+
+    void checkNotDone(RecursiveTask a) {
+        assertFalse(a.isDone());
+        assertFalse(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertFalse(a.isCancelled());
+        assertNull(a.getException());
+        assertNull(a.getRawResult());
+
+        if (! ForkJoinTask.inForkJoinPool()) {
+            Thread.currentThread().interrupt();
+            try {
+                a.get();
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+            Thread.currentThread().interrupt();
+            try {
+                a.get(5L, SECONDS);
+                shouldThrow();
+            } catch (InterruptedException success) {
+            } catch (Throwable fail) { threadUnexpectedException(fail); }
+        }
+
+        try {
+            a.get(0L, SECONDS);
+            shouldThrow();
+        } catch (TimeoutException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    <T> void checkCompletedNormally(RecursiveTask<T> a, T expected) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertTrue(a.isCompletedNormally());
+        assertFalse(a.isCompletedAbnormally());
+        assertNull(a.getException());
+        assertSame(expected, a.getRawResult());
+        assertSame(expected, a.join());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+        try {
+            assertSame(expected, a.get());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+        try {
+            assertSame(expected, a.get(5L, SECONDS));
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * Waits for the task to complete, and checks that when it does,
+     * it will have an Integer result equals to the given int.
+     */
+    void checkCompletesNormally(RecursiveTask<Integer> a, int expected) {
+        Integer r = a.join();
+        assertEquals(expected, (int) r);
+        checkCompletedNormally(a, r);
+    }
+
+    /**
+     * Like checkCompletesNormally, but verifies that the task has
+     * already completed.
+     */
+    void checkCompletedNormally(RecursiveTask<Integer> a, int expected) {
+        Integer r = a.getRawResult();
+        assertEquals(expected, (int) r);
+        checkCompletedNormally(a, r);
+    }
+
+    void checkCancelled(RecursiveTask a) {
+        assertTrue(a.isDone());
+        assertTrue(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertTrue(a.getException() instanceof CancellationException);
+        assertNull(a.getRawResult());
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (CancellationException success) {
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    void checkCompletedAbnormally(RecursiveTask a, Throwable t) {
+        assertTrue(a.isDone());
+        assertFalse(a.isCancelled());
+        assertFalse(a.isCompletedNormally());
+        assertTrue(a.isCompletedAbnormally());
+        assertSame(t.getClass(), a.getException().getClass());
+        assertNull(a.getRawResult());
+        assertFalse(a.cancel(false));
+        assertFalse(a.cancel(true));
+
+        try {
+            a.join();
+            shouldThrow();
+        } catch (Throwable expected) {
+            assertSame(t.getClass(), expected.getClass());
+        }
+
+        try {
+            a.get();
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+
+        try {
+            a.get(5L, SECONDS);
+            shouldThrow();
+        } catch (ExecutionException success) {
+            assertSame(t.getClass(), success.getCause().getClass());
+        } catch (Throwable fail) { threadUnexpectedException(fail); }
+    }
+
+    public static final class FJException extends RuntimeException {
+        public FJException() { super(); }
+    }
+
+    // An invalid return value for Fib
+    static final Integer NoResult = Integer.valueOf(-17);
+
+    // A simple recursive task for testing
+    final class FibTask extends CheckedRecursiveTask<Integer> {
+        final int number;
+        FibTask(int n) { number = n; }
+        public Integer realCompute() {
+            int n = number;
+            if (n <= 1)
+                return n;
+            FibTask f1 = new FibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+
+        public void publicSetRawResult(Integer result) {
+            setRawResult(result);
+        }
+    }
+
+    // A recursive action failing in base case
+    final class FailingFibTask extends RecursiveTask<Integer> {
+        final int number;
+        int result;
+        FailingFibTask(int n) { number = n; }
+        public Integer compute() {
+            int n = number;
+            if (n <= 1)
+                throw new FJException();
+            FailingFibTask f1 = new FailingFibTask(n - 1);
+            f1.fork();
+            return (new FibTask(n - 2)).compute() + f1.join();
+        }
+    }
+
+    /**
+     * invoke returns value when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks. getRawResult of a completed non-null task
+     * returns value;
+     */
+    public void testInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                Integer r = f.invoke();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes normally.
+     * isCompletedAbnormally and isCancelled return false for normally
+     * completed tasks
+     */
+    public void testQuietlyInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.quietlyInvoke();
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task returns when task completes
+     */
+    public void testForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.join();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task returns when task completes
+     */
+    public void testForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.get();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task returns when task completes
+     */
+    public void testForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                Integer r = f.get(5L, SECONDS);
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes
+     */
+    public void testForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                Integer r = f.getRawResult();
+                assertEquals(21, (int) r);
+                checkCompletedNormally(f, r);
+                return r;
+            }};
+        assertEquals(21, (int) testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * helpQuiesce returns when tasks are complete.
+     * getQueuedTaskCount returns 0 when quiescent
+     */
+    public void testForkHelpQuiesce() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                helpQuiesce();
+                while (!f.isDone()) // wait out race
+                    ;
+                assertEquals(0, getQueuedTaskCount());
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception when task completes abnormally
+     */
+    public void testAbnormalInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                try {
+                    f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyInvoke task returns when task completes abnormally
+     */
+    public void testAbnormalQuietlyInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                f.quietlyInvoke();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.join();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get();
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task throws exception when task completes abnormally
+     */
+    public void testAbnormalForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (ExecutionException success) {
+                    Throwable cause = success.getCause();
+                    assertTrue(cause instanceof FJException);
+                    checkCompletedAbnormally(f, cause);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task completes abnormally
+     */
+    public void testAbnormalForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                assertTrue(f.getException() instanceof FJException);
+                checkCompletedAbnormally(f, f.getException());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception when task cancelled
+     */
+    public void testCancelledInvoke() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                try {
+                    Integer r = f.invoke();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * join of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.join();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get();
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * timed get of a forked task throws exception when task cancelled
+     */
+    public void testCancelledForkTimedGet() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() throws Exception {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                try {
+                    Integer r = f.get(5L, SECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {
+                    checkCancelled(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * quietlyJoin of a forked task returns when task cancelled
+     */
+    public void testCancelledForkQuietlyJoin() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                assertTrue(f.cancel(true));
+                assertSame(f, f.fork());
+                f.quietlyJoin();
+                checkCancelled(f);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * getPool of executing task returns its pool
+     */
+    public void testGetPool() {
+        final ForkJoinPool mainPool = mainPool();
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertSame(mainPool, getPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool, a));
+    }
+
+    /**
+     * getPool of non-FJ task returns null
+     */
+    public void testGetPool2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertNull(getPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * inForkJoinPool of executing task returns true
+     */
+    public void testInForkJoinPool() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertTrue(inForkJoinPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * inForkJoinPool of non-FJ task returns false
+     */
+    public void testInForkJoinPool2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                assertFalse(inForkJoinPool());
+                return NoResult;
+            }};
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * The value set by setRawResult is returned by getRawResult
+     */
+    public void testSetRawResult() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                setRawResult(NoResult);
+                assertSame(NoResult, getRawResult());
+                return NoResult;
+            }
+        };
+        assertSame(NoResult, a.invoke());
+    }
+
+    /**
+     * A reinitialized normally completed task may be re-invoked
+     */
+    public void testReinitialize() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    Integer r = f.invoke();
+                    assertEquals(21, (int) r);
+                    checkCompletedNormally(f, r);
+                    f.reinitialize();
+                    f.publicSetRawResult(null);
+                    checkNotDone(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * A reinitialized abnormally completed task may be re-invoked
+     */
+    public void testReinitializeAbnormal() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                checkNotDone(f);
+
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        f.invoke();
+                        shouldThrow();
+                    } catch (FJException success) {
+                        checkCompletedAbnormally(f, success);
+                    }
+                    f.reinitialize();
+                    checkNotDone(f);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task throws exception after invoking completeExceptionally
+     */
+    public void testCompleteExceptionally() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.completeExceptionally(new FJException());
+                try {
+                    Integer r = f.invoke();
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invoke task suppresses execution invoking complete
+     */
+    public void testComplete() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                f.complete(NoResult);
+                Integer r = f.invoke();
+                assertSame(NoResult, r);
+                checkCompletedNormally(f, NoResult);
+                return r;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(t1, t2) invokes all task arguments
+     */
+    public void testInvokeAll2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                invokeAll(f, g);
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument invokes task
+     */
+    public void testInvokeAll1() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                invokeAll(f);
+                checkCompletedNormally(f, 21);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument invokes tasks
+     */
+    public void testInvokeAll3() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                invokeAll(f, g, h);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(collection) invokes all tasks in the collection
+     */
+    public void testInvokeAllCollection() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                invokeAll(set);
+                assertTrue(f.isDone());
+                assertTrue(g.isDone());
+                assertTrue(h.isDone());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with any null task throws NPE
+     */
+    public void testInvokeAllNPE() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = null;
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (NullPointerException success) {}
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(t1, t2) throw exception if any task does
+     */
+    public void testAbnormalInvokeAll2() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FailingFibTask g = new FailingFibTask(9);
+                try {
+                    invokeAll(f, g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with 1 argument throws exception if task does
+     */
+    public void testAbnormalInvokeAll1() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask g = new FailingFibTask(9);
+                try {
+                    invokeAll(g);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(tasks) with > 2 argument throws exception if any task does
+     */
+    public void testAbnormalInvokeAll3() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask f = new FibTask(8);
+                FailingFibTask g = new FailingFibTask(9);
+                FibTask h = new FibTask(7);
+                try {
+                    invokeAll(f, g, h);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(g, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * invokeAll(collection) throws exception if any task does
+     */
+    public void testAbnormalInvokeAllCollection() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FailingFibTask f = new FailingFibTask(8);
+                FibTask g = new FibTask(9);
+                FibTask h = new FibTask(7);
+                HashSet set = new HashSet();
+                set.add(f);
+                set.add(g);
+                set.add(h);
+                try {
+                    invokeAll(set);
+                    shouldThrow();
+                } catch (FJException success) {
+                    checkCompletedAbnormally(f, success);
+                }
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(mainPool(), a));
+    }
+
+    /**
+     * tryUnfork returns true for most recent unexecuted task,
+     * and suppresses execution
+     */
+    public void testTryUnfork() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertTrue(f.tryUnfork());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * getSurplusQueuedTaskCount returns > 0 when
+     * there are more tasks than threads
+     */
+    public void testGetSurplusQueuedTaskCount() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask h = new FibTask(7);
+                assertSame(h, h.fork());
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertTrue(getSurplusQueuedTaskCount() > 0);
+                helpQuiesce();
+                assertEquals(0, getSurplusQueuedTaskCount());
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                checkCompletedNormally(h, 13);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * peekNextLocalTask returns most recent unexecuted task.
+     */
+    public void testPeekNextLocalTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, peekNextLocalTask());
+                checkCompletesNormally(f, 21);
+                helpQuiesce();
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * pollNextLocalTask returns most recent unexecuted task
+     * without executing it
+     */
+    public void testPollNextLocalTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollNextLocalTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it
+     */
+    public void testPollTask() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(f, pollTask());
+                helpQuiesce();
+                checkNotDone(f);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(singletonPool(), a));
+    }
+
+    /**
+     * peekNextLocalTask returns least recent unexecuted task in async mode
+     */
+    public void testPeekNextLocalTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, peekNextLocalTask());
+                assertEquals(21, (int) f.join());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkCompletedNormally(g, 34);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+    /**
+     * pollNextLocalTask returns least recent unexecuted task without
+     * executing it, in async mode
+     */
+    public void testPollNextLocalTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollNextLocalTask());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkNotDone(g);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+    /**
+     * pollTask returns an unexecuted task without executing it, in
+     * async mode
+     */
+    public void testPollTaskAsync() {
+        RecursiveTask<Integer> a = new CheckedRecursiveTask<Integer>() {
+            public Integer realCompute() {
+                FibTask g = new FibTask(9);
+                assertSame(g, g.fork());
+                FibTask f = new FibTask(8);
+                assertSame(f, f.fork());
+                assertSame(g, pollTask());
+                helpQuiesce();
+                checkCompletedNormally(f, 21);
+                checkNotDone(g);
+                return NoResult;
+            }};
+        assertSame(NoResult, testInvokeOnPool(asyncSingletonPool(), a));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ReentrantLockTest.java b/ojluni/src/test/java/util/concurrent/tck/ReentrantLockTest.java
new file mode 100644
index 0000000..2e518d3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ReentrantLockTest.java
@@ -0,0 +1,1164 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ReentrantLockTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ReentrantLockTest.class);
+    }
+
+    /**
+     * A checked runnable calling lockInterruptibly
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final ReentrantLock lock;
+        InterruptibleLockRunnable(ReentrantLock lock) { this.lock = lock; }
+        public void realRun() throws InterruptedException {
+            lock.lockInterruptibly();
+        }
+    }
+
+    /**
+     * A checked runnable calling lockInterruptibly that expects to be
+     * interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final ReentrantLock lock;
+        InterruptedLockRunnable(ReentrantLock lock) { this.lock = lock; }
+        public void realRun() throws InterruptedException {
+            lock.lockInterruptibly();
+        }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicReentrantLock extends ReentrantLock {
+        PublicReentrantLock() { super(); }
+        PublicReentrantLock(boolean fair) { super(fair); }
+        public Thread getOwner() {
+            return super.getOwner();
+        }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public Collection<Thread> getWaitingThreads(Condition c) {
+            return super.getWaitingThreads(c);
+        }
+    }
+
+    /**
+     * Releases write lock, checking that it had a hold count of 1.
+     */
+    void releaseLock(PublicReentrantLock lock) {
+        assertLockedByMoi(lock);
+        lock.unlock();
+        assertFalse(lock.isHeldByCurrentThread());
+        assertNotLocked(lock);
+    }
+
+    /**
+     * Spin-waits until lock.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicReentrantLock lock, Thread t) {
+        long startTime = System.nanoTime();
+        while (!lock.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+        assertNotSame(t, lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is not locked.
+     */
+    void assertNotLocked(PublicReentrantLock lock) {
+        assertFalse(lock.isLocked());
+        assertFalse(lock.isHeldByCurrentThread());
+        assertNull(lock.getOwner());
+        assertEquals(0, lock.getHoldCount());
+    }
+
+    /**
+     * Checks that lock is locked by the given thread.
+     */
+    void assertLockedBy(PublicReentrantLock lock, Thread t) {
+        assertTrue(lock.isLocked());
+        assertSame(t, lock.getOwner());
+        assertEquals(t == Thread.currentThread(),
+                     lock.isHeldByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.getHoldCount() > 0);
+    }
+
+    /**
+     * Checks that lock is locked by the current thread.
+     */
+    void assertLockedByMoi(PublicReentrantLock lock) {
+        assertLockedBy(lock, Thread.currentThread());
+    }
+
+    /**
+     * Checks that condition c has no waiters.
+     */
+    void assertHasNoWaiters(PublicReentrantLock lock, Condition c) {
+        assertHasWaiters(lock, c, new Thread[] {});
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaiters(PublicReentrantLock lock, Condition c,
+                          Thread... threads) {
+        lock.lock();
+        assertEquals(threads.length > 0, lock.hasWaiters(c));
+        assertEquals(threads.length, lock.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, lock.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(lock.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+        lock.unlock();
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition "indefinitely" using the specified AwaitMethod.
+     */
+    void await(Condition c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining > timeoutNanos / 2);
+            assertTrue(nanosRemaining <= timeoutNanos);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Constructor sets given fairness, and is in unlocked state
+     */
+    public void testConstructor() {
+        PublicReentrantLock lock;
+
+        lock = new PublicReentrantLock();
+        assertFalse(lock.isFair());
+        assertNotLocked(lock);
+
+        lock = new PublicReentrantLock(true);
+        assertTrue(lock.isFair());
+        assertNotLocked(lock);
+
+        lock = new PublicReentrantLock(false);
+        assertFalse(lock.isFair());
+        assertNotLocked(lock);
+    }
+
+    /**
+     * locking an unlocked lock succeeds
+     */
+    public void testLock()      { testLock(false); }
+    public void testLock_fair() { testLock(true); }
+    public void testLock(boolean fair) {
+        PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        assertLockedByMoi(lock);
+        releaseLock(lock);
+    }
+
+    /**
+     * Unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testUnlock_IMSE()      { testUnlock_IMSE(false); }
+    public void testUnlock_IMSE_fair() { testUnlock_IMSE(true); }
+    public void testUnlock_IMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * tryLock on an unlocked lock succeeds
+     */
+    public void testTryLock()      { testTryLock(false); }
+    public void testTryLock_fair() { testTryLock(true); }
+    public void testTryLock(boolean fair) {
+        PublicReentrantLock lock = new PublicReentrantLock(fair);
+        assertTrue(lock.tryLock());
+        assertLockedByMoi(lock);
+        assertTrue(lock.tryLock());
+        assertLockedByMoi(lock);
+        lock.unlock();
+        releaseLock(lock);
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThreads());
+        lock.lock();
+        assertFalse(lock.hasQueuedThreads());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThreads());
+    }
+
+    /**
+     * getQueueLength reports number of waiting threads
+     */
+    public void testGetQueueLength()      { testGetQueueLength(false); }
+    public void testGetQueueLength_fair() { testGetQueueLength(true); }
+    public void testGetQueueLength(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertEquals(0, lock.getQueueLength());
+        lock.lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.unlock();
+        awaitTermination(t2);
+        assertEquals(0, lock.getQueueLength());
+    }
+
+    /**
+     * hasQueuedThread(null) throws NPE
+     */
+    public void testHasQueuedThreadNPE()      { testHasQueuedThreadNPE(false); }
+    public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); }
+    public void testHasQueuedThreadNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.hasQueuedThread(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasQueuedThread reports whether a thread is queued
+     */
+    public void testHasQueuedThread()      { testHasQueuedThread(false); }
+    public void testHasQueuedThread_fair() { testHasQueuedThread(true); }
+    public void testHasQueuedThread(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        lock.lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+    }
+
+    /**
+     * getQueuedThreads includes waiting threads
+     */
+    public void testGetQueuedThreads()      { testGetQueuedThreads(false); }
+    public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); }
+    public void testGetQueuedThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.lock();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        assertEquals(1, lock.getQueuedThreads().size());
+        lock.unlock();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * timed tryLock is interruptible
+     */
+    public void testTryLock_Interruptible()      { testTryLock_Interruptible(false); }
+    public void testTryLock_Interruptible_fair() { testTryLock_Interruptible(true); }
+    public void testTryLock_Interruptible(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * tryLock on a locked lock fails
+     */
+    public void testTryLockWhenLocked()      { testTryLockWhenLocked(false); }
+    public void testTryLockWhenLocked_fair() { testTryLockWhenLocked(true); }
+    public void testTryLockWhenLocked(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        lock.lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * Timed tryLock on a locked lock times out
+     */
+    public void testTryLock_Timeout()      { testTryLock_Timeout(false); }
+    public void testTryLock_Timeout_fair() { testTryLock_Timeout(true); }
+    public void testTryLock_Timeout(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final long timeoutMillis = timeoutMillis();
+        lock.lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertFalse(lock.tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * getHoldCount returns number of recursive holds
+     */
+    public void testGetHoldCount()      { testGetHoldCount(false); }
+    public void testGetHoldCount_fair() { testGetHoldCount(true); }
+    public void testGetHoldCount(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.lock();
+            assertEquals(i, lock.getHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.unlock();
+            assertEquals(i - 1, lock.getHoldCount());
+        }
+    }
+
+    /**
+     * isLocked is true when locked and false when not
+     */
+    public void testIsLocked()      { testIsLocked(false); }
+    public void testIsLocked_fair() { testIsLocked(true); }
+    public void testIsLocked(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            assertFalse(lock.isLocked());
+            lock.lock();
+            assertTrue(lock.isLocked());
+            lock.lock();
+            assertTrue(lock.isLocked());
+            lock.unlock();
+            assertTrue(lock.isLocked());
+            lock.unlock();
+            assertFalse(lock.isLocked());
+            final CyclicBarrier barrier = new CyclicBarrier(2);
+            Thread t = newStartedThread(new CheckedRunnable() {
+                    public void realRun() throws Exception {
+                        lock.lock();
+                        assertTrue(lock.isLocked());
+                        barrier.await();
+                        barrier.await();
+                        lock.unlock();
+                    }});
+
+            barrier.await();
+            assertTrue(lock.isLocked());
+            barrier.await();
+            awaitTermination(t);
+            assertFalse(lock.isLocked());
+        } catch (Exception fail) { threadUnexpectedException(fail); }
+    }
+
+    /**
+     * lockInterruptibly succeeds when unlocked, else is interruptible
+     */
+    public void testLockInterruptibly()      { testLockInterruptibly(false); }
+    public void testLockInterruptibly_fair() { testLockInterruptibly(true); }
+    public void testLockInterruptibly(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        try {
+            lock.lockInterruptibly();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertLockedByMoi(lock);
+        Thread t = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        assertTrue(lock.isLocked());
+        assertTrue(lock.isHeldByCurrentThread());
+        awaitTermination(t);
+        releaseLock(lock);
+    }
+
+    /**
+     * Calling await without holding lock throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE()      { testAwait_IMSE(false); }
+    public void testAwait_IMSE_fair() { testAwait_IMSE(true); }
+    public void testAwait_IMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE()      { testSignal_IMSE(false); }
+    public void testSignal_IMSE_fair() { testSignal_IMSE(true); }
+    public void testSignal_IMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * awaitNanos without a signal times out
+     */
+    public void testAwaitNanos_Timeout()      { testAwaitNanos_Timeout(false); }
+    public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); }
+    public void testAwaitNanos_Timeout(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final long timeoutMillis = timeoutMillis();
+        final long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+        lock.lock();
+        final long startTime = System.nanoTime();
+        try {
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining <= 0);
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        lock.unlock();
+    }
+
+    /**
+     * timed await without a signal times out
+     */
+    public void testAwait_Timeout()      { testAwait_Timeout(false); }
+    public void testAwait_Timeout_fair() { testAwait_Timeout(true); }
+    public void testAwait_Timeout(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final long timeoutMillis = timeoutMillis();
+        lock.lock();
+        final long startTime = System.nanoTime();
+        try {
+            assertFalse(c.await(timeoutMillis, MILLISECONDS));
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        lock.unlock();
+    }
+
+    /**
+     * awaitUntil without a signal times out
+     */
+    public void testAwaitUntil_Timeout()      { testAwaitUntil_Timeout(false); }
+    public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); }
+    public void testAwaitUntil_Timeout(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        lock.lock();
+        // We shouldn't assume that nanoTime and currentTimeMillis
+        // use the same time source, so don't use nanoTime here.
+        final java.util.Date delayedDate = delayedDate(timeoutMillis());
+        try {
+            assertFalse(c.awaitUntil(delayedDate));
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+        lock.unlock();
+    }
+
+    /**
+     * await returns when signalled
+     */
+    public void testAwait()      { testAwait(false); }
+    public void testAwait_fair() { testAwait(true); }
+    public void testAwait(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked);
+        lock.lock();
+        assertHasWaiters(lock, c, t);
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(t.isAlive());
+        lock.unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * hasWaiters throws NPE if null
+     */
+    public void testHasWaitersNPE()      { testHasWaitersNPE(false); }
+    public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); }
+    public void testHasWaitersNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws NPE if null
+     */
+    public void testGetWaitQueueLengthNPE()      { testGetWaitQueueLengthNPE(false); }
+    public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); }
+    public void testGetWaitQueueLengthNPE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        try {
+            lock.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE()      { testGetWaitingThreadsNPE(false); }
+    public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); }
+    public void testGetWaitingThreadsNPE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        try {
+            lock.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE()      { testHasWaitersIAE(false); }
+    public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); }
+    public void testHasWaitersIAE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final ReentrantLock lock2 = new ReentrantLock(fair);
+        try {
+            lock2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not locked
+     */
+    public void testHasWaitersIMSE()      { testHasWaitersIMSE(false); }
+    public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); }
+    public void testHasWaitersIMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE()      { testGetWaitQueueLengthIAE(false); }
+    public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); }
+    public void testGetWaitQueueLengthIAE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final ReentrantLock lock2 = new ReentrantLock(fair);
+        try {
+            lock2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitQueueLengthIMSE()      { testGetWaitQueueLengthIMSE(false); }
+    public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); }
+    public void testGetWaitQueueLengthIMSE(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE()      { testGetWaitingThreadsIAE(false); }
+    public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); }
+    public void testGetWaitingThreadsIAE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final PublicReentrantLock lock2 = new PublicReentrantLock(fair);
+        try {
+            lock2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitingThreadsIMSE()      { testGetWaitingThreadsIMSE(false); }
+    public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); }
+    public void testGetWaitingThreadsIMSE(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        try {
+            lock.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters()      { testHasWaiters(false); }
+    public void testHasWaiters_fair() { testHasWaiters(true); }
+    public void testHasWaiters(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                pleaseSignal.countDown();
+                c.await();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                lock.unlock();
+            }});
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t);
+        assertTrue(lock.hasWaiters(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertFalse(lock.hasWaiters(c));
+        lock.unlock();
+        awaitTermination(t);
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength()      { testGetWaitQueueLength(false); }
+    public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); }
+    public void testGetWaitQueueLength(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertFalse(lock.hasWaiters(c));
+                assertEquals(0, lock.getWaitQueueLength(c));
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertTrue(lock.hasWaiters(c));
+                assertEquals(1, lock.getWaitQueueLength(c));
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        lock.lock();
+        assertEquals(0, lock.getWaitQueueLength(c));
+        lock.unlock();
+
+        t1.start();
+        await(locked1);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1);
+        assertEquals(1, lock.getWaitQueueLength(c));
+        lock.unlock();
+
+        t2.start();
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertEquals(2, lock.getWaitQueueLength(c));
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads()      { testGetWaitingThreads(false); }
+    public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); }
+    public void testGetWaitingThreads(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertTrue(lock.getWaitingThreads(c).isEmpty());
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertFalse(lock.getWaitingThreads(c).isEmpty());
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        lock.lock();
+        assertTrue(lock.getWaitingThreads(c).isEmpty());
+        lock.unlock();
+
+        t1.start();
+        await(locked1);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1);
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertFalse(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(1, lock.getWaitingThreads(c).size());
+        lock.unlock();
+
+        t2.start();
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertTrue(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(2, lock.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly()      { testAwaitUninterruptibly(false); }
+    public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); }
+    public void testAwaitUninterruptibly(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before awaitUninterruptibly
+                lock.lock();
+                pleaseInterrupt.countDown();
+                Thread.currentThread().interrupt();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt during awaitUninterruptibly
+                lock.lock();
+                pleaseInterrupt.countDown();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.unlock();
+            }});
+
+        await(pleaseInterrupt);
+        lock.lock();
+        lock.unlock();
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        lock.lock();
+        c.signalAll();
+        lock.unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()           { testInterruptible(false, AwaitMethod.await); }
+    public void testInterruptible_await_fair()      { testInterruptible(true,  AwaitMethod.await); }
+    public void testInterruptible_awaitTimed()      { testInterruptible(false, AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitTimed_fair() { testInterruptible(true,  AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos()      { testInterruptible(false, AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitNanos_fair() { testInterruptible(true,  AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil()      { testInterruptible(false, AwaitMethod.awaitUntil); }
+    public void testInterruptible_awaitUntil_fair() { testInterruptible(true,  AwaitMethod.awaitUntil); }
+    public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantLock lock =
+            new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertHasNoWaiters(lock, c);
+                pleaseInterrupt.countDown();
+                try {
+                    await(c, awaitMethod);
+                } finally {
+                    assertLockedByMoi(lock);
+                    assertHasNoWaiters(lock, c);
+                    lock.unlock();
+                    assertFalse(Thread.interrupted());
+                }
+            }});
+
+        await(pleaseInterrupt);
+        assertHasWaiters(lock, c, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertNotLocked(lock);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()           { testSignalAll(false, AwaitMethod.await); }
+    public void testSignalAll_await_fair()      { testSignalAll(true,  AwaitMethod.await); }
+    public void testSignalAll_awaitTimed()      { testSignalAll(false, AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitTimed_fair() { testSignalAll(true,  AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos()      { testSignalAll(false, AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitNanos_fair() { testSignalAll(true,  AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil()      { testSignalAll(false, AwaitMethod.awaitUntil); }
+    public void testSignalAll_awaitUntil_fair() { testSignalAll(true,  AwaitMethod.awaitUntil); }
+    public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(2);
+        class Awaiter extends CheckedRunnable {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                pleaseSignal.countDown();
+                await(c, awaitMethod);
+                lock.unlock();
+            }
+        }
+
+        Thread t1 = newStartedThread(new Awaiter());
+        Thread t2 = newStartedThread(new Awaiter());
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * signal wakes up waiting threads in FIFO order
+     */
+    public void testSignalWakesFifo()      { testSignalWakesFifo(false); }
+    public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); }
+    public void testSignalWakesFifo(boolean fair) {
+        final PublicReentrantLock lock =
+            new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked1.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                locked2.countDown();
+                c.await();
+                lock.unlock();
+            }});
+
+        await(locked2);
+
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertFalse(lock.hasQueuedThreads());
+        c.signal();
+        assertHasWaiters(lock, c, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await after multiple reentrant locking preserves lock count
+     */
+    public void testAwaitLockCount()      { testAwaitLockCount(false); }
+    public void testAwaitLockCount_fair() { testAwaitLockCount(true); }
+    public void testAwaitLockCount(boolean fair) {
+        final PublicReentrantLock lock = new PublicReentrantLock(fair);
+        final Condition c = lock.newCondition();
+        final CountDownLatch pleaseSignal = new CountDownLatch(2);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertEquals(1, lock.getHoldCount());
+                pleaseSignal.countDown();
+                c.await();
+                assertLockedByMoi(lock);
+                assertEquals(1, lock.getHoldCount());
+                lock.unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.lock();
+                lock.lock();
+                assertLockedByMoi(lock);
+                assertEquals(2, lock.getHoldCount());
+                pleaseSignal.countDown();
+                c.await();
+                assertLockedByMoi(lock);
+                assertEquals(2, lock.getHoldCount());
+                lock.unlock();
+                lock.unlock();
+            }});
+
+        await(pleaseSignal);
+        lock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertEquals(1, lock.getHoldCount());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        lock.lock();
+
+        ReentrantLock clone = serialClone(lock);
+        assertEquals(lock.isFair(), clone.isFair());
+        assertTrue(lock.isLocked());
+        assertFalse(clone.isLocked());
+        assertEquals(1, lock.getHoldCount());
+        assertEquals(0, clone.getHoldCount());
+        clone.lock();
+        clone.lock();
+        assertTrue(clone.isLocked());
+        assertEquals(2, clone.getHoldCount());
+        assertEquals(1, lock.getHoldCount());
+        clone.unlock();
+        clone.unlock();
+        assertTrue(lock.isLocked());
+        assertFalse(clone.isLocked());
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        final ReentrantLock lock = new ReentrantLock(fair);
+        assertTrue(lock.toString().contains("Unlocked"));
+        lock.lock();
+        assertTrue(lock.toString().contains("Locked by"));
+        lock.unlock();
+        assertTrue(lock.toString().contains("Unlocked"));
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java b/ojluni/src/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java
new file mode 100644
index 0000000..dd2f4f3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ReentrantReadWriteLockTest.java
@@ -0,0 +1,1712 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ReentrantReadWriteLockTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ReentrantReadWriteLockTest.class);
+    }
+
+    /**
+     * A runnable calling lockInterruptibly
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final ReentrantReadWriteLock lock;
+        InterruptibleLockRunnable(ReentrantReadWriteLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLock().lockInterruptibly();
+        }
+    }
+
+    /**
+     * A runnable calling lockInterruptibly that expects to be
+     * interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final ReentrantReadWriteLock lock;
+        InterruptedLockRunnable(ReentrantReadWriteLock l) { lock = l; }
+        public void realRun() throws InterruptedException {
+            lock.writeLock().lockInterruptibly();
+        }
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicReentrantReadWriteLock extends ReentrantReadWriteLock {
+        PublicReentrantReadWriteLock() { super(); }
+        PublicReentrantReadWriteLock(boolean fair) { super(fair); }
+        public Thread getOwner() {
+            return super.getOwner();
+        }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public Collection<Thread> getWaitingThreads(Condition c) {
+            return super.getWaitingThreads(c);
+        }
+    }
+
+    /**
+     * Releases write lock, checking that it had a hold count of 1.
+     */
+    void releaseWriteLock(PublicReentrantReadWriteLock lock) {
+        ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        writeLock.unlock();
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * Spin-waits until lock.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicReentrantReadWriteLock lock, Thread t) {
+        long startTime = System.nanoTime();
+        while (!lock.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(t.isAlive());
+        assertNotSame(t, lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is not write-locked.
+     */
+    void assertNotWriteLocked(PublicReentrantReadWriteLock lock) {
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.isWriteLockedByCurrentThread());
+        assertFalse(lock.writeLock().isHeldByCurrentThread());
+        assertEquals(0, lock.getWriteHoldCount());
+        assertEquals(0, lock.writeLock().getHoldCount());
+        assertNull(lock.getOwner());
+    }
+
+    /**
+     * Checks that lock is write-locked by the given thread.
+     */
+    void assertWriteLockedBy(PublicReentrantReadWriteLock lock, Thread t) {
+        assertTrue(lock.isWriteLocked());
+        assertSame(t, lock.getOwner());
+        assertEquals(t == Thread.currentThread(),
+                     lock.isWriteLockedByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.writeLock().isHeldByCurrentThread());
+        assertEquals(t == Thread.currentThread(),
+                     lock.getWriteHoldCount() > 0);
+        assertEquals(t == Thread.currentThread(),
+                     lock.writeLock().getHoldCount() > 0);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * Checks that lock is write-locked by the current thread.
+     */
+    void assertWriteLockedByMoi(PublicReentrantReadWriteLock lock) {
+        assertWriteLockedBy(lock, Thread.currentThread());
+    }
+
+    /**
+     * Checks that condition c has no waiters.
+     */
+    void assertHasNoWaiters(PublicReentrantReadWriteLock lock, Condition c) {
+        assertHasWaiters(lock, c, new Thread[] {});
+    }
+
+    /**
+     * Checks that condition c has exactly the given waiter threads.
+     */
+    void assertHasWaiters(PublicReentrantReadWriteLock lock, Condition c,
+                          Thread... threads) {
+        lock.writeLock().lock();
+        assertEquals(threads.length > 0, lock.hasWaiters(c));
+        assertEquals(threads.length, lock.getWaitQueueLength(c));
+        assertEquals(threads.length == 0, lock.getWaitingThreads(c).isEmpty());
+        assertEquals(threads.length, lock.getWaitingThreads(c).size());
+        assertEquals(new HashSet<Thread>(lock.getWaitingThreads(c)),
+                     new HashSet<Thread>(Arrays.asList(threads)));
+        lock.writeLock().unlock();
+    }
+
+    enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
+
+    /**
+     * Awaits condition "indefinitely" using the specified AwaitMethod.
+     */
+    void await(Condition c, AwaitMethod awaitMethod)
+            throws InterruptedException {
+        long timeoutMillis = 2 * LONG_DELAY_MS;
+        switch (awaitMethod) {
+        case await:
+            c.await();
+            break;
+        case awaitTimed:
+            assertTrue(c.await(timeoutMillis, MILLISECONDS));
+            break;
+        case awaitNanos:
+            long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining > timeoutNanos / 2);
+            assertTrue(nanosRemaining <= timeoutNanos);
+            break;
+        case awaitUntil:
+            assertTrue(c.awaitUntil(delayedDate(timeoutMillis)));
+            break;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Constructor sets given fairness, and is in unlocked state
+     */
+    public void testConstructor() {
+        PublicReentrantReadWriteLock lock;
+
+        lock = new PublicReentrantReadWriteLock();
+        assertFalse(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+
+        lock = new PublicReentrantReadWriteLock(true);
+        assertTrue(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+
+        lock = new PublicReentrantReadWriteLock(false);
+        assertFalse(lock.isFair());
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * write-locking and read-locking an unlocked lock succeed
+     */
+    public void testLock()      { testLock(false); }
+    public void testLock_fair() { testLock(true); }
+    public void testLock(boolean fair) {
+        PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        assertNotWriteLocked(lock);
+        lock.writeLock().lock();
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+        lock.readLock().lock();
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        assertNotWriteLocked(lock);
+        assertEquals(0, lock.getReadLockCount());
+    }
+
+    /**
+     * getWriteHoldCount returns number of recursive holds
+     */
+    public void testGetWriteHoldCount()      { testGetWriteHoldCount(false); }
+    public void testGetWriteHoldCount_fair() { testGetWriteHoldCount(true); }
+    public void testGetWriteHoldCount(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.writeLock().lock();
+            assertEquals(i,lock.getWriteHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.writeLock().unlock();
+            assertEquals(i - 1,lock.getWriteHoldCount());
+        }
+    }
+
+    /**
+     * writelock.getHoldCount returns number of recursive holds
+     */
+    public void testGetHoldCount()      { testGetHoldCount(false); }
+    public void testGetHoldCount_fair() { testGetHoldCount(true); }
+    public void testGetHoldCount(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.writeLock().lock();
+            assertEquals(i,lock.writeLock().getHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.writeLock().unlock();
+            assertEquals(i - 1,lock.writeLock().getHoldCount());
+        }
+    }
+
+    /**
+     * getReadHoldCount returns number of recursive holds
+     */
+    public void testGetReadHoldCount()      { testGetReadHoldCount(false); }
+    public void testGetReadHoldCount_fair() { testGetReadHoldCount(true); }
+    public void testGetReadHoldCount(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        for (int i = 1; i <= SIZE; i++) {
+            lock.readLock().lock();
+            assertEquals(i,lock.getReadHoldCount());
+        }
+        for (int i = SIZE; i > 0; i--) {
+            lock.readLock().unlock();
+            assertEquals(i - 1,lock.getReadHoldCount());
+        }
+    }
+
+    /**
+     * write-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testWriteUnlock_IMSE()      { testWriteUnlock_IMSE(false); }
+    public void testWriteUnlock_IMSE_fair() { testWriteUnlock_IMSE(true); }
+    public void testWriteUnlock_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.writeLock().unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * read-unlocking an unlocked lock throws IllegalMonitorStateException
+     */
+    public void testReadUnlock_IMSE()      { testReadUnlock_IMSE(false); }
+    public void testReadUnlock_IMSE_fair() { testReadUnlock_IMSE(true); }
+    public void testReadUnlock_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.readLock().unlock();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * write-lockInterruptibly is interruptible
+     */
+    public void testWriteLockInterruptibly_Interruptible()      { testWriteLockInterruptibly_Interruptible(false); }
+    public void testWriteLockInterruptibly_Interruptible_fair() { testWriteLockInterruptibly_Interruptible(true); }
+    public void testWriteLockInterruptibly_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * timed write-tryLock is interruptible
+     */
+    public void testWriteTryLock_Interruptible()      { testWriteTryLock_Interruptible(false); }
+    public void testWriteTryLock_Interruptible_fair() { testWriteTryLock_Interruptible(true); }
+    public void testWriteTryLock_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read-lockInterruptibly is interruptible
+     */
+    public void testReadLockInterruptibly_Interruptible()      { testReadLockInterruptibly_Interruptible(false); }
+    public void testReadLockInterruptibly_Interruptible_fair() { testReadLockInterruptibly_Interruptible(true); }
+    public void testReadLockInterruptibly_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * timed read-tryLock is interruptible
+     */
+    public void testReadTryLock_Interruptible()      { testReadTryLock_Interruptible(false); }
+    public void testReadTryLock_Interruptible_fair() { testReadTryLock_Interruptible(true); }
+    public void testReadTryLock_Interruptible(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().tryLock(2 * LONG_DELAY_MS, MILLISECONDS);
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * write-tryLock on an unlocked lock succeeds
+     */
+    public void testWriteTryLock()      { testWriteTryLock(false); }
+    public void testWriteTryLock_fair() { testWriteTryLock(true); }
+    public void testWriteTryLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        assertTrue(lock.writeLock().tryLock());
+        assertWriteLockedByMoi(lock);
+        assertTrue(lock.writeLock().tryLock());
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * write-tryLock fails if locked
+     */
+    public void testWriteTryLockWhenLocked()      { testWriteTryLockWhenLocked(false); }
+    public void testWriteTryLockWhenLocked_fair() { testWriteTryLockWhenLocked(true); }
+    public void testWriteTryLockWhenLocked(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.writeLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read-tryLock fails if locked
+     */
+    public void testReadTryLockWhenLocked()      { testReadTryLockWhenLocked(false); }
+    public void testReadTryLockWhenLocked_fair() { testReadTryLockWhenLocked(true); }
+    public void testReadTryLockWhenLocked(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.readLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * Multiple threads can hold a read lock when not write-locked
+     */
+    public void testMultipleReadLocks()      { testMultipleReadLocks(false); }
+    public void testMultipleReadLocks_fair() { testMultipleReadLocks(true); }
+    public void testMultipleReadLocks(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                assertTrue(lock.readLock().tryLock());
+                lock.readLock().unlock();
+                assertTrue(lock.readLock().tryLock(LONG_DELAY_MS, MILLISECONDS));
+                lock.readLock().unlock();
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * A writelock succeeds only after a reading thread unlocks
+     */
+    public void testWriteAfterReadLock()      { testWriteAfterReadLock(false); }
+    public void testWriteAfterReadLock_fair() { testWriteAfterReadLock(true); }
+    public void testWriteAfterReadLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t);
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        assertEquals(0, lock.getReadLockCount());
+        awaitTermination(t);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * A writelock succeeds only after reading threads unlock
+     */
+    public void testWriteAfterMultipleReadLocks()      { testWriteAfterMultipleReadLocks(false); }
+    public void testWriteAfterMultipleReadLocks_fair() { testWriteAfterMultipleReadLocks(true); }
+    public void testWriteAfterMultipleReadLocks(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        lock.readLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                assertEquals(3, lock.getReadLockCount());
+                lock.readLock().unlock();
+            }});
+        awaitTermination(t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(2, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t2);
+        assertNotWriteLocked(lock);
+        assertEquals(2, lock.getReadLockCount());
+        lock.readLock().unlock();
+        lock.readLock().unlock();
+        assertEquals(0, lock.getReadLockCount());
+        awaitTermination(t2);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * A thread that tries to acquire a fair read lock (non-reentrantly)
+     * will block if there is a waiting writer thread
+     */
+    public void testReaderWriterReaderFairFifo() {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(true);
+        final AtomicBoolean t1GotLock = new AtomicBoolean(false);
+
+        lock.readLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.writeLock().lock();
+                assertEquals(0, lock.getReadLockCount());
+                t1GotLock.set(true);
+                lock.writeLock().unlock();
+            }});
+        waitForQueuedThread(lock, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(1, lock.getReadLockCount());
+                lock.readLock().lock();
+                assertEquals(1, lock.getReadLockCount());
+                assertTrue(t1GotLock.get());
+                lock.readLock().unlock();
+            }});
+        waitForQueuedThread(lock, t2);
+        assertTrue(t1.isAlive());
+        assertNotWriteLocked(lock);
+        assertEquals(1, lock.getReadLockCount());
+        lock.readLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * Readlocks succeed only after a writing thread unlocks
+     */
+    public void testReadAfterWriteLock()      { testReadAfterWriteLock(false); }
+    public void testReadAfterWriteLock_fair() { testReadAfterWriteLock(true); }
+    public void testReadAfterWriteLock(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        releaseWriteLock(lock);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read trylock succeeds if write locked by current thread
+     */
+    public void testReadHoldingWriteLock()      { testReadHoldingWriteLock(false); }
+    public void testReadHoldingWriteLock_fair() { testReadHoldingWriteLock(true); }
+    public void testReadHoldingWriteLock(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        assertTrue(lock.readLock().tryLock());
+        lock.readLock().unlock();
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * Read trylock succeeds (barging) even in the presence of waiting
+     * readers and/or writers
+     */
+    public void testReadTryLockBarging()      { testReadTryLockBarging(false); }
+    public void testReadTryLockBarging_fair() { testReadTryLockBarging(true); }
+    public void testReadTryLockBarging(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        if (fair)
+            waitForQueuedThread(lock, t2);
+
+        Thread t3 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().tryLock();
+                lock.readLock().unlock();
+            }});
+
+        assertTrue(lock.getReadLockCount() > 0);
+        awaitTermination(t3);
+        assertTrue(t1.isAlive());
+        if (fair) assertTrue(t2.isAlive());
+        lock.readLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read lock succeeds if write locked by current thread even if
+     * other threads are waiting for readlock
+     */
+    public void testReadHoldingWriteLock2()      { testReadHoldingWriteLock2(false); }
+    public void testReadHoldingWriteLock2_fair() { testReadHoldingWriteLock2(true); }
+    public void testReadHoldingWriteLock2(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+        lock.readLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.readLock().lock();
+                lock.readLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        lock.readLock().lock();
+        lock.readLock().unlock();
+        releaseWriteLock(lock);
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read lock succeeds if write locked by current thread even if
+     * other threads are waiting for writelock
+     */
+    public void testReadHoldingWriteLock3()      { testReadHoldingWriteLock3(false); }
+    public void testReadHoldingWriteLock3_fair() { testReadHoldingWriteLock3(true); }
+    public void testReadHoldingWriteLock3(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+        lock.readLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        lock.readLock().lock();
+        lock.readLock().unlock();
+        assertWriteLockedByMoi(lock);
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Write lock succeeds if write locked by current thread even if
+     * other threads are waiting for writelock
+     */
+    public void testWriteHoldingWriteLock4()      { testWriteHoldingWriteLock4(false); }
+    public void testWriteHoldingWriteLock4_fair() { testWriteHoldingWriteLock4(true); }
+    public void testWriteHoldingWriteLock4(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.writeLock().lock();
+        lock.writeLock().unlock();
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                lock.writeLock().lock();
+                lock.writeLock().unlock();
+            }});
+
+        waitForQueuedThread(lock, t1);
+        waitForQueuedThread(lock, t2);
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        lock.writeLock().lock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(2, lock.getWriteHoldCount());
+        lock.writeLock().unlock();
+        assertWriteLockedByMoi(lock);
+        assertEquals(1, lock.getWriteHoldCount());
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * Read tryLock succeeds if readlocked but not writelocked
+     */
+    public void testTryLockWhenReadLocked()      { testTryLockWhenReadLocked(false); }
+    public void testTryLockWhenReadLocked_fair() { testTryLockWhenReadLocked(true); }
+    public void testTryLockWhenReadLocked(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertTrue(lock.readLock().tryLock());
+                lock.readLock().unlock();
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * write tryLock fails when readlocked
+     */
+    public void testWriteTryLockWhenReadLocked()      { testWriteTryLockWhenReadLocked(false); }
+    public void testWriteTryLockWhenReadLocked_fair() { testWriteTryLockWhenReadLocked(true); }
+    public void testWriteTryLockWhenReadLocked(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.readLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertFalse(lock.writeLock().tryLock());
+            }});
+
+        awaitTermination(t);
+        lock.readLock().unlock();
+    }
+
+    /**
+     * write timed tryLock times out if locked
+     */
+    public void testWriteTryLock_Timeout()      { testWriteTryLock_Timeout(false); }
+    public void testWriteTryLock_Timeout_fair() { testWriteTryLock_Timeout(true); }
+    public void testWriteTryLock_Timeout(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final long timeoutMillis = timeoutMillis();
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertFalse(lock.writeLock().tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read timed tryLock times out if write-locked
+     */
+    public void testReadTryLock_Timeout()      { testReadTryLock_Timeout(false); }
+    public void testReadTryLock_Timeout_fair() { testReadTryLock_Timeout(true); }
+    public void testReadTryLock_Timeout(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                long timeoutMillis = timeoutMillis();
+                assertFalse(lock.readLock().tryLock(timeoutMillis, MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            }});
+
+        awaitTermination(t);
+        assertTrue(lock.writeLock().isHeldByCurrentThread());
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * write lockInterruptibly succeeds if unlocked, else is interruptible
+     */
+    public void testWriteLockInterruptibly()      { testWriteLockInterruptibly(false); }
+    public void testWriteLockInterruptibly_fair() { testWriteLockInterruptibly(true); }
+    public void testWriteLockInterruptibly(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.writeLock().lockInterruptibly();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        assertTrue(lock.writeLock().isHeldByCurrentThread());
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * read lockInterruptibly succeeds if lock free else is interruptible
+     */
+    public void testReadLockInterruptibly()      { testReadLockInterruptibly(false); }
+    public void testReadLockInterruptibly_fair() { testReadLockInterruptibly(true); }
+    public void testReadLockInterruptibly(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.readLock().lockInterruptibly();
+            lock.readLock().unlock();
+            lock.writeLock().lockInterruptibly();
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.readLock().lockInterruptibly();
+            }});
+
+        waitForQueuedThread(lock, t);
+        t.interrupt();
+        awaitTermination(t);
+        releaseWriteLock(lock);
+    }
+
+    /**
+     * Calling await without holding lock throws IllegalMonitorStateException
+     */
+    public void testAwait_IMSE()      { testAwait_IMSE(false); }
+    public void testAwait_IMSE_fair() { testAwait_IMSE(true); }
+    public void testAwait_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        for (AwaitMethod awaitMethod : AwaitMethod.values()) {
+            long startTime = System.nanoTime();
+            try {
+                await(c, awaitMethod);
+                shouldThrow();
+            } catch (IllegalMonitorStateException success) {
+            } catch (InterruptedException fail) {
+                threadUnexpectedException(fail);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * Calling signal without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignal_IMSE()      { testSignal_IMSE(false); }
+    public void testSignal_IMSE_fair() { testSignal_IMSE(true); }
+    public void testSignal_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            c.signal();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * Calling signalAll without holding lock throws IllegalMonitorStateException
+     */
+    public void testSignalAll_IMSE()      { testSignalAll_IMSE(false); }
+    public void testSignalAll_IMSE_fair() { testSignalAll_IMSE(true); }
+    public void testSignalAll_IMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            c.signalAll();
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * awaitNanos without a signal times out
+     */
+    public void testAwaitNanos_Timeout()      { testAwaitNanos_Timeout(false); }
+    public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); }
+    public void testAwaitNanos_Timeout(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final long timeoutMillis = timeoutMillis();
+        lock.writeLock().lock();
+        final long startTime = System.nanoTime();
+        final long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis);
+        try {
+            long nanosRemaining = c.awaitNanos(timeoutNanos);
+            assertTrue(nanosRemaining <= 0);
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * timed await without a signal times out
+     */
+    public void testAwait_Timeout()      { testAwait_Timeout(false); }
+    public void testAwait_Timeout_fair() { testAwait_Timeout(true); }
+    public void testAwait_Timeout(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final long timeoutMillis = timeoutMillis();
+        lock.writeLock().lock();
+        final long startTime = System.nanoTime();
+        try {
+            assertFalse(c.await(timeoutMillis, MILLISECONDS));
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * awaitUntil without a signal times out
+     */
+    public void testAwaitUntil_Timeout()      { testAwaitUntil_Timeout(false); }
+    public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); }
+    public void testAwaitUntil_Timeout(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        lock.writeLock().lock();
+        // We shouldn't assume that nanoTime and currentTimeMillis
+        // use the same time source, so don't use nanoTime here.
+        final java.util.Date delayedDate = delayedDate(timeoutMillis());
+        try {
+            assertFalse(c.awaitUntil(delayedDate));
+        } catch (InterruptedException fail) { threadUnexpectedException(fail); }
+        assertTrue(new java.util.Date().getTime() >= delayedDate.getTime());
+        lock.writeLock().unlock();
+    }
+
+    /**
+     * await returns when signalled
+     */
+    public void testAwait()      { testAwait(false); }
+    public void testAwait_fair() { testAwait(true); }
+    public void testAwait(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                locked.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(t.isAlive());
+        lock.writeLock().unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * awaitUninterruptibly is uninterruptible
+     */
+    public void testAwaitUninterruptibly()      { testAwaitUninterruptibly(false); }
+    public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); }
+    public void testAwaitUninterruptibly(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(2);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before awaitUninterruptibly
+                lock.writeLock().lock();
+                pleaseInterrupt.countDown();
+                Thread.currentThread().interrupt();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt during awaitUninterruptibly
+                lock.writeLock().lock();
+                pleaseInterrupt.countDown();
+                c.awaitUninterruptibly();
+                assertTrue(Thread.interrupted());
+                lock.writeLock().unlock();
+            }});
+
+        await(pleaseInterrupt);
+        lock.writeLock().lock();
+        lock.writeLock().unlock();
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        lock.writeLock().lock();
+        c.signalAll();
+        lock.writeLock().unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await/awaitNanos/awaitUntil is interruptible
+     */
+    public void testInterruptible_await()           { testInterruptible(false, AwaitMethod.await); }
+    public void testInterruptible_await_fair()      { testInterruptible(true,  AwaitMethod.await); }
+    public void testInterruptible_awaitTimed()      { testInterruptible(false, AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitTimed_fair() { testInterruptible(true,  AwaitMethod.awaitTimed); }
+    public void testInterruptible_awaitNanos()      { testInterruptible(false, AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitNanos_fair() { testInterruptible(true,  AwaitMethod.awaitNanos); }
+    public void testInterruptible_awaitUntil()      { testInterruptible(false, AwaitMethod.awaitUntil); }
+    public void testInterruptible_awaitUntil_fair() { testInterruptible(true,  AwaitMethod.awaitUntil); }
+    public void testInterruptible(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertHasNoWaiters(lock, c);
+                locked.countDown();
+                try {
+                    await(c, awaitMethod);
+                } finally {
+                    assertWriteLockedByMoi(lock);
+                    assertHasNoWaiters(lock, c);
+                    lock.writeLock().unlock();
+                    assertFalse(Thread.interrupted());
+                }
+            }});
+
+        await(locked);
+        assertHasWaiters(lock, c, t);
+        t.interrupt();
+        awaitTermination(t);
+        assertNotWriteLocked(lock);
+    }
+
+    /**
+     * signalAll wakes up all threads
+     */
+    public void testSignalAll_await()           { testSignalAll(false, AwaitMethod.await); }
+    public void testSignalAll_await_fair()      { testSignalAll(true,  AwaitMethod.await); }
+    public void testSignalAll_awaitTimed()      { testSignalAll(false, AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitTimed_fair() { testSignalAll(true,  AwaitMethod.awaitTimed); }
+    public void testSignalAll_awaitNanos()      { testSignalAll(false, AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitNanos_fair() { testSignalAll(true,  AwaitMethod.awaitNanos); }
+    public void testSignalAll_awaitUntil()      { testSignalAll(false, AwaitMethod.awaitUntil); }
+    public void testSignalAll_awaitUntil_fair() { testSignalAll(true,  AwaitMethod.awaitUntil); }
+    public void testSignalAll(boolean fair, final AwaitMethod awaitMethod) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(2);
+        final Lock writeLock = lock.writeLock();
+        class Awaiter extends CheckedRunnable {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked.countDown();
+                await(c, awaitMethod);
+                writeLock.unlock();
+            }
+        }
+
+        Thread t1 = newStartedThread(new Awaiter());
+        Thread t2 = newStartedThread(new Awaiter());
+
+        await(locked);
+        writeLock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        writeLock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * signal wakes up waiting threads in FIFO order
+     */
+    public void testSignalWakesFifo()      { testSignalWakesFifo(false); }
+    public void testSignalWakesFifo_fair() { testSignalWakesFifo(true); }
+    public void testSignalWakesFifo(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        final Lock writeLock = lock.writeLock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked1.countDown();
+                c.await();
+                writeLock.unlock();
+            }});
+
+        await(locked1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                writeLock.lock();
+                locked2.countDown();
+                c.await();
+                writeLock.unlock();
+            }});
+
+        await(locked2);
+
+        writeLock.lock();
+        assertHasWaiters(lock, c, t1, t2);
+        assertFalse(lock.hasQueuedThreads());
+        c.signal();
+        assertHasWaiters(lock, c, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        writeLock.unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * await after multiple reentrant locking preserves lock count
+     */
+    public void testAwaitLockCount()      { testAwaitLockCount(false); }
+    public void testAwaitLockCount_fair() { testAwaitLockCount(true); }
+    public void testAwaitLockCount(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(2);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertEquals(1, lock.writeLock().getHoldCount());
+                locked.countDown();
+                c.await();
+                assertWriteLockedByMoi(lock);
+                assertEquals(1, lock.writeLock().getHoldCount());
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                lock.writeLock().lock();
+                assertWriteLockedByMoi(lock);
+                assertEquals(2, lock.writeLock().getHoldCount());
+                locked.countDown();
+                c.await();
+                assertWriteLockedByMoi(lock);
+                assertEquals(2, lock.writeLock().getHoldCount());
+                lock.writeLock().unlock();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t1, t2);
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.writeLock().unlock();
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        lock.writeLock().lock();
+        lock.readLock().lock();
+
+        ReentrantReadWriteLock clone = serialClone(lock);
+        assertEquals(lock.isFair(), clone.isFair());
+        assertTrue(lock.isWriteLocked());
+        assertFalse(clone.isWriteLocked());
+        assertEquals(1, lock.getReadLockCount());
+        assertEquals(0, clone.getReadLockCount());
+        clone.writeLock().lock();
+        clone.readLock().lock();
+        assertTrue(clone.isWriteLocked());
+        assertEquals(1, clone.getReadLockCount());
+        clone.readLock().unlock();
+        clone.writeLock().unlock();
+        assertFalse(clone.isWriteLocked());
+        assertEquals(1, lock.getReadLockCount());
+        assertEquals(0, clone.getReadLockCount());
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThreads());
+        lock.writeLock().lock();
+        assertFalse(lock.hasQueuedThreads());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThreads());
+    }
+
+    /**
+     * hasQueuedThread(null) throws NPE
+     */
+    public void testHasQueuedThreadNPE()      { testHasQueuedThreadNPE(false); }
+    public void testHasQueuedThreadNPE_fair() { testHasQueuedThreadNPE(true); }
+    public void testHasQueuedThreadNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.hasQueuedThread(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasQueuedThread reports whether a thread is queued
+     */
+    public void testHasQueuedThread()      { testHasQueuedThread(false); }
+    public void testHasQueuedThread_fair() { testHasQueuedThread(true); }
+    public void testHasQueuedThread(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        lock.writeLock().lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertTrue(lock.hasQueuedThread(t2));
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThread(t1));
+        assertFalse(lock.hasQueuedThread(t2));
+    }
+
+    /**
+     * getQueueLength reports number of waiting threads
+     */
+    public void testGetQueueLength()      { testGetQueueLength(false); }
+    public void testGetQueueLength_fair() { testGetQueueLength(true); }
+    public void testGetQueueLength(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertEquals(0, lock.getQueueLength());
+        lock.writeLock().lock();
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertEquals(0, lock.getQueueLength());
+    }
+
+    /**
+     * getQueuedThreads includes waiting threads
+     */
+    public void testGetQueuedThreads()      { testGetQueuedThreads(false); }
+    public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); }
+    public void testGetQueuedThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        Thread t1 = new Thread(new InterruptedLockRunnable(lock));
+        Thread t2 = new Thread(new InterruptibleLockRunnable(lock));
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.writeLock().lock();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        t1.start();
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        t2.start();
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueuedThreads().size());
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        assertEquals(1, lock.getQueuedThreads().size());
+        lock.writeLock().unlock();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * hasWaiters throws NPE if null
+     */
+    public void testHasWaitersNPE()      { testHasWaitersNPE(false); }
+    public void testHasWaitersNPE_fair() { testHasWaitersNPE(true); }
+    public void testHasWaitersNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.hasWaiters(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws NPE if null
+     */
+    public void testGetWaitQueueLengthNPE()      { testGetWaitQueueLengthNPE(false); }
+    public void testGetWaitQueueLengthNPE_fair() { testGetWaitQueueLengthNPE(true); }
+    public void testGetWaitQueueLengthNPE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        try {
+            lock.getWaitQueueLength(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws NPE if null
+     */
+    public void testGetWaitingThreadsNPE()      { testGetWaitingThreadsNPE(false); }
+    public void testGetWaitingThreadsNPE_fair() { testGetWaitingThreadsNPE(true); }
+    public void testGetWaitingThreadsNPE(boolean fair) {
+        final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(fair);
+        try {
+            lock.getWaitingThreads(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalArgumentException if not owned
+     */
+    public void testHasWaitersIAE()      { testHasWaitersIAE(false); }
+    public void testHasWaitersIAE_fair() { testHasWaitersIAE(true); }
+    public void testHasWaitersIAE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair);
+        try {
+            lock2.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * hasWaiters throws IllegalMonitorStateException if not locked
+     */
+    public void testHasWaitersIMSE()      { testHasWaitersIMSE(false); }
+    public void testHasWaitersIMSE_fair() { testHasWaitersIMSE(true); }
+    public void testHasWaitersIMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.hasWaiters(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitQueueLengthIAE()      { testGetWaitQueueLengthIAE(false); }
+    public void testGetWaitQueueLengthIAE_fair() { testGetWaitQueueLengthIAE(true); }
+    public void testGetWaitQueueLengthIAE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final ReentrantReadWriteLock lock2 = new ReentrantReadWriteLock(fair);
+        try {
+            lock2.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitQueueLength throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitQueueLengthIMSE()      { testGetWaitQueueLengthIMSE(false); }
+    public void testGetWaitQueueLengthIMSE_fair() { testGetWaitQueueLengthIMSE(true); }
+    public void testGetWaitQueueLengthIMSE(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.getWaitQueueLength(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalArgumentException if not owned
+     */
+    public void testGetWaitingThreadsIAE()      { testGetWaitingThreadsIAE(false); }
+    public void testGetWaitingThreadsIAE_fair() { testGetWaitingThreadsIAE(true); }
+    public void testGetWaitingThreadsIAE(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final PublicReentrantReadWriteLock lock2 =
+            new PublicReentrantReadWriteLock(fair);
+        try {
+            lock2.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * getWaitingThreads throws IllegalMonitorStateException if not locked
+     */
+    public void testGetWaitingThreadsIMSE()      { testGetWaitingThreadsIMSE(false); }
+    public void testGetWaitingThreadsIMSE_fair() { testGetWaitingThreadsIMSE(true); }
+    public void testGetWaitingThreadsIMSE(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        try {
+            lock.getWaitingThreads(c);
+            shouldThrow();
+        } catch (IllegalMonitorStateException success) {}
+    }
+
+    /**
+     * hasWaiters returns true when a thread is waiting, else false
+     */
+    public void testHasWaiters()      { testHasWaiters(false); }
+    public void testHasWaiters_fair() { testHasWaiters(true); }
+    public void testHasWaiters(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                locked.countDown();
+                c.await();
+                assertHasNoWaiters(lock, c);
+                assertFalse(lock.hasWaiters(c));
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        assertTrue(lock.hasWaiters(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertFalse(lock.hasWaiters(c));
+        lock.writeLock().unlock();
+        awaitTermination(t);
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * getWaitQueueLength returns number of waiting threads
+     */
+    public void testGetWaitQueueLength()      { testGetWaitQueueLength(false); }
+    public void testGetWaitQueueLength_fair() { testGetWaitQueueLength(true); }
+    public void testGetWaitQueueLength(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertEquals(0, lock.getWaitQueueLength(c));
+                locked.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        await(locked);
+        lock.writeLock().lock();
+        assertHasWaiters(lock, c, t);
+        assertEquals(1, lock.getWaitQueueLength(c));
+        c.signal();
+        assertHasNoWaiters(lock, c);
+        assertEquals(0, lock.getWaitQueueLength(c));
+        lock.writeLock().unlock();
+        awaitTermination(t);
+    }
+
+    /**
+     * getWaitingThreads returns only and all waiting threads
+     */
+    public void testGetWaitingThreads()      { testGetWaitingThreads(false); }
+    public void testGetWaitingThreads_fair() { testGetWaitingThreads(true); }
+    public void testGetWaitingThreads(boolean fair) {
+        final PublicReentrantReadWriteLock lock =
+            new PublicReentrantReadWriteLock(fair);
+        final Condition c = lock.writeLock().newCondition();
+        final CountDownLatch locked1 = new CountDownLatch(1);
+        final CountDownLatch locked2 = new CountDownLatch(1);
+        Thread t1 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertTrue(lock.getWaitingThreads(c).isEmpty());
+                locked1.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        Thread t2 = new Thread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLock().lock();
+                assertFalse(lock.getWaitingThreads(c).isEmpty());
+                locked2.countDown();
+                c.await();
+                lock.writeLock().unlock();
+            }});
+
+        lock.writeLock().lock();
+        assertTrue(lock.getWaitingThreads(c).isEmpty());
+        lock.writeLock().unlock();
+
+        t1.start();
+        await(locked1);
+        t2.start();
+        await(locked2);
+
+        lock.writeLock().lock();
+        assertTrue(lock.hasWaiters(c));
+        assertTrue(lock.getWaitingThreads(c).contains(t1));
+        assertTrue(lock.getWaitingThreads(c).contains(t2));
+        assertEquals(2, lock.getWaitingThreads(c).size());
+        c.signalAll();
+        assertHasNoWaiters(lock, c);
+        lock.writeLock().unlock();
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+
+        assertHasNoWaiters(lock, c);
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.toString().contains("Write locks = 0"));
+        assertTrue(lock.toString().contains("Read locks = 0"));
+        lock.writeLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 1"));
+        assertTrue(lock.toString().contains("Read locks = 0"));
+        lock.writeLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 2"));
+        assertTrue(lock.toString().contains("Read locks = 0"));
+        lock.writeLock().unlock();
+        lock.writeLock().unlock();
+        lock.readLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 0"));
+        assertTrue(lock.toString().contains("Read locks = 1"));
+        lock.readLock().lock();
+        assertTrue(lock.toString().contains("Write locks = 0"));
+        assertTrue(lock.toString().contains("Read locks = 2"));
+    }
+
+    /**
+     * readLock.toString indicates current lock state
+     */
+    public void testReadLockToString()      { testReadLockToString(false); }
+    public void testReadLockToString_fair() { testReadLockToString(true); }
+    public void testReadLockToString(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.readLock().toString().contains("Read locks = 0"));
+        lock.readLock().lock();
+        assertTrue(lock.readLock().toString().contains("Read locks = 1"));
+        lock.readLock().lock();
+        assertTrue(lock.readLock().toString().contains("Read locks = 2"));
+        lock.readLock().unlock();
+        assertTrue(lock.readLock().toString().contains("Read locks = 1"));
+        lock.readLock().unlock();
+        assertTrue(lock.readLock().toString().contains("Read locks = 0"));
+    }
+
+    /**
+     * writeLock.toString indicates current lock state
+     */
+    public void testWriteLockToString()      { testWriteLockToString(false); }
+    public void testWriteLockToString_fair() { testWriteLockToString(true); }
+    public void testWriteLockToString(boolean fair) {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair);
+        assertTrue(lock.writeLock().toString().contains("Unlocked"));
+        lock.writeLock().lock();
+        assertTrue(lock.writeLock().toString().contains("Locked by"));
+        lock.writeLock().unlock();
+        assertTrue(lock.writeLock().toString().contains("Unlocked"));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java
new file mode 100644
index 0000000..d0e4182
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorSubclassTest.java
@@ -0,0 +1,1312 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.RunnableScheduledFuture;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ScheduledExecutorSubclassTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ScheduledExecutorSubclassTest.class);
+    }
+
+    static class CustomTask<V> implements RunnableScheduledFuture<V> {
+        private final RunnableScheduledFuture<V> task;
+        volatile boolean ran;
+        CustomTask(RunnableScheduledFuture<V> task) { this.task = task; }
+        public boolean isPeriodic() { return task.isPeriodic(); }
+        public void run() {
+            ran = true;
+            task.run();
+        }
+        public long getDelay(TimeUnit unit) { return task.getDelay(unit); }
+        public int compareTo(Delayed t) {
+            return task.compareTo(((CustomTask)t).task);
+        }
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return task.cancel(mayInterruptIfRunning);
+        }
+        public boolean isCancelled() { return task.isCancelled(); }
+        public boolean isDone() { return task.isDone(); }
+        public V get() throws InterruptedException, ExecutionException {
+            V v = task.get();
+            assertTrue(ran);
+            return v;
+        }
+        public V get(long time, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+            V v = task.get(time, unit);
+            assertTrue(ran);
+            return v;
+        }
+    }
+
+    public class CustomExecutor extends ScheduledThreadPoolExecutor {
+
+        protected <V> RunnableScheduledFuture<V> decorateTask(Runnable r, RunnableScheduledFuture<V> task) {
+            return new CustomTask<V>(task);
+        }
+
+        protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> c, RunnableScheduledFuture<V> task) {
+            return new CustomTask<V>(task);
+        }
+        CustomExecutor(int corePoolSize) { super(corePoolSize); }
+        CustomExecutor(int corePoolSize, RejectedExecutionHandler handler) {
+            super(corePoolSize, handler);
+        }
+
+        CustomExecutor(int corePoolSize, ThreadFactory threadFactory) {
+            super(corePoolSize, threadFactory);
+        }
+        CustomExecutor(int corePoolSize, ThreadFactory threadFactory,
+                       RejectedExecutionHandler handler) {
+            super(corePoolSize, threadFactory, handler);
+        }
+
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            await(done);
+        }
+    }
+
+    /**
+     * delayed schedule of callable successfully executes after delay
+     */
+    public void testSchedule1() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final long startTime = System.nanoTime();
+            Callable task = new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                    return Boolean.TRUE;
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * delayed schedule of runnable successfully executes after delay
+     */
+    public void testSchedule3() throws Exception {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            await(done);
+            assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate executes runnable after given initial delay
+     */
+    public void testSchedule4() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleAtFixedRate(task, timeoutMillis(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes runnable after given initial delay
+     */
+    public void testSchedule5() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleWithFixedDelay(task, timeoutMillis(),
+                                         LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    static class RunnableCounter implements Runnable {
+        AtomicInteger count = new AtomicInteger(0);
+        public void run() { count.getAndIncrement(); }
+    }
+
+    /**
+     * scheduleAtFixedRate executes series of tasks at given rate.
+     * Eventually, it must hold that:
+     *   cycles - 1 <= elapsedMillis/delay < cycles
+     */
+    public void testFixedRateSequence() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                final long startTime = System.nanoTime();
+                final int cycles = 8;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                final Runnable task = new CheckedRunnable() {
+                    public void realRun() { done.countDown(); }};
+                final ScheduledFuture periodicTask =
+                    p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS);
+                final int totalDelayMillis = (cycles - 1) * delay;
+                await(done, totalDelayMillis + LONG_DELAY_MS);
+                periodicTask.cancel(true);
+                final long elapsedMillis = millisElapsedSince(startTime);
+                assertTrue(elapsedMillis >= totalDelayMillis);
+                if (elapsedMillis <= cycles * delay)
+                    return;
+                // else retry with longer delay
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes series of tasks with given period.
+     * Eventually, it must hold that each task starts at least delay and at
+     * most 2 * delay after the termination of the previous task.
+     */
+    public void testFixedDelaySequence() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                final long startTime = System.nanoTime();
+                final AtomicLong previous = new AtomicLong(startTime);
+                final AtomicBoolean tryLongerDelay = new AtomicBoolean(false);
+                final int cycles = 8;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                final int d = delay;
+                final Runnable task = new CheckedRunnable() {
+                    public void realRun() {
+                        long now = System.nanoTime();
+                        long elapsedMillis
+                            = NANOSECONDS.toMillis(now - previous.get());
+                        if (done.getCount() == cycles) { // first execution
+                            if (elapsedMillis >= d)
+                                tryLongerDelay.set(true);
+                        } else {
+                            assertTrue(elapsedMillis >= d);
+                            if (elapsedMillis >= 2 * d)
+                                tryLongerDelay.set(true);
+                        }
+                        previous.set(now);
+                        done.countDown();
+                    }};
+                final ScheduledFuture periodicTask =
+                    p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS);
+                final int totalDelayMillis = (cycles - 1) * delay;
+                await(done, totalDelayMillis + cycles * LONG_DELAY_MS);
+                periodicTask.cancel(true);
+                final long elapsedMillis = millisElapsedSince(startTime);
+                assertTrue(elapsedMillis >= totalDelayMillis);
+                if (!tryLongerDelay.get())
+                    return;
+                // else retry with longer delay
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * schedule(null) throws NPE
+     */
+    public void testScheduleNull() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                TrackedCallable callable = null;
+                Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule1_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpRunnable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule2_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule callable throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule3_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleAtFixedRate1_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleAtFixedRate(new NoOpRunnable(),
+                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleWithFixedDelay1_RejectedExecutionException() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleWithFixedDelay(new NoOpRunnable(),
+                                         MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(THREADS);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks
+     * submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final CustomExecutor p = new CustomExecutor(1, threadFactory);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isShutdown());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminated());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminated());
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertFalse(q.contains(tasks[0]));
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertFalse(p.remove((Runnable)tasks[0]));
+            assertTrue(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[4]));
+            assertFalse(p.remove((Runnable)tasks[4]));
+            assertFalse(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[3]));
+            assertFalse(q.contains((Runnable)tasks[3]));
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final ScheduledFuture[] tasks = new ScheduledFuture[5];
+        final Runnable releaser = new Runnable() { public void run() {
+            for (ScheduledFuture task : tasks)
+                if (task != null) task.cancel(true); }};
+        final CustomExecutor p = new CustomExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, releaser)) {
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            int max = tasks.length;
+            if (tasks[4].cancel(true)) --max;
+            if (tasks[3].cancel(true)) --max;
+            // There must eventually be an interference-free point at
+            // which purge will not fail. (At worst, when queue is empty.)
+            long startTime = System.nanoTime();
+            do {
+                p.purge();
+                long count = p.getTaskCount();
+                if (count == max)
+                    return;
+            } while (millisElapsedSince(startTime) < LONG_DELAY_MS);
+            fail("Purge failed to remove cancelled tasks");
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final CustomExecutor p = new CustomExecutor(poolSize);
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow_delayedTasks() throws InterruptedException {
+        final CustomExecutor p = new CustomExecutor(1);
+        List<ScheduledFuture> tasks = new ArrayList<>();
+        for (int i = 0; i < 3; i++) {
+            Runnable r = new NoOpRunnable();
+            tasks.add(p.schedule(r, 9, SECONDS));
+            tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS));
+            tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS));
+        }
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(p.getQueue()));
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(queuedTasks));
+        assertEquals(tasks.size(), queuedTasks.size());
+        for (ScheduledFuture task : tasks) {
+            assertFalse(((CustomTask)task).ran);
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * By default, periodic tasks are cancelled at shutdown.
+     * By default, delayed tasks keep running after shutdown.
+     * Check that changing the default values work:
+     * - setExecuteExistingDelayedTasksAfterShutdownPolicy
+     * - setContinueExistingPeriodicTasksAfterShutdownPolicy
+     */
+    public void testShutdown_cancellation() throws Exception {
+        Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE };
+        for (Boolean policy : allBooleans)
+    {
+        final int poolSize = 2;
+        final CustomExecutor p = new CustomExecutor(poolSize);
+        final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE);
+        final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE);
+        final boolean effectiveRemovePolicy = (policy == Boolean.TRUE);
+        if (policy != null) {
+            p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy);
+            p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy);
+            p.setRemoveOnCancelPolicy(policy);
+        }
+        assertEquals(effectiveDelayedPolicy,
+                     p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertEquals(effectivePeriodicPolicy,
+                     p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        assertEquals(effectiveRemovePolicy,
+                     p.getRemoveOnCancelPolicy());
+        // Strategy: Wedge the pool with poolSize "blocker" threads
+        final AtomicInteger ran = new AtomicInteger(0);
+        final CountDownLatch poolBlocked = new CountDownLatch(poolSize);
+        final CountDownLatch unblock = new CountDownLatch(1);
+        final CountDownLatch periodicLatch1 = new CountDownLatch(2);
+        final CountDownLatch periodicLatch2 = new CountDownLatch(2);
+        Runnable task = new CheckedRunnable() { public void realRun()
+                                                    throws InterruptedException {
+            poolBlocked.countDown();
+            assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS));
+            ran.getAndIncrement();
+        }};
+        List<Future<?>> blockers = new ArrayList<>();
+        List<Future<?>> periodics = new ArrayList<>();
+        List<Future<?>> delayeds = new ArrayList<>();
+        for (int i = 0; i < poolSize; i++)
+            blockers.add(p.submit(task));
+        assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS));
+
+        periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1),
+                                            1, 1, MILLISECONDS));
+        periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
+                                               1, 1, MILLISECONDS));
+        delayeds.add(p.schedule(task, 1, MILLISECONDS));
+
+        assertTrue(p.getQueue().containsAll(periodics));
+        assertTrue(p.getQueue().containsAll(delayeds));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertFalse(p.isTerminated());
+        for (Future<?> periodic : periodics) {
+            assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled());
+            assertTrue(effectivePeriodicPolicy ^ periodic.isDone());
+        }
+        for (Future<?> delayed : delayeds) {
+            assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled());
+            assertTrue(effectiveDelayedPolicy ^ delayed.isDone());
+        }
+        if (testImplementationDetails) {
+            assertEquals(effectivePeriodicPolicy,
+                         p.getQueue().containsAll(periodics));
+            assertEquals(effectiveDelayedPolicy,
+                         p.getQueue().containsAll(delayeds));
+        }
+        // Release all pool threads
+        unblock.countDown();
+
+        for (Future<?> delayed : delayeds) {
+            if (effectiveDelayedPolicy) {
+                assertNull(delayed.get());
+            }
+        }
+        if (effectivePeriodicPolicy) {
+            assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS));
+            for (Future<?> periodic : periodics) {
+                assertTrue(periodic.cancel(false));
+                assertTrue(periodic.isCancelled());
+                assertTrue(periodic.isDone());
+            }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get());
+    }}
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e = new CustomExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p = new CustomExecutor(2);
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorTest.java b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorTest.java
new file mode 100644
index 0000000..7f85917
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ScheduledExecutorTest.java
@@ -0,0 +1,1284 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ScheduledExecutorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ScheduledExecutorTest.class);
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * delayed schedule of callable successfully executes after delay
+     */
+    public void testSchedule1() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Callable task = new CheckedCallable<Boolean>() {
+                public Boolean realCall() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                    return Boolean.TRUE;
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            assertSame(Boolean.TRUE, f.get());
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            assertTrue(done.await(0L, MILLISECONDS));
+        }
+    }
+
+    /**
+     * delayed schedule of runnable successfully executes after delay
+     */
+    public void testSchedule3() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
+            await(done);
+            assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate executes runnable after given initial delay
+     */
+    public void testSchedule4() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleAtFixedRate(task, timeoutMillis(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes runnable after given initial delay
+     */
+    public void testSchedule5() throws Exception {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final long startTime = System.nanoTime();
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable task = new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                }};
+            ScheduledFuture f =
+                p.scheduleWithFixedDelay(task, timeoutMillis(),
+                                         LONG_DELAY_MS, MILLISECONDS);
+            await(done);
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            f.cancel(true);
+        }
+    }
+
+    static class RunnableCounter implements Runnable {
+        AtomicInteger count = new AtomicInteger(0);
+        public void run() { count.getAndIncrement(); }
+    }
+
+    /**
+     * scheduleAtFixedRate executes series of tasks at given rate.
+     * Eventually, it must hold that:
+     *   cycles - 1 <= elapsedMillis/delay < cycles
+     */
+    public void testFixedRateSequence() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                final long startTime = System.nanoTime();
+                final int cycles = 8;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                final Runnable task = new CheckedRunnable() {
+                    public void realRun() { done.countDown(); }};
+                final ScheduledFuture periodicTask =
+                    p.scheduleAtFixedRate(task, 0, delay, MILLISECONDS);
+                final int totalDelayMillis = (cycles - 1) * delay;
+                await(done, totalDelayMillis + LONG_DELAY_MS);
+                periodicTask.cancel(true);
+                final long elapsedMillis = millisElapsedSince(startTime);
+                assertTrue(elapsedMillis >= totalDelayMillis);
+                if (elapsedMillis <= cycles * delay)
+                    return;
+                // else retry with longer delay
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay executes series of tasks with given period.
+     * Eventually, it must hold that each task starts at least delay and at
+     * most 2 * delay after the termination of the previous task.
+     */
+    public void testFixedDelaySequence() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int delay = 1; delay <= LONG_DELAY_MS; delay *= 3) {
+                final long startTime = System.nanoTime();
+                final AtomicLong previous = new AtomicLong(startTime);
+                final AtomicBoolean tryLongerDelay = new AtomicBoolean(false);
+                final int cycles = 8;
+                final CountDownLatch done = new CountDownLatch(cycles);
+                final int d = delay;
+                final Runnable task = new CheckedRunnable() {
+                    public void realRun() {
+                        long now = System.nanoTime();
+                        long elapsedMillis
+                            = NANOSECONDS.toMillis(now - previous.get());
+                        if (done.getCount() == cycles) { // first execution
+                            if (elapsedMillis >= d)
+                                tryLongerDelay.set(true);
+                        } else {
+                            assertTrue(elapsedMillis >= d);
+                            if (elapsedMillis >= 2 * d)
+                                tryLongerDelay.set(true);
+                        }
+                        previous.set(now);
+                        done.countDown();
+                    }};
+                final ScheduledFuture periodicTask =
+                    p.scheduleWithFixedDelay(task, 0, delay, MILLISECONDS);
+                final int totalDelayMillis = (cycles - 1) * delay;
+                await(done, totalDelayMillis + cycles * LONG_DELAY_MS);
+                periodicTask.cancel(true);
+                final long elapsedMillis = millisElapsedSince(startTime);
+                assertTrue(elapsedMillis >= totalDelayMillis);
+                if (!tryLongerDelay.get())
+                    return;
+                // else retry with longer delay
+            }
+            fail("unexpected execution rate");
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * schedule(null) throws NPE
+     */
+    public void testScheduleNull() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                TrackedCallable callable = null;
+                Future f = p.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule1_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpRunnable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule2_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * schedule callable throws RejectedExecutionException if shutdown
+     */
+    public void testSchedule3_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.schedule(new NoOpCallable(),
+                           MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleAtFixedRate throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleAtFixedRate1_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleAtFixedRate(new NoOpRunnable(),
+                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * scheduleWithFixedDelay throws RejectedExecutionException if shutdown
+     */
+    public void testScheduleWithFixedDelay1_RejectedExecutionException() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                p.scheduleWithFixedDelay(new NoOpRunnable(),
+                                         MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (RejectedExecutionException success) {
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() throws InterruptedException {
+        ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(THREADS);
+        final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+        final CountDownLatch done = new CountDownLatch(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getLargestPoolSize());
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getPoolSize());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks
+     * submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() throws InterruptedException {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ScheduledThreadPoolExecutor p =
+            new ScheduledThreadPoolExecutor(1, threadFactory);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() throws InterruptedException {
+        ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        assertFalse(p.isShutdown());
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.shutdown();
+                assertTrue(p.isShutdown());
+            } catch (SecurityException ok) {}
+        }
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminated());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminated());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertFalse(q.contains(tasks[0]));
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            ScheduledFuture[] tasks = new ScheduledFuture[5];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                Runnable r = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                tasks[i] = p.schedule(r, 1, MILLISECONDS);
+            }
+            await(threadStarted);
+            BlockingQueue<Runnable> q = p.getQueue();
+            assertFalse(p.remove((Runnable)tasks[0]));
+            assertTrue(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[4]));
+            assertFalse(p.remove((Runnable)tasks[4]));
+            assertFalse(q.contains((Runnable)tasks[4]));
+            assertTrue(q.contains((Runnable)tasks[3]));
+            assertTrue(p.remove((Runnable)tasks[3]));
+            assertFalse(q.contains((Runnable)tasks[3]));
+        }
+    }
+
+    /**
+     * purge eventually removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final ScheduledFuture[] tasks = new ScheduledFuture[5];
+        final Runnable releaser = new Runnable() { public void run() {
+            for (ScheduledFuture task : tasks)
+                if (task != null) task.cancel(true); }};
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p, releaser)) {
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
+                                      LONG_DELAY_MS, MILLISECONDS);
+            int max = tasks.length;
+            if (tasks[4].cancel(true)) --max;
+            if (tasks[3].cancel(true)) --max;
+            // There must eventually be an interference-free point at
+            // which purge will not fail. (At worst, when queue is empty.)
+            long startTime = System.nanoTime();
+            do {
+                p.purge();
+                long count = p.getTaskCount();
+                if (count == max)
+                    return;
+            } while (millisElapsedSince(startTime) < LONG_DELAY_MS);
+            fail("Purge failed to remove cancelled tasks");
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final ScheduledThreadPoolExecutor p =
+            new ScheduledThreadPoolExecutor(poolSize);
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow_delayedTasks() throws InterruptedException {
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        List<ScheduledFuture> tasks = new ArrayList<>();
+        for (int i = 0; i < 3; i++) {
+            Runnable r = new NoOpRunnable();
+            tasks.add(p.schedule(r, 9, SECONDS));
+            tasks.add(p.scheduleAtFixedRate(r, 9, 9, SECONDS));
+            tasks.add(p.scheduleWithFixedDelay(r, 9, 9, SECONDS));
+        }
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(p.getQueue()));
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        if (testImplementationDetails)
+            assertEquals(new HashSet(tasks), new HashSet(queuedTasks));
+        assertEquals(tasks.size(), queuedTasks.size());
+        for (ScheduledFuture task : tasks) {
+            assertFalse(task.isDone());
+            assertFalse(task.isCancelled());
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+    }
+
+    /**
+     * By default, periodic tasks are cancelled at shutdown.
+     * By default, delayed tasks keep running after shutdown.
+     * Check that changing the default values work:
+     * - setExecuteExistingDelayedTasksAfterShutdownPolicy
+     * - setContinueExistingPeriodicTasksAfterShutdownPolicy
+     */
+    public void testShutdown_cancellation() throws Exception {
+        Boolean[] allBooleans = { null, Boolean.FALSE, Boolean.TRUE };
+        for (Boolean policy : allBooleans)
+    {
+        final int poolSize = 2;
+        final ScheduledThreadPoolExecutor p
+            = new ScheduledThreadPoolExecutor(poolSize);
+        final boolean effectiveDelayedPolicy = (policy != Boolean.FALSE);
+        final boolean effectivePeriodicPolicy = (policy == Boolean.TRUE);
+        final boolean effectiveRemovePolicy = (policy == Boolean.TRUE);
+        if (policy != null) {
+            p.setExecuteExistingDelayedTasksAfterShutdownPolicy(policy);
+            p.setContinueExistingPeriodicTasksAfterShutdownPolicy(policy);
+            p.setRemoveOnCancelPolicy(policy);
+        }
+        assertEquals(effectiveDelayedPolicy,
+                     p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
+        assertEquals(effectivePeriodicPolicy,
+                     p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
+        assertEquals(effectiveRemovePolicy,
+                     p.getRemoveOnCancelPolicy());
+        // Strategy: Wedge the pool with poolSize "blocker" threads
+        final AtomicInteger ran = new AtomicInteger(0);
+        final CountDownLatch poolBlocked = new CountDownLatch(poolSize);
+        final CountDownLatch unblock = new CountDownLatch(1);
+        final CountDownLatch periodicLatch1 = new CountDownLatch(2);
+        final CountDownLatch periodicLatch2 = new CountDownLatch(2);
+        Runnable task = new CheckedRunnable() { public void realRun()
+                                                    throws InterruptedException {
+            poolBlocked.countDown();
+            assertTrue(unblock.await(LONG_DELAY_MS, MILLISECONDS));
+            ran.getAndIncrement();
+        }};
+        List<Future<?>> blockers = new ArrayList<>();
+        List<Future<?>> periodics = new ArrayList<>();
+        List<Future<?>> delayeds = new ArrayList<>();
+        for (int i = 0; i < poolSize; i++)
+            blockers.add(p.submit(task));
+        assertTrue(poolBlocked.await(LONG_DELAY_MS, MILLISECONDS));
+
+        periodics.add(p.scheduleAtFixedRate(countDowner(periodicLatch1),
+                                            1, 1, MILLISECONDS));
+        periodics.add(p.scheduleWithFixedDelay(countDowner(periodicLatch2),
+                                               1, 1, MILLISECONDS));
+        delayeds.add(p.schedule(task, 1, MILLISECONDS));
+
+        assertTrue(p.getQueue().containsAll(periodics));
+        assertTrue(p.getQueue().containsAll(delayeds));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        assertTrue(p.isShutdown());
+        assertFalse(p.isTerminated());
+        for (Future<?> periodic : periodics) {
+            assertTrue(effectivePeriodicPolicy ^ periodic.isCancelled());
+            assertTrue(effectivePeriodicPolicy ^ periodic.isDone());
+        }
+        for (Future<?> delayed : delayeds) {
+            assertTrue(effectiveDelayedPolicy ^ delayed.isCancelled());
+            assertTrue(effectiveDelayedPolicy ^ delayed.isDone());
+        }
+        if (testImplementationDetails) {
+            assertEquals(effectivePeriodicPolicy,
+                         p.getQueue().containsAll(periodics));
+            assertEquals(effectiveDelayedPolicy,
+                         p.getQueue().containsAll(delayeds));
+        }
+        // Release all pool threads
+        unblock.countDown();
+
+        for (Future<?> delayed : delayeds) {
+            if (effectiveDelayedPolicy) {
+                assertNull(delayed.get());
+            }
+        }
+        if (effectivePeriodicPolicy) {
+            assertTrue(periodicLatch1.await(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(periodicLatch2.await(LONG_DELAY_MS, MILLISECONDS));
+            for (Future<?> periodic : periodics) {
+                assertTrue(periodic.cancel(false));
+                assertTrue(periodic.isCancelled());
+                assertTrue(periodic.isDone());
+            }
+        }
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(2 + (effectiveDelayedPolicy ? 1 : 0), ran.get());
+    }}
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+                                                 MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e = new ScheduledThreadPoolExecutor(2);
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p = new ScheduledThreadPoolExecutor(2);
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+    /**
+     * A fixed delay task with overflowing period should not prevent a
+     * one-shot task from executing.
+     * https://bugs.openjdk.java.net/browse/JDK-8051859
+     */
+    public void testScheduleWithFixedDelay_overflow() throws Exception {
+        final CountDownLatch delayedDone = new CountDownLatch(1);
+        final CountDownLatch immediateDone = new CountDownLatch(1);
+        final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final Runnable immediate = new Runnable() { public void run() {
+                immediateDone.countDown();
+            }};
+            final Runnable delayed = new Runnable() { public void run() {
+                delayedDone.countDown();
+                p.submit(immediate);
+            }};
+            p.scheduleWithFixedDelay(delayed, 0L, Long.MAX_VALUE, SECONDS);
+            await(delayedDone);
+            await(immediateDone);
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/SemaphoreTest.java b/ojluni/src/test/java/util/concurrent/tck/SemaphoreTest.java
new file mode 100644
index 0000000..3c0321f
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/SemaphoreTest.java
@@ -0,0 +1,670 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.Collection;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class SemaphoreTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(SemaphoreTest.class);
+    }
+
+    /**
+     * Subclass to expose protected methods
+     */
+    static class PublicSemaphore extends Semaphore {
+        PublicSemaphore(int permits) { super(permits); }
+        PublicSemaphore(int permits, boolean fair) { super(permits, fair); }
+        public Collection<Thread> getQueuedThreads() {
+            return super.getQueuedThreads();
+        }
+        public boolean hasQueuedThread(Thread t) {
+            return super.getQueuedThreads().contains(t);
+        }
+        public void reducePermits(int reduction) {
+            super.reducePermits(reduction);
+        }
+    }
+
+    /**
+     * A runnable calling acquire
+     */
+    class InterruptibleLockRunnable extends CheckedRunnable {
+        final Semaphore lock;
+        InterruptibleLockRunnable(Semaphore s) { lock = s; }
+        public void realRun() {
+            try {
+                lock.acquire();
+            }
+            catch (InterruptedException ignored) {}
+        }
+    }
+
+    /**
+     * A runnable calling acquire that expects to be interrupted
+     */
+    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
+        final Semaphore lock;
+        InterruptedLockRunnable(Semaphore s) { lock = s; }
+        public void realRun() throws InterruptedException {
+            lock.acquire();
+        }
+    }
+
+    /**
+     * Spin-waits until s.hasQueuedThread(t) becomes true.
+     */
+    void waitForQueuedThread(PublicSemaphore s, Thread t) {
+        long startTime = System.nanoTime();
+        while (!s.hasQueuedThread(t)) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+        assertTrue(s.hasQueuedThreads());
+        assertTrue(t.isAlive());
+    }
+
+    /**
+     * Spin-waits until s.hasQueuedThreads() becomes true.
+     */
+    void waitForQueuedThreads(Semaphore s) {
+        long startTime = System.nanoTime();
+        while (!s.hasQueuedThreads()) {
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                throw new AssertionFailedError("timed out");
+            Thread.yield();
+        }
+    }
+
+    enum AcquireMethod {
+        acquire() {
+            void acquire(Semaphore s) throws InterruptedException {
+                s.acquire();
+            }
+        },
+        acquireN() {
+            void acquire(Semaphore s, int permits) throws InterruptedException {
+                s.acquire(permits);
+            }
+        },
+        acquireUninterruptibly() {
+            void acquire(Semaphore s) {
+                s.acquireUninterruptibly();
+            }
+        },
+        acquireUninterruptiblyN() {
+            void acquire(Semaphore s, int permits) {
+                s.acquireUninterruptibly(permits);
+            }
+        },
+        tryAcquire() {
+            void acquire(Semaphore s) {
+                assertTrue(s.tryAcquire());
+            }
+        },
+        tryAcquireN() {
+            void acquire(Semaphore s, int permits) {
+                assertTrue(s.tryAcquire(permits));
+            }
+        },
+        tryAcquireTimed() {
+            void acquire(Semaphore s) throws InterruptedException {
+                assertTrue(s.tryAcquire(2 * LONG_DELAY_MS, MILLISECONDS));
+            }
+        },
+        tryAcquireTimedN {
+            void acquire(Semaphore s, int permits) throws InterruptedException {
+                assertTrue(s.tryAcquire(permits, 2 * LONG_DELAY_MS, MILLISECONDS));
+            }
+        };
+
+        // Intentionally meta-circular
+
+        /** Acquires 1 permit. */
+        void acquire(Semaphore s) throws InterruptedException {
+            acquire(s, 1);
+        }
+        /** Acquires the given number of permits. */
+        void acquire(Semaphore s, int permits) throws InterruptedException {
+            for (int i = 0; i < permits; i++)
+                acquire(s);
+        }
+    }
+
+    /**
+     * Zero, negative, and positive initial values are allowed in constructor
+     */
+    public void testConstructor()      { testConstructor(false); }
+    public void testConstructor_fair() { testConstructor(true); }
+    public void testConstructor(boolean fair) {
+        for (int permits : new int[] { -42, -1, 0, 1, 42 }) {
+            Semaphore s = new Semaphore(permits, fair);
+            assertEquals(permits, s.availablePermits());
+            assertEquals(fair, s.isFair());
+        }
+    }
+
+    /**
+     * Constructor without fairness argument behaves as nonfair
+     */
+    public void testConstructorDefaultsToNonFair() {
+        for (int permits : new int[] { -42, -1, 0, 1, 42 }) {
+            Semaphore s = new Semaphore(permits);
+            assertEquals(permits, s.availablePermits());
+            assertFalse(s.isFair());
+        }
+    }
+
+    /**
+     * tryAcquire succeeds when sufficient permits, else fails
+     */
+    public void testTryAcquireInSameThread()      { testTryAcquireInSameThread(false); }
+    public void testTryAcquireInSameThread_fair() { testTryAcquireInSameThread(true); }
+    public void testTryAcquireInSameThread(boolean fair) {
+        Semaphore s = new Semaphore(2, fair);
+        assertEquals(2, s.availablePermits());
+        assertTrue(s.tryAcquire());
+        assertTrue(s.tryAcquire());
+        assertEquals(0, s.availablePermits());
+        assertFalse(s.tryAcquire());
+        assertFalse(s.tryAcquire());
+        assertEquals(0, s.availablePermits());
+    }
+
+    /**
+     * timed tryAcquire times out
+     */
+    public void testTryAcquire_timeout()      { testTryAcquire_timeout(false); }
+    public void testTryAcquire_timeout_fair() { testTryAcquire_timeout(true); }
+    public void testTryAcquire_timeout(boolean fair) {
+        Semaphore s = new Semaphore(0, fair);
+        long startTime = System.nanoTime();
+        try { assertFalse(s.tryAcquire(timeoutMillis(), MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+    }
+
+    /**
+     * timed tryAcquire(N) times out
+     */
+    public void testTryAcquireN_timeout()      { testTryAcquireN_timeout(false); }
+    public void testTryAcquireN_timeout_fair() { testTryAcquireN_timeout(true); }
+    public void testTryAcquireN_timeout(boolean fair) {
+        Semaphore s = new Semaphore(2, fair);
+        long startTime = System.nanoTime();
+        try { assertFalse(s.tryAcquire(3, timeoutMillis(), MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+    }
+
+    /**
+     * acquire(), acquire(N), timed tryAcquired, timed tryAcquire(N)
+     * are interruptible
+     */
+    public void testInterruptible_acquire()               { testInterruptible(false, AcquireMethod.acquire); }
+    public void testInterruptible_acquire_fair()          { testInterruptible(true,  AcquireMethod.acquire); }
+    public void testInterruptible_acquireN()              { testInterruptible(false, AcquireMethod.acquireN); }
+    public void testInterruptible_acquireN_fair()         { testInterruptible(true,  AcquireMethod.acquireN); }
+    public void testInterruptible_tryAcquireTimed()       { testInterruptible(false, AcquireMethod.tryAcquireTimed); }
+    public void testInterruptible_tryAcquireTimed_fair()  { testInterruptible(true,  AcquireMethod.tryAcquireTimed); }
+    public void testInterruptible_tryAcquireTimedN()      { testInterruptible(false, AcquireMethod.tryAcquireTimedN); }
+    public void testInterruptible_tryAcquireTimedN_fair() { testInterruptible(true,  AcquireMethod.tryAcquireTimedN); }
+    public void testInterruptible(boolean fair, final AcquireMethod acquirer) {
+        final PublicSemaphore s = new PublicSemaphore(0, fair);
+        final Semaphore pleaseInterrupt = new Semaphore(0, fair);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                // Interrupt before acquire
+                Thread.currentThread().interrupt();
+                try {
+                    acquirer.acquire(s);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                // Interrupt during acquire
+                try {
+                    acquirer.acquire(s);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                // Interrupt before acquire(N)
+                Thread.currentThread().interrupt();
+                try {
+                    acquirer.acquire(s, 3);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+
+                pleaseInterrupt.release();
+
+                // Interrupt during acquire(N)
+                try {
+                    acquirer.acquire(s, 3);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        waitForQueuedThread(s, t);
+        t.interrupt();
+        await(pleaseInterrupt);
+        waitForQueuedThread(s, t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * acquireUninterruptibly(), acquireUninterruptibly(N) are
+     * uninterruptible
+     */
+    public void testUninterruptible_acquireUninterruptibly()       { testUninterruptible(false, AcquireMethod.acquireUninterruptibly); }
+    public void testUninterruptible_acquireUninterruptibly_fair()  { testUninterruptible(true,  AcquireMethod.acquireUninterruptibly); }
+    public void testUninterruptible_acquireUninterruptiblyN()      { testUninterruptible(false, AcquireMethod.acquireUninterruptiblyN); }
+    public void testUninterruptible_acquireUninterruptiblyN_fair() { testUninterruptible(true,  AcquireMethod.acquireUninterruptiblyN); }
+    public void testUninterruptible(boolean fair, final AcquireMethod acquirer) {
+        final PublicSemaphore s = new PublicSemaphore(0, fair);
+        final Semaphore pleaseInterrupt = new Semaphore(-1, fair);
+
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Interrupt before acquire
+                pleaseInterrupt.release();
+                Thread.currentThread().interrupt();
+                acquirer.acquire(s);
+                assertTrue(Thread.interrupted());
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Interrupt during acquire
+                pleaseInterrupt.release();
+                acquirer.acquire(s);
+                assertTrue(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        waitForQueuedThread(s, t1);
+        waitForQueuedThread(s, t2);
+        t2.interrupt();
+
+        assertThreadStaysAlive(t1);
+        assertTrue(t2.isAlive());
+
+        s.release(2);
+
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * hasQueuedThreads reports whether there are waiting threads
+     */
+    public void testHasQueuedThreads()      { testHasQueuedThreads(false); }
+    public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); }
+    public void testHasQueuedThreads(boolean fair) {
+        final PublicSemaphore lock = new PublicSemaphore(1, fair);
+        assertFalse(lock.hasQueuedThreads());
+        lock.acquireUninterruptibly();
+        Thread t1 = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.hasQueuedThreads());
+        Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock));
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.hasQueuedThreads());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertTrue(lock.hasQueuedThreads());
+        lock.release();
+        awaitTermination(t2);
+        assertFalse(lock.hasQueuedThreads());
+    }
+
+    /**
+     * getQueueLength reports number of waiting threads
+     */
+    public void testGetQueueLength()      { testGetQueueLength(false); }
+    public void testGetQueueLength_fair() { testGetQueueLength(true); }
+    public void testGetQueueLength(boolean fair) {
+        final PublicSemaphore lock = new PublicSemaphore(1, fair);
+        assertEquals(0, lock.getQueueLength());
+        lock.acquireUninterruptibly();
+        Thread t1 = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t1);
+        assertEquals(1, lock.getQueueLength());
+        Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock));
+        waitForQueuedThread(lock, t2);
+        assertEquals(2, lock.getQueueLength());
+        t1.interrupt();
+        awaitTermination(t1);
+        assertEquals(1, lock.getQueueLength());
+        lock.release();
+        awaitTermination(t2);
+        assertEquals(0, lock.getQueueLength());
+    }
+
+    /**
+     * getQueuedThreads includes waiting threads
+     */
+    public void testGetQueuedThreads()      { testGetQueuedThreads(false); }
+    public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); }
+    public void testGetQueuedThreads(boolean fair) {
+        final PublicSemaphore lock = new PublicSemaphore(1, fair);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        lock.acquireUninterruptibly();
+        assertTrue(lock.getQueuedThreads().isEmpty());
+        Thread t1 = newStartedThread(new InterruptedLockRunnable(lock));
+        waitForQueuedThread(lock, t1);
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock));
+        waitForQueuedThread(lock, t2);
+        assertTrue(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        t1.interrupt();
+        awaitTermination(t1);
+        assertFalse(lock.getQueuedThreads().contains(t1));
+        assertTrue(lock.getQueuedThreads().contains(t2));
+        lock.release();
+        awaitTermination(t2);
+        assertTrue(lock.getQueuedThreads().isEmpty());
+    }
+
+    /**
+     * drainPermits reports and removes given number of permits
+     */
+    public void testDrainPermits()      { testDrainPermits(false); }
+    public void testDrainPermits_fair() { testDrainPermits(true); }
+    public void testDrainPermits(boolean fair) {
+        Semaphore s = new Semaphore(0, fair);
+        assertEquals(0, s.availablePermits());
+        assertEquals(0, s.drainPermits());
+        s.release(10);
+        assertEquals(10, s.availablePermits());
+        assertEquals(10, s.drainPermits());
+        assertEquals(0, s.availablePermits());
+        assertEquals(0, s.drainPermits());
+    }
+
+    /**
+     * release(-N) throws IllegalArgumentException
+     */
+    public void testReleaseIAE()      { testReleaseIAE(false); }
+    public void testReleaseIAE_fair() { testReleaseIAE(true); }
+    public void testReleaseIAE(boolean fair) {
+        Semaphore s = new Semaphore(10, fair);
+        try {
+            s.release(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * reducePermits(-N) throws IllegalArgumentException
+     */
+    public void testReducePermitsIAE()      { testReducePermitsIAE(false); }
+    public void testReducePermitsIAE_fair() { testReducePermitsIAE(true); }
+    public void testReducePermitsIAE(boolean fair) {
+        PublicSemaphore s = new PublicSemaphore(10, fair);
+        try {
+            s.reducePermits(-1);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * reducePermits reduces number of permits
+     */
+    public void testReducePermits()      { testReducePermits(false); }
+    public void testReducePermits_fair() { testReducePermits(true); }
+    public void testReducePermits(boolean fair) {
+        PublicSemaphore s = new PublicSemaphore(10, fair);
+        assertEquals(10, s.availablePermits());
+        s.reducePermits(0);
+        assertEquals(10, s.availablePermits());
+        s.reducePermits(1);
+        assertEquals(9, s.availablePermits());
+        s.reducePermits(10);
+        assertEquals(-1, s.availablePermits());
+        s.reducePermits(10);
+        assertEquals(-11, s.availablePermits());
+        s.reducePermits(0);
+        assertEquals(-11, s.availablePermits());
+    }
+
+    /**
+     * a reserialized semaphore has same number of permits and
+     * fairness, but no queued threads
+     */
+    public void testSerialization()      { testSerialization(false); }
+    public void testSerialization_fair() { testSerialization(true); }
+    public void testSerialization(boolean fair) {
+        try {
+            Semaphore s = new Semaphore(3, fair);
+            s.acquire();
+            s.acquire();
+            s.release();
+
+            Semaphore clone = serialClone(s);
+            assertEquals(fair, s.isFair());
+            assertEquals(fair, clone.isFair());
+            assertEquals(2, s.availablePermits());
+            assertEquals(2, clone.availablePermits());
+            clone.acquire();
+            clone.acquire();
+            clone.release();
+            assertEquals(2, s.availablePermits());
+            assertEquals(1, clone.availablePermits());
+            assertFalse(s.hasQueuedThreads());
+            assertFalse(clone.hasQueuedThreads());
+        } catch (InterruptedException e) { threadUnexpectedException(e); }
+
+        {
+            PublicSemaphore s = new PublicSemaphore(0, fair);
+            Thread t = newStartedThread(new InterruptibleLockRunnable(s));
+            // waitForQueuedThreads(s); // suffers from "flicker", so ...
+            waitForQueuedThread(s, t);  // ... we use this instead
+            PublicSemaphore clone = serialClone(s);
+            assertEquals(fair, s.isFair());
+            assertEquals(fair, clone.isFair());
+            assertEquals(0, s.availablePermits());
+            assertEquals(0, clone.availablePermits());
+            assertTrue(s.hasQueuedThreads());
+            assertFalse(clone.hasQueuedThreads());
+            s.release();
+            awaitTermination(t);
+            assertFalse(s.hasQueuedThreads());
+            assertFalse(clone.hasQueuedThreads());
+        }
+    }
+
+    /**
+     * tryAcquire(n) succeeds when sufficient permits, else fails
+     */
+    public void testTryAcquireNInSameThread()      { testTryAcquireNInSameThread(false); }
+    public void testTryAcquireNInSameThread_fair() { testTryAcquireNInSameThread(true); }
+    public void testTryAcquireNInSameThread(boolean fair) {
+        Semaphore s = new Semaphore(2, fair);
+        assertEquals(2, s.availablePermits());
+        assertFalse(s.tryAcquire(3));
+        assertEquals(2, s.availablePermits());
+        assertTrue(s.tryAcquire(2));
+        assertEquals(0, s.availablePermits());
+        assertFalse(s.tryAcquire(1));
+        assertFalse(s.tryAcquire(2));
+        assertEquals(0, s.availablePermits());
+    }
+
+    /**
+     * acquire succeeds if permits available
+     */
+    public void testReleaseAcquireSameThread_acquire()       { testReleaseAcquireSameThread(false, AcquireMethod.acquire); }
+    public void testReleaseAcquireSameThread_acquire_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.acquire); }
+    public void testReleaseAcquireSameThread_acquireN()      { testReleaseAcquireSameThread(false, AcquireMethod.acquireN); }
+    public void testReleaseAcquireSameThread_acquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireN); }
+    public void testReleaseAcquireSameThread_acquireUninterruptibly()       { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_acquireUninterruptibly_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_acquireUninterruptiblyN()      { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_acquireUninterruptiblyN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireSameThread_tryAcquire()       { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquire); }
+    public void testReleaseAcquireSameThread_tryAcquire_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquire); }
+    public void testReleaseAcquireSameThread_tryAcquireN()      { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireN); }
+    public void testReleaseAcquireSameThread_tryAcquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireN); }
+    public void testReleaseAcquireSameThread_tryAcquireTimed()       { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireSameThread_tryAcquireTimed_fair()  { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireSameThread_tryAcquireTimedN()      { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireSameThread_tryAcquireTimedN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireSameThread(boolean fair,
+                                             final AcquireMethod acquirer) {
+        Semaphore s = new Semaphore(1, fair);
+        for (int i = 1; i < 6; i++) {
+            s.release(i);
+            assertEquals(1 + i, s.availablePermits());
+            try {
+                acquirer.acquire(s, i);
+            } catch (InterruptedException e) { threadUnexpectedException(e); }
+            assertEquals(1, s.availablePermits());
+        }
+    }
+
+    /**
+     * release in one thread enables acquire in another thread
+     */
+    public void testReleaseAcquireDifferentThreads_acquire()       { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquire); }
+    public void testReleaseAcquireDifferentThreads_acquire_fair()  { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquire); }
+    public void testReleaseAcquireDifferentThreads_acquireN()      { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireN); }
+    public void testReleaseAcquireDifferentThreads_acquireN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireN); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptibly()       { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptibly_fair()  { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN()      { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimed()       { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimed_fair()  { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimed); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimedN()      { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireDifferentThreads_tryAcquireTimedN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimedN); }
+    public void testReleaseAcquireDifferentThreads(boolean fair,
+                                                   final AcquireMethod acquirer) {
+        final Semaphore s = new Semaphore(0, fair);
+        final int rounds = 4;
+        long startTime = System.nanoTime();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                for (int i = 0; i < rounds; i++) {
+                    assertFalse(s.hasQueuedThreads());
+                    if (i % 2 == 0)
+                        acquirer.acquire(s);
+                    else
+                        acquirer.acquire(s, 3);
+                }}});
+
+        for (int i = 0; i < rounds; i++) {
+            while (! (s.availablePermits() == 0 && s.hasQueuedThreads()))
+                Thread.yield();
+            assertTrue(t.isAlive());
+            if (i % 2 == 0)
+                s.release();
+            else
+                s.release(3);
+        }
+        awaitTermination(t);
+        assertEquals(0, s.availablePermits());
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+    }
+
+    /**
+     * fair locks are strictly FIFO
+     */
+    public void testFairLocksFifo() {
+        final PublicSemaphore s = new PublicSemaphore(1, true);
+        final CountDownLatch pleaseRelease = new CountDownLatch(1);
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Will block; permits are available, but not three
+                s.acquire(3);
+            }});
+
+        waitForQueuedThread(s, t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                // Will fail, even though 1 permit is available
+                assertFalse(s.tryAcquire(0L, MILLISECONDS));
+                assertFalse(s.tryAcquire(1, 0L, MILLISECONDS));
+
+                // untimed tryAcquire will barge and succeed
+                assertTrue(s.tryAcquire());
+                s.release(2);
+                assertTrue(s.tryAcquire(2));
+                s.release();
+
+                pleaseRelease.countDown();
+                // Will queue up behind t1, even though 1 permit is available
+                s.acquire();
+            }});
+
+        await(pleaseRelease);
+        waitForQueuedThread(s, t2);
+        s.release(2);
+        awaitTermination(t1);
+        assertTrue(t2.isAlive());
+        s.release();
+        awaitTermination(t2);
+    }
+
+    /**
+     * toString indicates current number of permits
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        PublicSemaphore s = new PublicSemaphore(0, fair);
+        assertTrue(s.toString().contains("Permits = 0"));
+        s.release();
+        assertTrue(s.toString().contains("Permits = 1"));
+        s.release(2);
+        assertTrue(s.toString().contains("Permits = 3"));
+        s.reducePermits(5);
+        assertTrue(s.toString().contains("Permits = -2"));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/SplittableRandomTest.java b/ojluni/src/test/java/util/concurrent/tck/SplittableRandomTest.java
new file mode 100644
index 0000000..99c4bde
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/SplittableRandomTest.java
@@ -0,0 +1,556 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.SplittableRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class SplittableRandomTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(SplittableRandomTest.class);
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * 1. Many of the test methods are adapted from ThreadLocalRandomTest.
+     *
+     * 2. These tests do not check for random number generator quality.
+     * But we check for minimal API compliance by requiring that
+     * repeated calls to nextX methods, up to NCALLS tries, produce at
+     * least two distinct results. (In some possible universe, a
+     * "correct" implementation might fail, but the odds are vastly
+     * less than that of encountering a hardware failure while running
+     * the test.) For bounded nextX methods, we sample various
+     * intervals across multiples of primes. In other tests, we repeat
+     * under REPS different values.
+     */
+
+    // max numbers of calls to detect getting stuck on one value
+    static final int NCALLS = 10000;
+
+    // max sampled int bound
+    static final int MAX_INT_BOUND = (1 << 26);
+
+    // max sampled long bound
+    static final long MAX_LONG_BOUND = (1L << 40);
+
+    // Number of replications for other checks
+    static final int REPS =
+        Integer.getInteger("SplittableRandomTest.reps", 4);
+
+    /**
+     * Repeated calls to nextInt produce at least two distinct results
+     */
+    public void testNextInt() {
+        SplittableRandom sr = new SplittableRandom();
+        int f = sr.nextInt();
+        int i = 0;
+        while (i < NCALLS && sr.nextInt() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextLong produce at least two distinct results
+     */
+    public void testNextLong() {
+        SplittableRandom sr = new SplittableRandom();
+        long f = sr.nextLong();
+        int i = 0;
+        while (i < NCALLS && sr.nextLong() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextDouble produce at least two distinct results
+     */
+    public void testNextDouble() {
+        SplittableRandom sr = new SplittableRandom();
+        double f = sr.nextDouble();
+        int i = 0;
+        while (i < NCALLS && sr.nextDouble() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Two SplittableRandoms created with the same seed produce the
+     * same values for nextLong.
+     */
+    public void testSeedConstructor() {
+        for (long seed = 2; seed < MAX_LONG_BOUND; seed += 15485863) {
+            SplittableRandom sr1 = new SplittableRandom(seed);
+            SplittableRandom sr2 = new SplittableRandom(seed);
+            for (int i = 0; i < REPS; ++i)
+                assertEquals(sr1.nextLong(), sr2.nextLong());
+        }
+    }
+
+    /**
+     * A SplittableRandom produced by split() of a default-constructed
+     * SplittableRandom generates a different sequence
+     */
+    public void testSplit1() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int reps = 0; reps < REPS; ++reps) {
+            SplittableRandom sc = sr.split();
+            int i = 0;
+            while (i < NCALLS && sr.nextLong() == sc.nextLong())
+                ++i;
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * A SplittableRandom produced by split() of a seeded-constructed
+     * SplittableRandom generates a different sequence
+     */
+    public void testSplit2() {
+        SplittableRandom sr = new SplittableRandom(12345);
+        for (int reps = 0; reps < REPS; ++reps) {
+            SplittableRandom sc = sr.split();
+            int i = 0;
+            while (i < NCALLS && sr.nextLong() == sc.nextLong())
+                ++i;
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextInt(non-positive) throws IllegalArgumentException
+     */
+    public void testNextIntBoundNonPositive() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextInt(-17),
+            () -> sr.nextInt(0),
+            () -> sr.nextInt(Integer.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextInt(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextIntBadBounds() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextInt(17, 2),
+            () -> sr.nextInt(-42, -42),
+            () -> sr.nextInt(Integer.MAX_VALUE, Integer.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextInt(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int i = 0; i < 2; i++) assertEquals(0, sr.nextInt(1));
+        // sample bound space across prime number increments
+        for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
+            int f = sr.nextInt(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            int j;
+            while (i < NCALLS &&
+                   (j = sr.nextInt(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextInt(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded2() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
+                int f = sr.nextInt(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                int j;
+                while (i < NCALLS &&
+                       (j = sr.nextInt(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextLong(non-positive) throws IllegalArgumentException
+     */
+    public void testNextLongBoundNonPositive() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextLong(-17L),
+            () -> sr.nextLong(0L),
+            () -> sr.nextLong(Long.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextLong(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextLongBadBounds() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextLong(17L, 2L),
+            () -> sr.nextLong(-42L, -42L),
+            () -> sr.nextLong(Long.MAX_VALUE, Long.MIN_VALUE),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextLong(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded() {
+        SplittableRandom sr = new SplittableRandom();
+        for (int i = 0; i < 2; i++) assertEquals(0L, sr.nextLong(1L));
+        for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
+            long f = sr.nextLong(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            long j;
+            while (i < NCALLS &&
+                   (j = sr.nextLong(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextLong(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded2() {
+        SplittableRandom sr = new SplittableRandom();
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                long f = sr.nextLong(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                long j;
+                while (i < NCALLS &&
+                       (j = sr.nextLong(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextDouble(non-positive) throws IllegalArgumentException
+     */
+    public void testNextDoubleBoundNonPositive() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextDouble(-17.0d),
+            () -> sr.nextDouble(0.0d),
+            () -> sr.nextDouble(-Double.MIN_VALUE),
+            () -> sr.nextDouble(Double.NEGATIVE_INFINITY),
+            () -> sr.nextDouble(Double.NaN),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * nextDouble(! (least < bound)) throws IllegalArgumentException
+     */
+    public void testNextDoubleBadBounds() {
+        SplittableRandom sr = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> sr.nextDouble(17.0d, 2.0d),
+            () -> sr.nextDouble(-42.0d, -42.0d),
+            () -> sr.nextDouble(Double.MAX_VALUE, Double.MIN_VALUE),
+            () -> sr.nextDouble(Double.NaN, 0.0d),
+            () -> sr.nextDouble(0.0d, Double.NaN),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    // TODO: Test infinite bounds!
+    //() -> sr.nextDouble(Double.NEGATIVE_INFINITY, 0.0d),
+    //() -> sr.nextDouble(0.0d, Double.POSITIVE_INFINITY),
+
+    /**
+     * nextDouble(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextDoubleBounded2() {
+        SplittableRandom sr = new SplittableRandom();
+        for (double least = 0.0001; least < 1.0e20; least *= 8) {
+            for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
+                double f = sr.nextDouble(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                double j;
+                while (i < NCALLS &&
+                       (j = sr.nextDouble(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * Invoking sized ints, long, doubles, with negative sizes throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamSize() {
+        SplittableRandom r = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> { java.util.stream.IntStream x = r.ints(-1L); },
+            () -> { java.util.stream.IntStream x = r.ints(-1L, 2, 3); },
+            () -> { java.util.stream.LongStream x = r.longs(-1L); },
+            () -> { java.util.stream.LongStream x = r.longs(-1L, -1L, 1L); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(-1L); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(-1L, .5, .6); },
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * Invoking bounded ints, long, doubles, with illegal bounds throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamBounds() {
+        SplittableRandom r = new SplittableRandom();
+        Runnable[] throwingActions = {
+            () -> { java.util.stream.IntStream x = r.ints(2, 1); },
+            () -> { java.util.stream.IntStream x = r.ints(10, 42, 42); },
+            () -> { java.util.stream.LongStream x = r.longs(-1L, -1L); },
+            () -> { java.util.stream.LongStream x = r.longs(10, 1L, -2L); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(0.0, 0.0); },
+            () -> { java.util.stream.DoubleStream x = r.doubles(10, .5, .4); },
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * A parallel sized stream of ints generates the given number of values
+     */
+    public void testIntsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.ints(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of longs generates the given number of values
+     */
+    public void testLongsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.longs(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of doubles generates the given number of values
+     */
+    public void testDoublesCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.doubles(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded ints is within bounds
+     */
+    public void testBoundedInts() {
+        AtomicInteger fails = new AtomicInteger(0);
+        SplittableRandom r = new SplittableRandom();
+        long size = 12345L;
+        for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
+                final int lo = least, hi = bound;
+                r.ints(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded longs is within bounds
+     */
+    public void testBoundedLongs() {
+        AtomicInteger fails = new AtomicInteger(0);
+        SplittableRandom r = new SplittableRandom();
+        long size = 123L;
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                final long lo = least, hi = bound;
+                r.longs(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded doubles is within bounds
+     */
+    public void testBoundedDoubles() {
+        AtomicInteger fails = new AtomicInteger(0);
+        SplittableRandom r = new SplittableRandom();
+        long size = 456;
+        for (double least = 0.00011; least < 1.0e20; least *= 9) {
+            for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
+                final double lo = least, hi = bound;
+                r.doubles(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * A parallel unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.ints().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.longs().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCount() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.doubles().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCountSeq() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.ints().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCountSeq() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.longs().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCountSeq() {
+        LongAdder counter = new LongAdder();
+        SplittableRandom r = new SplittableRandom();
+        long size = 100;
+        r.doubles().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/StampedLockTest.java b/ojluni/src/test/java/util/concurrent/tck/StampedLockTest.java
new file mode 100644
index 0000000..5ca2410
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/StampedLockTest.java
@@ -0,0 +1,1210 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.StampedLock;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class StampedLockTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(StampedLockTest.class);
+    }
+
+    /**
+     * Releases write lock, checking isWriteLocked before and after
+     */
+    void releaseWriteLock(StampedLock lock, long stamp) {
+        assertTrue(lock.isWriteLocked());
+        assertValid(lock, stamp);
+        lock.unlockWrite(stamp);
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.validate(stamp));
+    }
+
+    /**
+     * Releases read lock, checking isReadLocked before and after
+     */
+    void releaseReadLock(StampedLock lock, long stamp) {
+        assertTrue(lock.isReadLocked());
+        assertValid(lock, stamp);
+        lock.unlockRead(stamp);
+        assertFalse(lock.isReadLocked());
+        assertTrue(lock.validate(stamp));
+    }
+
+    long assertNonZero(long v) {
+        assertTrue(v != 0L);
+        return v;
+    }
+
+    long assertValid(StampedLock lock, long stamp) {
+        assertTrue(stamp != 0L);
+        assertTrue(lock.validate(stamp));
+        return stamp;
+    }
+
+    void assertUnlocked(StampedLock lock) {
+        assertFalse(lock.isReadLocked());
+        assertFalse(lock.isWriteLocked());
+        assertEquals(0, lock.getReadLockCount());
+        assertValid(lock, lock.tryOptimisticRead());
+    }
+
+    List<Action> lockLockers(Lock lock) {
+        List<Action> lockers = new ArrayList<>();
+        lockers.add(() -> lock.lock());
+        lockers.add(() -> lock.lockInterruptibly());
+        lockers.add(() -> lock.tryLock());
+        lockers.add(() -> lock.tryLock(Long.MIN_VALUE, DAYS));
+        lockers.add(() -> lock.tryLock(0L, DAYS));
+        lockers.add(() -> lock.tryLock(Long.MAX_VALUE, DAYS));
+        return lockers;
+    }
+
+    List<Function<StampedLock, Long>> readLockers() {
+        List<Function<StampedLock, Long>> readLockers = new ArrayList<>();
+        readLockers.add(sl -> sl.readLock());
+        readLockers.add(sl -> sl.tryReadLock());
+        readLockers.add(sl -> readLockInterruptiblyUninterrupted(sl));
+        readLockers.add(sl -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
+        readLockers.add(sl -> tryReadLockUninterrupted(sl, 0L, DAYS));
+        readLockers.add(sl -> sl.tryConvertToReadLock(sl.tryOptimisticRead()));
+        return readLockers;
+    }
+
+    List<BiConsumer<StampedLock, Long>> readUnlockers() {
+        List<BiConsumer<StampedLock, Long>> readUnlockers = new ArrayList<>();
+        readUnlockers.add((sl, stamp) -> sl.unlockRead(stamp));
+        readUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockRead()));
+        readUnlockers.add((sl, stamp) -> sl.asReadLock().unlock());
+        readUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+        readUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
+        return readUnlockers;
+    }
+
+    List<Function<StampedLock, Long>> writeLockers() {
+        List<Function<StampedLock, Long>> writeLockers = new ArrayList<>();
+        writeLockers.add(sl -> sl.writeLock());
+        writeLockers.add(sl -> sl.tryWriteLock());
+        writeLockers.add(sl -> writeLockInterruptiblyUninterrupted(sl));
+        writeLockers.add(sl -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
+        writeLockers.add(sl -> tryWriteLockUninterrupted(sl, 0L, DAYS));
+        writeLockers.add(sl -> sl.tryConvertToWriteLock(sl.tryOptimisticRead()));
+        return writeLockers;
+    }
+
+    List<BiConsumer<StampedLock, Long>> writeUnlockers() {
+        List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>();
+        writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp));
+        writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite()));
+        writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock());
+        writeUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+        writeUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
+        return writeUnlockers;
+    }
+
+    /**
+     * Constructed StampedLock is in unlocked state
+     */
+    public void testConstructor() {
+        assertUnlocked(new StampedLock());
+    }
+
+    /**
+     * write-locking, then unlocking, an unlocked lock succeed
+     */
+    public void testWriteLock_lockUnlock() {
+        StampedLock lock = new StampedLock();
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            assertFalse(lock.isWriteLocked());
+            assertFalse(lock.isReadLocked());
+            assertEquals(0, lock.getReadLockCount());
+
+            long s = writeLocker.apply(lock);
+            assertValid(lock, s);
+            assertTrue(lock.isWriteLocked());
+            assertFalse(lock.isReadLocked());
+            assertEquals(0, lock.getReadLockCount());
+            writeUnlocker.accept(lock, s);
+            assertUnlocked(lock);
+        }
+    }
+
+    /**
+     * read-locking, then unlocking, an unlocked lock succeed
+     */
+    public void testReadLock_lockUnlock() {
+        StampedLock lock = new StampedLock();
+
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = 42;
+            for (int i = 0; i < 2; i++) {
+                s = assertValid(lock, readLocker.apply(lock));
+                assertFalse(lock.isWriteLocked());
+                assertTrue(lock.isReadLocked());
+                assertEquals(i + 1, lock.getReadLockCount());
+            }
+            for (int i = 0; i < 2; i++) {
+                assertFalse(lock.isWriteLocked());
+                assertTrue(lock.isReadLocked());
+                assertEquals(2 - i, lock.getReadLockCount());
+                readUnlocker.accept(lock, s);
+            }
+            assertUnlocked(lock);
+        }
+    }
+
+    /**
+     * tryUnlockWrite fails if not write locked
+     */
+    public void testTryUnlockWrite_failure() {
+        StampedLock lock = new StampedLock();
+        assertFalse(lock.tryUnlockWrite());
+
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = assertValid(lock, readLocker.apply(lock));
+            assertFalse(lock.tryUnlockWrite());
+            assertTrue(lock.isReadLocked());
+            readUnlocker.accept(lock, s);
+            assertUnlocked(lock);
+        }
+    }
+
+    /**
+     * tryUnlockRead fails if not read locked
+     */
+    public void testTryUnlockRead_failure() {
+        StampedLock lock = new StampedLock();
+        assertFalse(lock.tryUnlockRead());
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            long s = writeLocker.apply(lock);
+            assertFalse(lock.tryUnlockRead());
+            assertTrue(lock.isWriteLocked());
+            writeUnlocker.accept(lock, s);
+            assertUnlocked(lock);
+        }
+    }
+
+    /**
+     * validate(0L) fails
+     */
+    public void testValidate0() {
+        StampedLock lock = new StampedLock();
+        assertFalse(lock.validate(0L));
+    }
+
+    /**
+     * A stamp obtained from a successful lock operation validates while the lock is held
+     */
+    public void testValidate() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = assertNonZero(readLocker.apply(lock));
+            assertTrue(lock.validate(s));
+            readUnlocker.accept(lock, s);
+        }
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            long s = assertNonZero(writeLocker.apply(lock));
+            assertTrue(lock.validate(s));
+            writeUnlocker.accept(lock, s);
+        }
+    }
+
+    /**
+     * A stamp obtained from an unsuccessful lock operation does not validate
+     */
+    public void testValidate2() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s = assertNonZero(lock.writeLock());
+        assertTrue(lock.validate(s));
+        assertFalse(lock.validate(lock.tryWriteLock()));
+        assertFalse(lock.validate(lock.tryWriteLock(0L, SECONDS)));
+        assertFalse(lock.validate(lock.tryReadLock()));
+        assertFalse(lock.validate(lock.tryReadLock(0L, SECONDS)));
+        assertFalse(lock.validate(lock.tryOptimisticRead()));
+        lock.unlockWrite(s);
+    }
+
+    void assertThrowInterruptedExceptionWhenPreInterrupted(Action[] actions) {
+        for (Action action : actions) {
+            Thread.currentThread().interrupt();
+            try {
+                action.run();
+                shouldThrow();
+            }
+            catch (InterruptedException success) {}
+            catch (Throwable fail) { threadUnexpectedException(fail); }
+            assertFalse(Thread.interrupted());
+        }
+    }
+
+    /**
+     * interruptible operations throw InterruptedException when pre-interrupted
+     */
+    public void testInterruptibleOperationsThrowInterruptedExceptionWhenPreInterrupted() {
+        final StampedLock lock = new StampedLock();
+
+        Action[] interruptibleLockActions = {
+            () -> lock.writeLockInterruptibly(),
+            () -> lock.tryWriteLock(Long.MIN_VALUE, DAYS),
+            () -> lock.tryWriteLock(Long.MAX_VALUE, DAYS),
+            () -> lock.readLockInterruptibly(),
+            () -> lock.tryReadLock(Long.MIN_VALUE, DAYS),
+            () -> lock.tryReadLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asWriteLock().lockInterruptibly(),
+            () -> lock.asWriteLock().tryLock(0L, DAYS),
+            () -> lock.asWriteLock().tryLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asReadLock().lockInterruptibly(),
+            () -> lock.asReadLock().tryLock(0L, DAYS),
+            () -> lock.asReadLock().tryLock(Long.MAX_VALUE, DAYS),
+        };
+        shuffle(interruptibleLockActions);
+
+        assertThrowInterruptedExceptionWhenPreInterrupted(interruptibleLockActions);
+        {
+            long s = lock.writeLock();
+            assertThrowInterruptedExceptionWhenPreInterrupted(interruptibleLockActions);
+            lock.unlockWrite(s);
+        }
+        {
+            long s = lock.readLock();
+            assertThrowInterruptedExceptionWhenPreInterrupted(interruptibleLockActions);
+            lock.unlockRead(s);
+        }
+    }
+
+    void assertThrowInterruptedExceptionWhenInterrupted(Action[] actions) {
+        int n = actions.length;
+        Future<?>[] futures = new Future<?>[n];
+        CountDownLatch threadsStarted = new CountDownLatch(n);
+        CountDownLatch done = new CountDownLatch(n);
+
+        for (int i = 0; i < n; i++) {
+            Action action = actions[i];
+            futures[i] = cachedThreadPool.submit(new CheckedRunnable() {
+                public void realRun() throws Throwable {
+                    threadsStarted.countDown();
+                    try {
+                        action.run();
+                        shouldThrow();
+                    }
+                    catch (InterruptedException success) {}
+                    catch (Throwable fail) { threadUnexpectedException(fail); }
+                    assertFalse(Thread.interrupted());
+                    done.countDown();
+                }});
+        }
+
+        await(threadsStarted);
+        assertEquals(n, done.getCount());
+        for (Future<?> future : futures) // Interrupt all the tasks
+            future.cancel(true);
+        await(done);
+    }
+
+    /**
+     * interruptible operations throw InterruptedException when write locked and interrupted
+     */
+    public void testInterruptibleOperationsThrowInterruptedExceptionWriteLockedInterrupted() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+
+        Action[] interruptibleLockBlockingActions = {
+            () -> lock.writeLockInterruptibly(),
+            () -> lock.tryWriteLock(Long.MAX_VALUE, DAYS),
+            () -> lock.readLockInterruptibly(),
+            () -> lock.tryReadLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asWriteLock().lockInterruptibly(),
+            () -> lock.asWriteLock().tryLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asReadLock().lockInterruptibly(),
+            () -> lock.asReadLock().tryLock(Long.MAX_VALUE, DAYS),
+        };
+        shuffle(interruptibleLockBlockingActions);
+
+        assertThrowInterruptedExceptionWhenInterrupted(interruptibleLockBlockingActions);
+    }
+
+    /**
+     * interruptible operations throw InterruptedException when read locked and interrupted
+     */
+    public void testInterruptibleOperationsThrowInterruptedExceptionReadLockedInterrupted() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+
+        Action[] interruptibleLockBlockingActions = {
+            () -> lock.writeLockInterruptibly(),
+            () -> lock.tryWriteLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asWriteLock().lockInterruptibly(),
+            () -> lock.asWriteLock().tryLock(Long.MAX_VALUE, DAYS),
+        };
+        shuffle(interruptibleLockBlockingActions);
+
+        assertThrowInterruptedExceptionWhenInterrupted(interruptibleLockBlockingActions);
+    }
+
+    /**
+     * Non-interruptible operations ignore and preserve interrupt status
+     */
+    public void testNonInterruptibleOperationsIgnoreInterrupts() {
+        final StampedLock lock = new StampedLock();
+        Thread.currentThread().interrupt();
+
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = assertValid(lock, lock.readLock());
+            readUnlocker.accept(lock, s);
+            s = assertValid(lock, lock.tryReadLock());
+            readUnlocker.accept(lock, s);
+        }
+
+        lock.asReadLock().lock();
+        lock.asReadLock().unlock();
+
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            long s = assertValid(lock, lock.writeLock());
+            writeUnlocker.accept(lock, s);
+            s = assertValid(lock, lock.tryWriteLock());
+            writeUnlocker.accept(lock, s);
+        }
+
+        lock.asWriteLock().lock();
+        lock.asWriteLock().unlock();
+
+        assertTrue(Thread.interrupted());
+    }
+
+    /**
+     * tryWriteLock on an unlocked lock succeeds
+     */
+    public void testTryWriteLock() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.tryWriteLock();
+        assertTrue(s != 0L);
+        assertTrue(lock.isWriteLocked());
+        assertEquals(0L, lock.tryWriteLock());
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * tryWriteLock fails if locked
+     */
+    public void testTryWriteLockWhenLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0L, lock.tryWriteLock());
+            }});
+
+        assertEquals(0L, lock.tryWriteLock());
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * tryReadLock fails if write-locked
+     */
+    public void testTryReadLockWhenLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                assertEquals(0L, lock.tryReadLock());
+            }});
+
+        assertEquals(0L, lock.tryReadLock());
+        awaitTermination(t);
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * Multiple threads can hold a read lock when not write-locked
+     */
+    public void testMultipleReadLocks() {
+        final StampedLock lock = new StampedLock();
+        final long s = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long s2 = lock.tryReadLock();
+                assertValid(lock, s2);
+                lock.unlockRead(s2);
+                long s3 = lock.tryReadLock(LONG_DELAY_MS, MILLISECONDS);
+                assertValid(lock, s3);
+                lock.unlockRead(s3);
+                long s4 = lock.readLock();
+                assertValid(lock, s4);
+                lock.unlockRead(s4);
+                lock.asReadLock().lock();
+                lock.asReadLock().unlock();
+                lock.asReadLock().lockInterruptibly();
+                lock.asReadLock().unlock();
+                lock.asReadLock().tryLock(Long.MIN_VALUE, DAYS);
+                lock.asReadLock().unlock();
+            }});
+
+        awaitTermination(t);
+        lock.unlockRead(s);
+    }
+
+    /**
+     * writeLock() succeeds only after a reading thread unlocks
+     */
+    public void testWriteAfterReadLock() throws InterruptedException {
+        final CountDownLatch aboutToLock = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        long rs = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                aboutToLock.countDown();
+                long s = lock.writeLock();
+                assertTrue(lock.isWriteLocked());
+                assertFalse(lock.isReadLocked());
+                lock.unlockWrite(s);
+            }});
+
+        aboutToLock.await();
+        waitForThreadToEnterWaitState(t);
+        assertFalse(lock.isWriteLocked());
+        assertTrue(lock.isReadLocked());
+        lock.unlockRead(rs);
+        awaitTermination(t);
+        assertUnlocked(lock);
+    }
+
+    /**
+     * writeLock() succeeds only after reading threads unlock
+     */
+    public void testWriteAfterMultipleReadLocks() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long rs = lock.readLock();
+                lock.unlockRead(rs);
+            }});
+
+        awaitTermination(t1);
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long ws = lock.writeLock();
+                lock.unlockWrite(ws);
+            }});
+
+        assertTrue(lock.isReadLocked());
+        assertFalse(lock.isWriteLocked());
+        lock.unlockRead(s);
+        awaitTermination(t2);
+        assertUnlocked(lock);
+    }
+
+    /**
+     * readLock() succeed only after a writing thread unlocks
+     */
+    public void testReadAfterWriteLock() {
+        final StampedLock lock = new StampedLock();
+        final CountDownLatch threadsStarted = new CountDownLatch(2);
+        final long s = lock.writeLock();
+        final Runnable acquireReleaseReadLock = new CheckedRunnable() {
+            public void realRun() {
+                threadsStarted.countDown();
+                long rs = lock.readLock();
+                assertTrue(lock.isReadLocked());
+                assertFalse(lock.isWriteLocked());
+                lock.unlockRead(rs);
+            }};
+        Thread t1 = newStartedThread(acquireReleaseReadLock);
+        Thread t2 = newStartedThread(acquireReleaseReadLock);
+
+        await(threadsStarted);
+        waitForThreadToEnterWaitState(t1);
+        waitForThreadToEnterWaitState(t2);
+        assertTrue(lock.isWriteLocked());
+        assertFalse(lock.isReadLocked());
+        releaseWriteLock(lock, s);
+        awaitTermination(t1);
+        awaitTermination(t2);
+        assertUnlocked(lock);
+    }
+
+    /**
+     * tryReadLock succeeds if read locked but not write locked
+     */
+    public void testTryLockWhenReadLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                long rs = lock.tryReadLock();
+                assertValid(lock, rs);
+                lock.unlockRead(rs);
+            }});
+
+        awaitTermination(t);
+        lock.unlockRead(s);
+    }
+
+    /**
+     * tryWriteLock fails when read locked
+     */
+    public void testTryWriteLockWhenReadLocked() {
+        final StampedLock lock = new StampedLock();
+        long s = lock.readLock();
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() {
+                threadAssertEquals(0L, lock.tryWriteLock());
+            }});
+
+        awaitTermination(t);
+        lock.unlockRead(s);
+    }
+
+    /**
+     * timed lock operations time out if lock not available
+     */
+    public void testTimedLock_Timeout() throws Exception {
+        ArrayList<Future<?>> futures = new ArrayList<>();
+
+        // Write locked
+        final StampedLock lock = new StampedLock();
+        long stamp = lock.writeLock();
+        assertEquals(0L, lock.tryReadLock(0L, DAYS));
+        assertEquals(0L, lock.tryReadLock(Long.MIN_VALUE, DAYS));
+        assertFalse(lock.asReadLock().tryLock(0L, DAYS));
+        assertFalse(lock.asReadLock().tryLock(Long.MIN_VALUE, DAYS));
+        assertEquals(0L, lock.tryWriteLock(0L, DAYS));
+        assertEquals(0L, lock.tryWriteLock(Long.MIN_VALUE, DAYS));
+        assertFalse(lock.asWriteLock().tryLock(0L, DAYS));
+        assertFalse(lock.asWriteLock().tryLock(Long.MIN_VALUE, DAYS));
+
+        futures.add(cachedThreadPool.submit(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertEquals(0L, lock.tryWriteLock(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }}));
+
+        futures.add(cachedThreadPool.submit(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertEquals(0L, lock.tryReadLock(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }}));
+
+        // Read locked
+        final StampedLock lock2 = new StampedLock();
+        long stamp2 = lock2.readLock();
+        assertEquals(0L, lock2.tryWriteLock(0L, DAYS));
+        assertEquals(0L, lock2.tryWriteLock(Long.MIN_VALUE, DAYS));
+        assertFalse(lock2.asWriteLock().tryLock(0L, DAYS));
+        assertFalse(lock2.asWriteLock().tryLock(Long.MIN_VALUE, DAYS));
+
+        futures.add(cachedThreadPool.submit(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertEquals(0L, lock2.tryWriteLock(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }}));
+
+        for (Future<?> future : futures)
+            assertNull(future.get());
+
+        releaseWriteLock(lock, stamp);
+        releaseReadLock(lock2, stamp2);
+    }
+
+    /**
+     * writeLockInterruptibly succeeds if unlocked
+     */
+    public void testWriteLockInterruptibly() throws InterruptedException {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLockInterruptibly();
+        assertTrue(lock.isWriteLocked());
+        releaseWriteLock(lock, s);
+    }
+
+    /**
+     * readLockInterruptibly succeeds if lock free
+     */
+    public void testReadLockInterruptibly() throws InterruptedException {
+        final StampedLock lock = new StampedLock();
+
+        long s = assertValid(lock, lock.readLockInterruptibly());
+        assertTrue(lock.isReadLocked());
+        lock.unlockRead(s);
+
+        lock.asReadLock().lockInterruptibly();
+        assertTrue(lock.isReadLocked());
+        lock.asReadLock().unlock();
+    }
+
+    /**
+     * A serialized lock deserializes as unlocked
+     */
+    public void testSerialization() {
+        StampedLock lock = new StampedLock();
+        lock.writeLock();
+        StampedLock clone = serialClone(lock);
+        assertTrue(lock.isWriteLocked());
+        assertFalse(clone.isWriteLocked());
+        long s = clone.writeLock();
+        assertTrue(clone.isWriteLocked());
+        clone.unlockWrite(s);
+        assertFalse(clone.isWriteLocked());
+    }
+
+    /**
+     * toString indicates current lock state
+     */
+    public void testToString() {
+        StampedLock lock = new StampedLock();
+        assertTrue(lock.toString().contains("Unlocked"));
+        long s = lock.writeLock();
+        assertTrue(lock.toString().contains("Write-locked"));
+        lock.unlockWrite(s);
+        s = lock.readLock();
+        assertTrue(lock.toString().contains("Read-locks"));
+    }
+
+    /**
+     * tryOptimisticRead succeeds and validates if unlocked, fails if
+     * exclusively locked
+     */
+    public void testValidateOptimistic() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+
+        assertValid(lock, lock.tryOptimisticRead());
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+            long s = assertValid(lock, writeLocker.apply(lock));
+            assertEquals(0L, lock.tryOptimisticRead());
+            releaseWriteLock(lock, s);
+        }
+
+        for (Function<StampedLock, Long> readLocker : readLockers()) {
+            long s = assertValid(lock, readLocker.apply(lock));
+            long p = assertValid(lock, lock.tryOptimisticRead());
+            releaseReadLock(lock, s);
+            assertTrue(lock.validate(p));
+        }
+
+        assertValid(lock, lock.tryOptimisticRead());
+    }
+
+    /**
+     * tryOptimisticRead stamp does not validate if a write lock intervenes
+     */
+    public void testValidateOptimisticWriteLocked() {
+        final StampedLock lock = new StampedLock();
+        final long p = assertValid(lock, lock.tryOptimisticRead());
+        final long s = assertValid(lock, lock.writeLock());
+        assertFalse(lock.validate(p));
+        assertEquals(0L, lock.tryOptimisticRead());
+        assertTrue(lock.validate(s));
+        lock.unlockWrite(s);
+    }
+
+    /**
+     * tryOptimisticRead stamp does not validate if a write lock
+     * intervenes in another thread
+     */
+    public void testValidateOptimisticWriteLocked2()
+            throws InterruptedException {
+        final CountDownLatch locked = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+        final long p = assertValid(lock, lock.tryOptimisticRead());
+
+        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                lock.writeLockInterruptibly();
+                locked.countDown();
+                lock.writeLockInterruptibly();
+            }});
+
+        locked.await();
+        assertFalse(lock.validate(p));
+        assertEquals(0L, lock.tryOptimisticRead());
+        waitForThreadToEnterWaitState(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertTrue(lock.isWriteLocked());
+    }
+
+    /**
+     * tryConvertToOptimisticRead succeeds and validates if successfully locked
+     */
+    public void testTryConvertToOptimisticRead() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s, p, q;
+        assertEquals(0L, lock.tryConvertToOptimisticRead(0L));
+
+        s = assertValid(lock, lock.tryOptimisticRead());
+        assertEquals(s, lock.tryConvertToOptimisticRead(s));
+        assertTrue(lock.validate(s));
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+            s = assertValid(lock, writeLocker.apply(lock));
+            p = assertValid(lock, lock.tryConvertToOptimisticRead(s));
+            assertFalse(lock.validate(s));
+            assertTrue(lock.validate(p));
+            assertUnlocked(lock);
+        }
+
+        for (Function<StampedLock, Long> readLocker : readLockers()) {
+            s = assertValid(lock, readLocker.apply(lock));
+            q = assertValid(lock, lock.tryOptimisticRead());
+            assertEquals(q, lock.tryConvertToOptimisticRead(q));
+            assertTrue(lock.validate(q));
+            assertTrue(lock.isReadLocked());
+            p = assertValid(lock, lock.tryConvertToOptimisticRead(s));
+            assertTrue(lock.validate(p));
+            assertTrue(lock.validate(s));
+            assertUnlocked(lock);
+            assertEquals(q, lock.tryConvertToOptimisticRead(q));
+            assertTrue(lock.validate(q));
+        }
+    }
+
+    /**
+     * tryConvertToReadLock succeeds for valid stamps
+     */
+    public void testTryConvertToReadLock() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s, p;
+
+        assertEquals(0L, lock.tryConvertToReadLock(0L));
+
+        s = assertValid(lock, lock.tryOptimisticRead());
+        p = assertValid(lock, lock.tryConvertToReadLock(s));
+        assertTrue(lock.isReadLocked());
+        assertEquals(1, lock.getReadLockCount());
+        assertTrue(lock.validate(s));
+        lock.unlockRead(p);
+
+        s = assertValid(lock, lock.tryOptimisticRead());
+        lock.readLock();
+        p = assertValid(lock, lock.tryConvertToReadLock(s));
+        assertTrue(lock.isReadLocked());
+        assertEquals(2, lock.getReadLockCount());
+        lock.unlockRead(p);
+        lock.unlockRead(p);
+        assertUnlocked(lock);
+
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+                s = assertValid(lock, writeLocker.apply(lock));
+                p = assertValid(lock, lock.tryConvertToReadLock(s));
+                assertFalse(lock.validate(s));
+                assertTrue(lock.isReadLocked());
+                assertEquals(1, lock.getReadLockCount());
+                readUnlocker.accept(lock, p);
+            }
+
+            for (Function<StampedLock, Long> readLocker : readLockers()) {
+                s = assertValid(lock, readLocker.apply(lock));
+                assertEquals(s, lock.tryConvertToReadLock(s));
+                assertTrue(lock.validate(s));
+                assertTrue(lock.isReadLocked());
+                assertEquals(1, lock.getReadLockCount());
+                readUnlocker.accept(lock, s);
+            }
+        }
+    }
+
+    /**
+     * tryConvertToWriteLock succeeds if lock available; fails if multiply read locked
+     */
+    public void testTryConvertToWriteLock() throws InterruptedException {
+        StampedLock lock = new StampedLock();
+        long s, p;
+
+        assertEquals(0L, lock.tryConvertToWriteLock(0L));
+
+        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.isWriteLocked());
+        lock.unlockWrite(p);
+
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+                s = assertValid(lock, writeLocker.apply(lock));
+                assertEquals(s, lock.tryConvertToWriteLock(s));
+                assertTrue(lock.validate(s));
+                assertTrue(lock.isWriteLocked());
+                writeUnlocker.accept(lock, s);
+            }
+
+            for (Function<StampedLock, Long> readLocker : readLockers()) {
+                s = assertValid(lock, readLocker.apply(lock));
+                p = assertValid(lock, lock.tryConvertToWriteLock(s));
+                assertFalse(lock.validate(s));
+                assertTrue(lock.validate(p));
+                assertTrue(lock.isWriteLocked());
+                writeUnlocker.accept(lock, p);
+            }
+        }
+
+        // failure if multiply read locked
+        for (Function<StampedLock, Long> readLocker : readLockers()) {
+            s = assertValid(lock, readLocker.apply(lock));
+            p = assertValid(lock, readLocker.apply(lock));
+            assertEquals(0L, lock.tryConvertToWriteLock(s));
+            assertTrue(lock.validate(s));
+            assertTrue(lock.validate(p));
+            assertEquals(2, lock.getReadLockCount());
+            lock.unlock(p);
+            lock.unlock(s);
+            assertUnlocked(lock);
+        }
+    }
+
+    /**
+     * asWriteLock can be locked and unlocked
+     */
+    public void testAsWriteLock() throws Throwable {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asWriteLock();
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isWriteLocked());
+            assertFalse(sl.isReadLocked());
+            assertFalse(lock.tryLock());
+            lock.unlock();
+            assertUnlocked(sl);
+        }
+    }
+
+    /**
+     * asReadLock can be locked and unlocked
+     */
+    public void testAsReadLock() throws Throwable {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asReadLock();
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertFalse(sl.isWriteLocked());
+            assertEquals(1, sl.getReadLockCount());
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertEquals(2, sl.getReadLockCount());
+            lock.unlock();
+            lock.unlock();
+            assertUnlocked(sl);
+        }
+    }
+
+    /**
+     * asReadWriteLock.writeLock can be locked and unlocked
+     */
+    public void testAsReadWriteLockWriteLock() throws Throwable {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asReadWriteLock().writeLock();
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isWriteLocked());
+            assertFalse(sl.isReadLocked());
+            assertFalse(lock.tryLock());
+            lock.unlock();
+            assertUnlocked(sl);
+        }
+    }
+
+    /**
+     * asReadWriteLock.readLock can be locked and unlocked
+     */
+    public void testAsReadWriteLockReadLock() throws Throwable {
+        StampedLock sl = new StampedLock();
+        Lock lock = sl.asReadWriteLock().readLock();
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertFalse(sl.isWriteLocked());
+            assertEquals(1, sl.getReadLockCount());
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertEquals(2, sl.getReadLockCount());
+            lock.unlock();
+            lock.unlock();
+            assertUnlocked(sl);
+        }
+    }
+
+    /**
+     * Lock.newCondition throws UnsupportedOperationException
+     */
+    public void testLockViewsDoNotSupportConditions() {
+        StampedLock sl = new StampedLock();
+        assertThrows(UnsupportedOperationException.class,
+                     () -> sl.asWriteLock().newCondition(),
+                     () -> sl.asReadLock().newCondition(),
+                     () -> sl.asReadWriteLock().writeLock().newCondition(),
+                     () -> sl.asReadWriteLock().readLock().newCondition());
+    }
+
+    /**
+     * Passing optimistic read stamps to unlock operations result in
+     * IllegalMonitorStateException
+     */
+    public void testCannotUnlockOptimisticReadStamps() {
+        Runnable[] actions = {
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = assertValid(sl, sl.tryOptimisticRead());
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryOptimisticRead();
+                sl.unlock(stamp);
+            },
+
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryOptimisticRead();
+                sl.writeLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                sl.readLock();
+                long stamp = assertValid(sl, sl.tryOptimisticRead());
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                sl.readLock();
+                long stamp = assertValid(sl, sl.tryOptimisticRead());
+                sl.unlock(stamp);
+            },
+
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                assertValid(sl, stamp);
+                sl.writeLock();
+                sl.unlockWrite(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                sl.writeLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                sl.readLock();
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                sl.readLock();
+                sl.unlock(stamp);
+            },
+
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                assertValid(sl, stamp);
+                sl.writeLock();
+                sl.unlockWrite(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.writeLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.readLock();
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                sl.readLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                assertValid(sl, stamp);
+                sl.readLock();
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.readLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                sl.readLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.readLock();
+                sl.unlock(stamp);
+            },
+        };
+
+        assertThrows(IllegalMonitorStateException.class, actions);
+    }
+
+    static long writeLockInterruptiblyUninterrupted(StampedLock sl) {
+        try { return sl.writeLockInterruptibly(); }
+        catch (InterruptedException ex) { throw new AssertionError(ex); }
+    }
+
+    static long tryWriteLockUninterrupted(StampedLock sl, long time, TimeUnit unit) {
+        try { return sl.tryWriteLock(time, unit); }
+        catch (InterruptedException ex) { throw new AssertionError(ex); }
+    }
+
+    static long readLockInterruptiblyUninterrupted(StampedLock sl) {
+        try { return sl.readLockInterruptibly(); }
+        catch (InterruptedException ex) { throw new AssertionError(ex); }
+    }
+
+    static long tryReadLockUninterrupted(StampedLock sl, long time, TimeUnit unit) {
+        try { return sl.tryReadLock(time, unit); }
+        catch (InterruptedException ex) { throw new AssertionError(ex); }
+    }
+
+    /**
+     * Invalid stamps result in IllegalMonitorStateException
+     */
+    public void testInvalidStampsThrowIllegalMonitorStateException() {
+        final StampedLock sl = new StampedLock();
+
+        assertThrows(IllegalMonitorStateException.class,
+                     () -> sl.unlockWrite(0L),
+                     () -> sl.unlockRead(0L),
+                     () -> sl.unlock(0L));
+
+        final long optimisticStamp = sl.tryOptimisticRead();
+        final long readStamp = sl.readLock();
+        sl.unlockRead(readStamp);
+        final long writeStamp = sl.writeLock();
+        sl.unlockWrite(writeStamp);
+        assertTrue(optimisticStamp != 0L && readStamp != 0L && writeStamp != 0L);
+        final long[] noLongerValidStamps = { optimisticStamp, readStamp, writeStamp };
+        final Runnable assertNoLongerValidStampsThrow = () -> {
+            for (long noLongerValidStamp : noLongerValidStamps)
+                assertThrows(IllegalMonitorStateException.class,
+                             () -> sl.unlockWrite(noLongerValidStamp),
+                             () -> sl.unlockRead(noLongerValidStamp),
+                             () -> sl.unlock(noLongerValidStamp));
+        };
+        assertNoLongerValidStampsThrow.run();
+
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            final long stamp = readLocker.apply(sl);
+            assertValid(sl, stamp);
+            assertNoLongerValidStampsThrow.run();
+            assertThrows(IllegalMonitorStateException.class,
+                         () -> sl.unlockWrite(stamp),
+                         () -> sl.unlockRead(sl.tryOptimisticRead()),
+                         () -> sl.unlockRead(0L));
+            readUnlocker.accept(sl, stamp);
+            assertUnlocked(sl);
+            assertNoLongerValidStampsThrow.run();
+        }
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            final long stamp = writeLocker.apply(sl);
+            assertValid(sl, stamp);
+            assertNoLongerValidStampsThrow.run();
+            assertThrows(IllegalMonitorStateException.class,
+                         () -> sl.unlockRead(stamp),
+                         () -> sl.unlockWrite(0L));
+            writeUnlocker.accept(sl, stamp);
+            assertUnlocked(sl);
+            assertNoLongerValidStampsThrow.run();
+        }
+    }
+
+    /**
+     * Read locks can be very deeply nested
+     */
+    public void testDeeplyNestedReadLocks() {
+        final StampedLock lock = new StampedLock();
+        final int depth = 300;
+        final long[] stamps = new long[depth];
+        final List<Function<StampedLock, Long>> readLockers = readLockers();
+        final List<BiConsumer<StampedLock, Long>> readUnlockers = readUnlockers();
+        for (int i = 0; i < depth; i++) {
+            Function<StampedLock, Long> readLocker
+                = readLockers.get(i % readLockers.size());
+            long stamp = readLocker.apply(lock);
+            assertEquals(i + 1, lock.getReadLockCount());
+            assertTrue(lock.isReadLocked());
+            stamps[i] = stamp;
+        }
+        for (int i = 0; i < depth; i++) {
+            BiConsumer<StampedLock, Long> readUnlocker
+                = readUnlockers.get(i % readUnlockers.size());
+            assertEquals(depth - i, lock.getReadLockCount());
+            assertTrue(lock.isReadLocked());
+            readUnlocker.accept(lock, stamps[depth - 1 - i]);
+        }
+        assertUnlocked(lock);
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/SynchronousQueueTest.java b/ojluni/src/test/java/util/concurrent/tck/SynchronousQueueTest.java
new file mode 100644
index 0000000..5ff8ea2
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/SynchronousQueueTest.java
@@ -0,0 +1,643 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+
+import junit.framework.Test;
+
+public class SynchronousQueueTest extends JSR166TestCase {
+
+    public static class Fair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new SynchronousQueue(true);
+        }
+    }
+
+    public static class NonFair extends BlockingQueueTest {
+        protected BlockingQueue emptyCollection() {
+            return new SynchronousQueue(false);
+        }
+    }
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return newTestSuite(SynchronousQueueTest.class,
+                            new Fair().testSuite(),
+                            new NonFair().testSuite());
+    }
+
+    /**
+     * Any SynchronousQueue is both empty and full
+     */
+    public void testEmptyFull()      { testEmptyFull(false); }
+    public void testEmptyFull_fair() { testEmptyFull(true); }
+    public void testEmptyFull(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertEquals(0, q.remainingCapacity());
+        assertFalse(q.offer(zero));
+    }
+
+    /**
+     * offer fails if no active taker
+     */
+    public void testOffer()      { testOffer(false); }
+    public void testOffer_fair() { testOffer(true); }
+    public void testOffer(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        assertFalse(q.offer(one));
+    }
+
+    /**
+     * add throws IllegalStateException if no active taker
+     */
+    public void testAdd()      { testAdd(false); }
+    public void testAdd_fair() { testAdd(true); }
+    public void testAdd(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        assertEquals(0, q.remainingCapacity());
+        try {
+            q.add(one);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * addAll(this) throws IllegalArgumentException
+     */
+    public void testAddAll_self()      { testAddAll_self(false); }
+    public void testAddAll_self_fair() { testAddAll_self(true); }
+    public void testAddAll_self(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.addAll(q);
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * addAll throws ISE if no active taker
+     */
+    public void testAddAll_ISE()      { testAddAll_ISE(false); }
+    public void testAddAll_ISE_fair() { testAddAll_ISE(true); }
+    public void testAddAll_ISE(boolean fair) {
+        SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] ints = new Integer[1];
+        for (int i = 0; i < ints.length; i++)
+            ints[i] = i;
+        Collection<Integer> coll = Arrays.asList(ints);
+        try {
+            q.addAll(coll);
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * put blocks interruptibly if no active taker
+     */
+    public void testBlockingPut()      { testBlockingPut(false); }
+    public void testBlockingPut_fair() { testBlockingPut(true); }
+    public void testBlockingPut(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.currentThread().interrupt();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * put blocks interruptibly waiting for take
+     */
+    public void testPutWithTake()      { testPutWithTake(false); }
+    public void testPutWithTake_fair() { testPutWithTake(true); }
+    public void testPutWithTake(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseTake = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                pleaseTake.countDown();
+                q.put(one);
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.put(99);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseTake);
+        assertEquals(0, q.remainingCapacity());
+        try { assertSame(one, q.take()); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        assertEquals(0, q.remainingCapacity());
+    }
+
+    /**
+     * timed offer times out if elements not taken
+     */
+    public void testTimedOffer()      { testTimedOffer(false); }
+    public void testTimedOffer_fair() { testTimedOffer(true); }
+    public void testTimedOffer(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertFalse(q.offer(new Object(), timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+                pleaseInterrupt.countDown();
+                try {
+                    q.offer(new Object(), 2 * LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * poll return null if no active putter
+     */
+    public void testPoll()      { testPoll(false); }
+    public void testPoll_fair() { testPoll(true); }
+    public void testPoll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertNull(q.poll());
+    }
+
+    /**
+     * timed poll with zero timeout times out if no active putter
+     */
+    public void testTimedPoll0()      { testTimedPoll0(false); }
+    public void testTimedPoll0_fair() { testTimedPoll0(true); }
+    public void testTimedPoll0(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try { assertNull(q.poll(0, MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+    }
+
+    /**
+     * timed poll with nonzero timeout times out if no active putter
+     */
+    public void testTimedPoll()      { testTimedPoll(false); }
+    public void testTimedPoll_fair() { testTimedPoll(true); }
+    public void testTimedPoll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        long startTime = System.nanoTime();
+        try { assertNull(q.poll(timeoutMillis(), MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+    }
+
+    /**
+     * timed poll before a delayed offer times out, returning null;
+     * after offer succeeds; on interruption throws
+     */
+    public void testTimedPollWithOffer()      { testTimedPollWithOffer(false); }
+    public void testTimedPollWithOffer_fair() { testTimedPollWithOffer(true); }
+    public void testTimedPollWithOffer(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CountDownLatch pleaseOffer = new CountDownLatch(1);
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertNull(q.poll(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+
+                pleaseOffer.countDown();
+                startTime = System.nanoTime();
+                assertSame(zero, q.poll(LONG_DELAY_MS, MILLISECONDS));
+
+                Thread.currentThread().interrupt();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    q.poll(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            }});
+
+        await(pleaseOffer);
+        long startTime = System.nanoTime();
+        try { assertTrue(q.offer(zero, LONG_DELAY_MS, MILLISECONDS)); }
+        catch (InterruptedException e) { threadUnexpectedException(e); }
+        assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * peek() returns null if no active putter
+     */
+    public void testPeek()      { testPeek(false); }
+    public void testPeek_fair() { testPeek(true); }
+    public void testPeek(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertNull(q.peek());
+    }
+
+    /**
+     * element() throws NoSuchElementException if no active putter
+     */
+    public void testElement()      { testElement(false); }
+    public void testElement_fair() { testElement(true); }
+    public void testElement(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.element();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * remove() throws NoSuchElementException if no active putter
+     */
+    public void testRemove()      { testRemove(false); }
+    public void testRemove_fair() { testRemove(true); }
+    public void testRemove(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            q.remove();
+            shouldThrow();
+        } catch (NoSuchElementException success) {}
+    }
+
+    /**
+     * contains returns false
+     */
+    public void testContains()      { testContains(false); }
+    public void testContains_fair() { testContains(true); }
+    public void testContains(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        assertFalse(q.contains(zero));
+    }
+
+    /**
+     * clear ensures isEmpty
+     */
+    public void testClear()      { testClear(false); }
+    public void testClear_fair() { testClear(true); }
+    public void testClear(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll returns false unless empty
+     */
+    public void testContainsAll()      { testContainsAll(false); }
+    public void testContainsAll_fair() { testContainsAll(true); }
+    public void testContainsAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertTrue(q.containsAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.containsAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * retainAll returns false
+     */
+    public void testRetainAll()      { testRetainAll(false); }
+    public void testRetainAll_fair() { testRetainAll(true); }
+    public void testRetainAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertFalse(q.retainAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.retainAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * removeAll returns false
+     */
+    public void testRemoveAll()      { testRemoveAll(false); }
+    public void testRemoveAll_fair() { testRemoveAll(true); }
+    public void testRemoveAll(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Integer[] empty = new Integer[0];
+        assertFalse(q.removeAll(Arrays.asList(empty)));
+        Integer[] ints = new Integer[1]; ints[0] = zero;
+        assertFalse(q.containsAll(Arrays.asList(ints)));
+    }
+
+    /**
+     * toArray is empty
+     */
+    public void testToArray()      { testToArray(false); }
+    public void testToArray_fair() { testToArray(true); }
+    public void testToArray(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Object[] o = q.toArray();
+        assertEquals(0, o.length);
+    }
+
+    /**
+     * toArray(Integer array) returns its argument with the first
+     * element (if present) nulled out
+     */
+    public void testToArray2()      { testToArray2(false); }
+    public void testToArray2_fair() { testToArray2(true); }
+    public void testToArray2(boolean fair) {
+        final SynchronousQueue<Integer> q = new SynchronousQueue<>(fair);
+        Integer[] a;
+
+        a = new Integer[0];
+        assertSame(a, q.toArray(a));
+
+        a = new Integer[3];
+        Arrays.fill(a, 42);
+        assertSame(a, q.toArray(a));
+        assertNull(a[0]);
+        for (int i = 1; i < a.length; i++)
+            assertEquals(42, (int) a[i]);
+    }
+
+    /**
+     * toArray(null) throws NPE
+     */
+    public void testToArray_null()      { testToArray_null(false); }
+    public void testToArray_null_fair() { testToArray_null(true); }
+    public void testToArray_null(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        try {
+            Object[] o = q.toArray(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * iterator does not traverse any elements
+     */
+    public void testIterator()      { testIterator(false); }
+    public void testIterator_fair() { testIterator(true); }
+    public void testIterator(boolean fair) {
+        assertIteratorExhausted(new SynchronousQueue(fair).iterator());
+    }
+
+    /**
+     * iterator remove throws ISE
+     */
+    public void testIteratorRemove()      { testIteratorRemove(false); }
+    public void testIteratorRemove_fair() { testIteratorRemove(true); }
+    public void testIteratorRemove(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Iterator it = q.iterator();
+        try {
+            it.remove();
+            shouldThrow();
+        } catch (IllegalStateException success) {}
+    }
+
+    /**
+     * toString returns a non-null string
+     */
+    public void testToString()      { testToString(false); }
+    public void testToString_fair() { testToString(true); }
+    public void testToString(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        String s = q.toString();
+        assertNotNull(s);
+    }
+
+    /**
+     * offer transfers elements across Executor tasks
+     */
+    public void testOfferInExecutor()      { testOfferInExecutor(false); }
+    public void testOfferInExecutor_fair() { testOfferInExecutor(true); }
+    public void testOfferInExecutor(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(q.offer(one));
+                    threadsStarted.await();
+                    assertTrue(q.offer(one, LONG_DELAY_MS, MILLISECONDS));
+                    assertEquals(0, q.remainingCapacity());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    assertSame(one, q.take());
+                }});
+        }
+    }
+
+    /**
+     * timed poll retrieves elements across Executor threads
+     */
+    public void testPollInExecutor()      { testPollInExecutor(false); }
+    public void testPollInExecutor_fair() { testPollInExecutor(true); }
+    public void testPollInExecutor(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        final CheckedBarrier threadsStarted = new CheckedBarrier(2);
+        final ExecutorService executor = Executors.newFixedThreadPool(2);
+        try (PoolCleaner cleaner = cleaner(executor)) {
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertNull(q.poll());
+                    threadsStarted.await();
+                    assertSame(one, q.poll(LONG_DELAY_MS, MILLISECONDS));
+                    assertTrue(q.isEmpty());
+                }});
+
+            executor.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadsStarted.await();
+                    q.put(one);
+                }});
+        }
+    }
+
+    /**
+     * a deserialized serialized queue is usable
+     */
+    public void testSerialization() {
+        final SynchronousQueue x = new SynchronousQueue();
+        final SynchronousQueue y = new SynchronousQueue(false);
+        final SynchronousQueue z = new SynchronousQueue(true);
+        assertSerialEquals(x, y);
+        assertNotSerialEquals(x, z);
+        SynchronousQueue[] qs = { x, y, z };
+        for (SynchronousQueue q : qs) {
+            SynchronousQueue clone = serialClone(q);
+            assertNotSame(q, clone);
+            assertSerialEquals(q, clone);
+            assertTrue(clone.isEmpty());
+            assertEquals(0, clone.size());
+            assertEquals(0, clone.remainingCapacity());
+            assertFalse(clone.offer(zero));
+        }
+    }
+
+    /**
+     * drainTo(c) of empty queue doesn't transfer elements
+     */
+    public void testDrainTo()      { testDrainTo(false); }
+    public void testDrainTo_fair() { testDrainTo(true); }
+    public void testDrainTo(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        ArrayList l = new ArrayList();
+        q.drainTo(l);
+        assertEquals(0, q.size());
+        assertEquals(0, l.size());
+    }
+
+    /**
+     * drainTo empties queue, unblocking a waiting put.
+     */
+    public void testDrainToWithActivePut()      { testDrainToWithActivePut(false); }
+    public void testDrainToWithActivePut_fair() { testDrainToWithActivePut(true); }
+    public void testDrainToWithActivePut(boolean fair) {
+        final SynchronousQueue q = new SynchronousQueue(fair);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(one);
+            }});
+
+        ArrayList l = new ArrayList();
+        long startTime = System.nanoTime();
+        while (l.isEmpty()) {
+            q.drainTo(l);
+            if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                fail("timed out");
+            Thread.yield();
+        }
+        assertTrue(l.size() == 1);
+        assertSame(one, l.get(0));
+        awaitTermination(t);
+    }
+
+    /**
+     * drainTo(c, n) empties up to n elements of queue into c
+     */
+    public void testDrainToN() throws InterruptedException {
+        final SynchronousQueue q = new SynchronousQueue();
+        Thread t1 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(one);
+            }});
+
+        Thread t2 = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                q.put(two);
+            }});
+
+        ArrayList l = new ArrayList();
+        int drained;
+        while ((drained = q.drainTo(l, 1)) == 0) Thread.yield();
+        assertEquals(1, drained);
+        assertEquals(1, l.size());
+        while ((drained = q.drainTo(l, 1)) == 0) Thread.yield();
+        assertEquals(1, drained);
+        assertEquals(2, l.size());
+        assertTrue(l.contains(one));
+        assertTrue(l.contains(two));
+        awaitTermination(t1);
+        awaitTermination(t2);
+    }
+
+    /**
+     * remove(null), contains(null) always return false
+     */
+    public void testNeverContainsNull() {
+        Collection<?> q = new SynchronousQueue();
+        assertFalse(q.contains(null));
+        assertFalse(q.remove(null));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/SystemTest.java b/ojluni/src/test/java/util/concurrent/tck/SystemTest.java
new file mode 100644
index 0000000..fc2a05b
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/SystemTest.java
@@ -0,0 +1,97 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class SystemTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(SystemTest.class);
+    }
+
+    /**
+     * Worst case rounding for millisecs; set for 60 cycle millis clock.
+     * This value might need to be changed on JVMs with coarser
+     * System.currentTimeMillis clocks.
+     */
+    static final long MILLIS_ROUND = 17;
+
+    /**
+     * Nanos between readings of millis is no longer than millis (plus
+     * possible rounding).
+     * This shows only that nano timing not (much) worse than milli.
+     */
+    public void testNanoTime1() throws InterruptedException {
+        long m1 = System.currentTimeMillis();
+        Thread.sleep(1);
+        long n1 = System.nanoTime();
+        Thread.sleep(SHORT_DELAY_MS);
+        long n2 = System.nanoTime();
+        Thread.sleep(1);
+        long m2 = System.currentTimeMillis();
+        long millis = m2 - m1;
+        long nanos = n2 - n1;
+        assertTrue(nanos >= 0);
+        long nanosAsMillis = nanos / 1000000;
+        assertTrue(nanosAsMillis <= millis + MILLIS_ROUND);
+    }
+
+    /**
+     * Millis between readings of nanos is less than nanos, adjusting
+     * for rounding.
+     * This shows only that nano timing not (much) worse than milli.
+     */
+    public void testNanoTime2() throws InterruptedException {
+        long n1 = System.nanoTime();
+        Thread.sleep(1);
+        long m1 = System.currentTimeMillis();
+        Thread.sleep(SHORT_DELAY_MS);
+        long m2 = System.currentTimeMillis();
+        Thread.sleep(1);
+        long n2 = System.nanoTime();
+        long millis = m2 - m1;
+        long nanos = n2 - n1;
+
+        assertTrue(nanos >= 0);
+        long nanosAsMillis = nanos / 1000000;
+        assertTrue(millis <= nanosAsMillis + MILLIS_ROUND);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java b/ojluni/src/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java
new file mode 100644
index 0000000..37ecba1
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ThreadLocalRandom8Test.java
@@ -0,0 +1,279 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.LongAdder;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadLocalRandom8Test extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadLocalRandom8Test.class);
+    }
+
+    // max sampled int bound
+    static final int MAX_INT_BOUND = (1 << 26);
+
+    // max sampled long bound
+    static final long MAX_LONG_BOUND = (1L << 42);
+
+    // Number of replications for other checks
+    static final int REPS =
+        Integer.getInteger("ThreadLocalRandom8Test.reps", 4);
+
+    /**
+     * Invoking sized ints, long, doubles, with negative sizes throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamSize() {
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        Runnable[] throwingActions = {
+            () -> r.ints(-1L),
+            () -> r.ints(-1L, 2, 3),
+            () -> r.longs(-1L),
+            () -> r.longs(-1L, -1L, 1L),
+            () -> r.doubles(-1L),
+            () -> r.doubles(-1L, .5, .6),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * Invoking bounded ints, long, doubles, with illegal bounds throws
+     * IllegalArgumentException
+     */
+    public void testBadStreamBounds() {
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        Runnable[] throwingActions = {
+            () -> r.ints(2, 1),
+            () -> r.ints(10, 42, 42),
+            () -> r.longs(-1L, -1L),
+            () -> r.longs(10, 1L, -2L),
+            () -> r.doubles(0.0, 0.0),
+            () -> r.doubles(10, .5, .4),
+        };
+        assertThrows(IllegalArgumentException.class, throwingActions);
+    }
+
+    /**
+     * A parallel sized stream of ints generates the given number of values
+     */
+    public void testIntsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.ints(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of longs generates the given number of values
+     */
+    public void testLongsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.longs(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * A parallel sized stream of doubles generates the given number of values
+     */
+    public void testDoublesCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 0;
+        for (int reps = 0; reps < REPS; ++reps) {
+            counter.reset();
+            r.doubles(size).parallel().forEach(x -> counter.increment());
+            assertEquals(size, counter.sum());
+            size += 524959;
+        }
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded ints is within bounds
+     */
+    public void testBoundedInts() {
+        AtomicInteger fails = new AtomicInteger(0);
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 12345L;
+        for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) {
+                final int lo = least, hi = bound;
+                r.ints(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded longs is within bounds
+     */
+    public void testBoundedLongs() {
+        AtomicInteger fails = new AtomicInteger(0);
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 123L;
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                final long lo = least, hi = bound;
+                r.longs(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * Each of a parallel sized stream of bounded doubles is within bounds
+     */
+    public void testBoundedDoubles() {
+        AtomicInteger fails = new AtomicInteger(0);
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 456;
+        for (double least = 0.00011; least < 1.0e20; least *= 9) {
+            for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) {
+                final double lo = least, hi = bound;
+                r.doubles(size, lo, hi).parallel().forEach(
+                    x -> {
+                        if (x < lo || x >= hi)
+                            fails.getAndIncrement(); });
+            }
+        }
+        assertEquals(0, fails.get());
+    }
+
+    /**
+     * A parallel unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.ints().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.longs().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A parallel unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCount() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.doubles().limit(size).parallel().forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of ints generates at least 100 values
+     */
+    public void testUnsizedIntsCountSeq() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.ints().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of longs generates at least 100 values
+     */
+    public void testUnsizedLongsCountSeq() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.longs().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A sequential unsized stream of doubles generates at least 100 values
+     */
+    public void testUnsizedDoublesCountSeq() {
+        LongAdder counter = new LongAdder();
+        ThreadLocalRandom r = ThreadLocalRandom.current();
+        long size = 100;
+        r.doubles().limit(size).forEach(x -> counter.increment());
+        assertEquals(size, counter.sum());
+    }
+
+    /**
+     * A deserialized ThreadLocalRandom is always identical to
+     * ThreadLocalRandom.current()
+     */
+    public void testSerialization() {
+        assertSame(
+            ThreadLocalRandom.current(),
+            serialClone(ThreadLocalRandom.current()));
+        // In the current implementation, there is exactly one shared instance
+        if (testImplementationDetails)
+            assertSame(
+                ThreadLocalRandom.current(),
+                java.util.concurrent.CompletableFuture.supplyAsync(
+                    () -> serialClone(ThreadLocalRandom.current())).join());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ThreadLocalRandomTest.java b/ojluni/src/test/java/util/concurrent/tck/ThreadLocalRandomTest.java
new file mode 100644
index 0000000..9fa46b5
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ThreadLocalRandomTest.java
@@ -0,0 +1,413 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadLocalRandomTest extends JSR166TestCase {
+
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadLocalRandomTest.class);
+    }
+
+    /*
+     * Testing coverage notes:
+     *
+     * We don't test randomness properties, but only that repeated
+     * calls, up to NCALLS tries, produce at least one different
+     * result.  For bounded versions, we sample various intervals
+     * across multiples of primes.
+     */
+
+    // max numbers of calls to detect getting stuck on one value
+    static final int NCALLS = 10000;
+
+    // max sampled int bound
+    static final int MAX_INT_BOUND = (1 << 28);
+
+    // max sampled long bound
+    static final long MAX_LONG_BOUND = (1L << 42);
+
+    // Number of replications for other checks
+    static final int REPS = 20;
+
+    /**
+     * setSeed throws UnsupportedOperationException
+     */
+    public void testSetSeed() {
+        try {
+            ThreadLocalRandom.current().setSeed(17);
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+    }
+
+    /**
+     * Repeated calls to next (only accessible via reflection) produce
+     * at least two distinct results, and repeated calls produce all
+     * possible values.
+     */
+    public void testNext() throws ReflectiveOperationException {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        final java.lang.reflect.Method m;
+        try {
+            m = ThreadLocalRandom.class.getDeclaredMethod(
+                    "next", new Class[] { int.class });
+            m.setAccessible(true);
+        } catch (SecurityException acceptable) {
+            // Security manager may deny access
+            return;
+        } catch (Exception ex) {
+            // jdk9 module system may deny access
+            if (ex.getClass().getSimpleName()
+                .equals("InaccessibleObjectException"))
+                return;
+            throw ex;
+        }
+
+        int i;
+        {
+            int val = new java.util.Random().nextInt(4);
+            for (i = 0; i < NCALLS; i++) {
+                int q = (int) m.invoke(rnd, new Object[] { 2 });
+                if (val == q) break;
+            }
+            assertTrue(i < NCALLS);
+        }
+
+        {
+            int r = (int) m.invoke(rnd, new Object[] { 3 });
+            for (i = 0; i < NCALLS; i++) {
+                int q = (int) m.invoke(rnd, new Object[] { 3 });
+                assertTrue(q < (1<<3));
+                if (r != q) break;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * Repeated calls to nextInt produce at least two distinct results
+     */
+    public void testNextInt() {
+        int f = ThreadLocalRandom.current().nextInt();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextInt() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextLong produce at least two distinct results
+     */
+    public void testNextLong() {
+        long f = ThreadLocalRandom.current().nextLong();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextLong() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextBoolean produce at least two distinct results
+     */
+    public void testNextBoolean() {
+        boolean f = ThreadLocalRandom.current().nextBoolean();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextBoolean() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextFloat produce at least two distinct results
+     */
+    public void testNextFloat() {
+        float f = ThreadLocalRandom.current().nextFloat();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextFloat() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextDouble produce at least two distinct results
+     */
+    public void testNextDouble() {
+        double f = ThreadLocalRandom.current().nextDouble();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextDouble() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * Repeated calls to nextGaussian produce at least two distinct results
+     */
+    public void testNextGaussian() {
+        double f = ThreadLocalRandom.current().nextGaussian();
+        int i = 0;
+        while (i < NCALLS && ThreadLocalRandom.current().nextGaussian() == f)
+            ++i;
+        assertTrue(i < NCALLS);
+    }
+
+    /**
+     * nextInt(non-positive) throws IllegalArgumentException
+     */
+    public void testNextIntBoundNonPositive() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (int bound : new int[] { 0, -17, Integer.MIN_VALUE }) {
+            try {
+                rnd.nextInt(bound);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextInt(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextIntBadBounds() {
+        int[][] badBoundss = {
+            { 17, 2 },
+            { -42, -42 },
+            { Integer.MAX_VALUE, Integer.MIN_VALUE },
+        };
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (int[] badBounds : badBoundss) {
+            try {
+                rnd.nextInt(badBounds[0], badBounds[1]);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextInt(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded() {
+        // sample bound space across prime number increments
+        for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) {
+            int f = ThreadLocalRandom.current().nextInt(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            int j;
+            while (i < NCALLS &&
+                   (j = ThreadLocalRandom.current().nextInt(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextInt(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextIntBounded2() {
+        for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) {
+            for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) {
+                int f = ThreadLocalRandom.current().nextInt(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                int j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextInt(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextLong(non-positive) throws IllegalArgumentException
+     */
+    public void testNextLongBoundNonPositive() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (long bound : new long[] { 0L, -17L, Long.MIN_VALUE }) {
+            try {
+                rnd.nextLong(bound);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextLong(least >= bound) throws IllegalArgumentException
+     */
+    public void testNextLongBadBounds() {
+        long[][] badBoundss = {
+            { 17L, 2L },
+            { -42L, -42L },
+            { Long.MAX_VALUE, Long.MIN_VALUE },
+        };
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        for (long[] badBounds : badBoundss) {
+            try {
+                rnd.nextLong(badBounds[0], badBounds[1]);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextLong(bound) returns 0 <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded() {
+        for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) {
+            long f = ThreadLocalRandom.current().nextLong(bound);
+            assertTrue(0 <= f && f < bound);
+            int i = 0;
+            long j;
+            while (i < NCALLS &&
+                   (j = ThreadLocalRandom.current().nextLong(bound)) == f) {
+                assertTrue(0 <= j && j < bound);
+                ++i;
+            }
+            assertTrue(i < NCALLS);
+        }
+    }
+
+    /**
+     * nextLong(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextLongBounded2() {
+        for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) {
+            for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) {
+                long f = ThreadLocalRandom.current().nextLong(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                long j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextLong(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * nextDouble(non-positive) throws IllegalArgumentException
+     */
+    public void testNextDoubleBoundNonPositive() {
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+        double[] badBounds = {
+            0.0d,
+            -17.0d,
+            -Double.MIN_VALUE,
+            Double.NEGATIVE_INFINITY,
+            Double.NaN,
+        };
+        for (double bound : badBounds) {
+            try {
+                rnd.nextDouble(bound);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * nextDouble(least, bound) returns least <= value < bound;
+     * repeated calls produce at least two distinct results
+     */
+    public void testNextDoubleBounded2() {
+        for (double least = 0.0001; least < 1.0e20; least *= 8) {
+            for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) {
+                double f = ThreadLocalRandom.current().nextDouble(least, bound);
+                assertTrue(least <= f && f < bound);
+                int i = 0;
+                double j;
+                while (i < NCALLS &&
+                       (j = ThreadLocalRandom.current().nextDouble(least, bound)) == f) {
+                    assertTrue(least <= j && j < bound);
+                    ++i;
+                }
+                assertTrue(i < NCALLS);
+            }
+        }
+    }
+
+    /**
+     * Different threads produce different pseudo-random sequences
+     */
+    public void testDifferentSequences() {
+        // Don't use main thread's ThreadLocalRandom - it is likely to
+        // be polluted by previous tests.
+        final AtomicReference<ThreadLocalRandom> threadLocalRandom =
+            new AtomicReference<ThreadLocalRandom>();
+        final AtomicLong rand = new AtomicLong();
+
+        long firstRand = 0;
+        ThreadLocalRandom firstThreadLocalRandom = null;
+
+        Runnable getRandomState = new CheckedRunnable() {
+            public void realRun() {
+                ThreadLocalRandom current = ThreadLocalRandom.current();
+                assertSame(current, ThreadLocalRandom.current());
+                // test bug: the following is not guaranteed and not true in JDK8
+                //                assertNotSame(current, threadLocalRandom.get());
+                rand.set(current.nextLong());
+                threadLocalRandom.set(current);
+            }};
+
+        Thread first = newStartedThread(getRandomState);
+        awaitTermination(first);
+        firstRand = rand.get();
+        firstThreadLocalRandom = threadLocalRandom.get();
+
+        for (int i = 0; i < NCALLS; i++) {
+            Thread t = newStartedThread(getRandomState);
+            awaitTermination(t);
+            if (firstRand != rand.get())
+                return;
+        }
+        fail("all threads generate the same pseudo-random sequence");
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ThreadLocalTest.java b/ojluni/src/test/java/util/concurrent/tck/ThreadLocalTest.java
new file mode 100644
index 0000000..05588ba
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ThreadLocalTest.java
@@ -0,0 +1,129 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadLocalTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ThreadLocalTest.class);
+    }
+
+    static ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {
+            public Integer initialValue() {
+                return one;
+            }
+        };
+
+    static InheritableThreadLocal<Integer> itl =
+        new InheritableThreadLocal<Integer>() {
+            protected Integer initialValue() {
+                return zero;
+            }
+
+            protected Integer childValue(Integer parentValue) {
+                return new Integer(parentValue.intValue() + 1);
+            }
+        };
+
+    /**
+     * remove causes next access to return initial value
+     */
+    public void testRemove() {
+        assertSame(tl.get(), one);
+        tl.set(two);
+        assertSame(tl.get(), two);
+        tl.remove();
+        assertSame(tl.get(), one);
+    }
+
+    /**
+     * remove in InheritableThreadLocal causes next access to return
+     * initial value
+     */
+    public void testRemoveITL() {
+        assertSame(itl.get(), zero);
+        itl.set(two);
+        assertSame(itl.get(), two);
+        itl.remove();
+        assertSame(itl.get(), zero);
+    }
+
+    private class ITLThread extends Thread {
+        final int[] x;
+        ITLThread(int[] array) { x = array; }
+        public void run() {
+            Thread child = null;
+            if (itl.get().intValue() < x.length - 1) {
+                child = new ITLThread(x);
+                child.start();
+            }
+            Thread.yield();
+
+            int threadId = itl.get().intValue();
+            for (int j = 0; j < threadId; j++) {
+                x[threadId]++;
+                Thread.yield();
+            }
+
+            if (child != null) { // Wait for child (if any)
+                try {
+                    child.join();
+                } catch (InterruptedException e) {
+                    threadUnexpectedException(e);
+                }
+            }
+        }
+    }
+
+    /**
+     * InheritableThreadLocal propagates generic values.
+     */
+    public void testGenericITL() throws InterruptedException {
+        final int threadCount = 10;
+        final int[] x = new int[threadCount];
+        Thread progenitor = new ITLThread(x);
+        progenitor.start();
+        progenitor.join();
+        for (int i = 0; i < threadCount; i++) {
+            assertEquals(i, x[i]);
+        }
+    }
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java b/ojluni/src/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java
new file mode 100644
index 0000000..2e84a6e
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ThreadPoolExecutorSubclassTest.java
@@ -0,0 +1,2062 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadPoolExecutorSubclassTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadPoolExecutorSubclassTest.class);
+    }
+
+    static class CustomTask<V> implements RunnableFuture<V> {
+        final Callable<V> callable;
+        final ReentrantLock lock = new ReentrantLock();
+        final Condition cond = lock.newCondition();
+        boolean done;
+        boolean cancelled;
+        V result;
+        Thread thread;
+        Exception exception;
+        CustomTask(Callable<V> c) {
+            if (c == null) throw new NullPointerException();
+            callable = c;
+        }
+        CustomTask(final Runnable r, final V res) {
+            if (r == null) throw new NullPointerException();
+            callable = new Callable<V>() {
+                public V call() throws Exception { r.run(); return res; }};
+        }
+        public boolean isDone() {
+            lock.lock(); try { return done; } finally { lock.unlock() ; }
+        }
+        public boolean isCancelled() {
+            lock.lock(); try { return cancelled; } finally { lock.unlock() ; }
+        }
+        public boolean cancel(boolean mayInterrupt) {
+            lock.lock();
+            try {
+                if (!done) {
+                    cancelled = true;
+                    done = true;
+                    if (mayInterrupt && thread != null)
+                        thread.interrupt();
+                    return true;
+                }
+                return false;
+            }
+            finally { lock.unlock() ; }
+        }
+        public void run() {
+            lock.lock();
+            try {
+                if (done)
+                    return;
+                thread = Thread.currentThread();
+            }
+            finally { lock.unlock() ; }
+            V v = null;
+            Exception e = null;
+            try {
+                v = callable.call();
+            }
+            catch (Exception ex) {
+                e = ex;
+            }
+            lock.lock();
+            try {
+                if (!done) {
+                    result = v;
+                    exception = e;
+                    done = true;
+                    thread = null;
+                    cond.signalAll();
+                }
+            }
+            finally { lock.unlock(); }
+        }
+        public V get() throws InterruptedException, ExecutionException {
+            lock.lock();
+            try {
+                while (!done)
+                    cond.await();
+                if (cancelled)
+                    throw new CancellationException();
+                if (exception != null)
+                    throw new ExecutionException(exception);
+                return result;
+            }
+            finally { lock.unlock(); }
+        }
+        public V get(long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+            long nanos = unit.toNanos(timeout);
+            lock.lock();
+            try {
+                while (!done) {
+                    if (nanos <= 0L)
+                        throw new TimeoutException();
+                    nanos = cond.awaitNanos(nanos);
+                }
+                if (cancelled)
+                    throw new CancellationException();
+                if (exception != null)
+                    throw new ExecutionException(exception);
+                return result;
+            }
+            finally { lock.unlock(); }
+        }
+    }
+
+    static class CustomTPE extends ThreadPoolExecutor {
+        protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) {
+            return new CustomTask<V>(c);
+        }
+        protected <V> RunnableFuture<V> newTaskFor(Runnable r, V v) {
+            return new CustomTask<V>(r, v);
+        }
+
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue) {
+            super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+                  workQueue);
+        }
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  ThreadFactory threadFactory) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
+             threadFactory);
+        }
+
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  RejectedExecutionHandler handler) {
+        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
+              handler);
+        }
+        CustomTPE(int corePoolSize,
+                  int maximumPoolSize,
+                  long keepAliveTime,
+                  TimeUnit unit,
+                  BlockingQueue<Runnable> workQueue,
+                  ThreadFactory threadFactory,
+                  RejectedExecutionHandler handler) {
+            super(corePoolSize, maximumPoolSize, keepAliveTime, unit,
+              workQueue, threadFactory, handler);
+        }
+
+        final CountDownLatch beforeCalled = new CountDownLatch(1);
+        final CountDownLatch afterCalled = new CountDownLatch(1);
+        final CountDownLatch terminatedCalled = new CountDownLatch(1);
+
+        public CustomTPE() {
+            super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue<Runnable>());
+        }
+        protected void beforeExecute(Thread t, Runnable r) {
+            beforeCalled.countDown();
+        }
+        protected void afterExecute(Runnable r, Throwable t) {
+            afterCalled.countDown();
+        }
+        protected void terminated() {
+            terminatedCalled.countDown();
+        }
+
+        public boolean beforeCalled() {
+            return beforeCalled.getCount() == 0;
+        }
+        public boolean afterCalled() {
+            return afterCalled.getCount() == 0;
+        }
+        public boolean terminatedCalled() {
+            return terminatedCalled.getCount() == 0;
+        }
+    }
+
+    static class FailingThreadFactory implements ThreadFactory {
+        int calls = 0;
+        public Thread newThread(Runnable r) {
+            if (++calls > 1) return null;
+            return new Thread(r);
+        }
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          2 * LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * prestartCoreThread starts a thread if under corePoolSize, else doesn't
+     */
+    public void testPrestartCoreThread() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 6,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(1, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            assertTrue(p.prestartCoreThread());
+            assertEquals(3, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * prestartAllCoreThreads starts all corePoolSize threads
+     */
+    public void testPrestartAllCoreThreads() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 6,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getKeepAliveTime returns value given in constructor if not otherwise set
+     */
+    public void testGetKeepAliveTime() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          1000, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getKeepAliveTime(SECONDS));
+        }
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        final ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          threadFactory,
+                          new NoOpREHandler());
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ThreadFactory threadFactory = new SimpleThreadFactory();
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getRejectedExecutionHandler returns handler in constructor if not set
+     */
+    public void testGetRejectedExecutionHandler() {
+        final RejectedExecutionHandler handler = new NoOpREHandler();
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          handler);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler sets the handler returned by
+     * getRejectedExecutionHandler
+     */
+    public void testSetRejectedExecutionHandler() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            RejectedExecutionHandler handler = new NoOpREHandler();
+            p.setRejectedExecutionHandler(handler);
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler(null) throws NPE
+     */
+    public void testSetRejectedExecutionHandlerNull() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setRejectedExecutionHandler(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(THREADS, THREADS,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getLargestPoolSize());
+            final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getMaximumPoolSize returns value given in constructor if not
+     * otherwise set
+     */
+    public void testGetMaximumPoolSize() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(3, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(5);
+            assertEquals(5, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(4);
+            assertEquals(4, p.getMaximumPoolSize());
+        }
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getPoolSize());
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isShutdown());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable<Boolean> task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertSame(q, p.getQueue());
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertSame(q, p.getQueue());
+            assertFalse(q.contains(tasks[0]));
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertEquals(tasks.length - 1, q.size());
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable[] tasks = new Runnable[6];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                tasks[i] = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertFalse(p.remove(tasks[0]));
+            assertTrue(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[4]));
+            assertFalse(p.remove(tasks[4]));
+            assertFalse(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[3]));
+            assertFalse(q.contains(tasks[3]));
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(10);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable<Boolean> task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertEquals(tasks.length, p.getTaskCount());
+            assertEquals(tasks.length - 1, q.size());
+            assertEquals(1L, p.getActiveCount());
+            assertEquals(0L, p.getCompletedTaskCount());
+            tasks[4].cancel(true);
+            tasks[3].cancel(false);
+            p.purge();
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+            p.purge();         // Nothing to do
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final ThreadPoolExecutor p =
+            new CustomTPE(poolSize, poolSize,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    // Exception Tests
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor2() {
+        try {
+            new CustomTPE(1, -1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor3() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor4() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor5() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS, null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor6() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor7() {
+        try {
+            new CustomTPE(1,-1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor8() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor9() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor10() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException2() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS, null, new SimpleThreadFactory());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if threadFactory is set to null
+     */
+    public void testConstructorNullPointerException3() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          (ThreadFactory) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor11() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor12() {
+        try {
+            new CustomTPE(1, -1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor13() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor14() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor15() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException4() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          null,
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is set to null
+     */
+    public void testConstructorNullPointerException5() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor16() {
+        try {
+            new CustomTPE(-1, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor17() {
+        try {
+            new CustomTPE(1, -1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor18() {
+        try {
+            new CustomTPE(1, 0, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor19() {
+        try {
+            new CustomTPE(1, 2, -1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor20() {
+        try {
+            new CustomTPE(2, 1, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is null
+     */
+    public void testConstructorNullPointerException6() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          null,
+                          new SimpleThreadFactory(),
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is null
+     */
+    public void testConstructorNullPointerException7() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          new SimpleThreadFactory(),
+                          (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if ThreadFactory is null
+     */
+    public void testConstructorNullPointerException8() {
+        try {
+            new CustomTPE(1, 2, 1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10),
+                          (ThreadFactory) null,
+                          new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * execute throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedExecute() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.execute(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * executor using CallerRunsPolicy runs task if saturated.
+     */
+    public void testSaturatedExecute2() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.CallerRunsPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable blocker = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            p.execute(blocker);
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = new TrackedNoOpRunnable();
+            for (int i = 0; i < tasks.length; i++)
+                p.execute(tasks[i]);
+            for (int i = 1; i < tasks.length; i++)
+                assertTrue(tasks[i].done);
+            assertFalse(tasks[0].done); // waiting in queue
+        }
+    }
+
+    /**
+     * executor using DiscardPolicy drops task if saturated.
+     */
+    public void testSaturatedExecute3() {
+        final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+        for (int i = 0; i < tasks.length; ++i)
+            tasks[i] = new TrackedNoOpRunnable();
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            p.execute(awaiter(done));
+
+            for (TrackedNoOpRunnable task : tasks)
+                p.execute(task);
+            for (int i = 1; i < tasks.length; i++)
+                assertFalse(tasks[i].done);
+        }
+        for (int i = 1; i < tasks.length; i++)
+            assertFalse(tasks[i].done);
+        assertTrue(tasks[0].done); // was waiting in queue
+    }
+
+    /**
+     * executor using DiscardOldestPolicy drops oldest task if saturated.
+     */
+    public void testSaturatedExecute4() {
+        final CountDownLatch done = new CountDownLatch(1);
+        LatchAwaiter r1 = awaiter(done);
+        LatchAwaiter r2 = awaiter(done);
+        LatchAwaiter r3 = awaiter(done);
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardOldestPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(LatchAwaiter.NEW, r1.state);
+            assertEquals(LatchAwaiter.NEW, r2.state);
+            assertEquals(LatchAwaiter.NEW, r3.state);
+            p.execute(r1);
+            p.execute(r2);
+            assertTrue(p.getQueue().contains(r2));
+            p.execute(r3);
+            assertFalse(p.getQueue().contains(r2));
+            assertTrue(p.getQueue().contains(r3));
+        }
+        assertEquals(LatchAwaiter.DONE, r1.state);
+        assertEquals(LatchAwaiter.NEW, r2.state);
+        assertEquals(LatchAwaiter.DONE, r3.state);
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testRejectedExecutionExceptionOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(new NoOpRunnable());
+                shouldThrow();
+            } catch (RejectedExecutionException success) {}
+        }
+    }
+
+    /**
+     * execute using CallerRunsPolicy drops task on shutdown
+     */
+    public void testCallerRunsOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.CallerRunsPolicy());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardPolicy drops task on shutdown
+     */
+    public void testDiscardOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardPolicy());
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardOldestPolicy drops task on shutdown
+     */
+    public void testDiscardOldestOnShutdown() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new CustomTPE.DiscardOldestPolicy());
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          1L, SECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * setCorePoolSize of negative value throws IllegalArgumentException
+     */
+    public void testCorePoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(1, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setCorePoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize(int) throws IllegalArgumentException
+     * if given a value less the core pool size
+     */
+    public void testMaximumPoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize throws IllegalArgumentException
+     * if given a negative value
+     */
+    public void testMaximumPoolSizeIllegalArgumentException2() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS,
+                          MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setKeepAliveTime throws IllegalArgumentException
+     * when given a negative value
+     */
+    public void testKeepAliveTimeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 3,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setKeepAliveTime(-1, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * terminated() is called on termination
+     */
+    public void testTerminated() {
+        CustomTPE p = new CustomTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.terminatedCalled());
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * beforeExecute and afterExecute are called when executing task
+     */
+    public void testBeforeAfter() throws InterruptedException {
+        CustomTPE p = new CustomTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                }});
+            await(p.afterCalled);
+            assertEquals(0, done.getCount());
+            assertTrue(p.afterCalled());
+            assertTrue(p.beforeCalled());
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+                                                 MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new CustomTPE(2, 2,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p =
+                new CustomTPE(2, 2,
+                              LONG_DELAY_MS, MILLISECONDS,
+                              new ArrayBlockingQueue<Runnable>(10));
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+    /**
+     * Execution continues if there is at least one thread even if
+     * thread factory fails to create more
+     */
+    public void testFailingThreadFactory() throws InterruptedException {
+        final ExecutorService e =
+            new CustomTPE(100, 100,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new LinkedBlockingQueue<Runnable>(),
+                          new FailingThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            final int TASKS = 100;
+            final CountDownLatch done = new CountDownLatch(TASKS);
+            for (int k = 0; k < TASKS; ++k)
+                e.execute(new CheckedRunnable() {
+                    public void realRun() {
+                        done.countDown();
+                    }});
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * allowsCoreThreadTimeOut is by default false.
+     */
+    public void testAllowsCoreThreadTimeOut() {
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 2,
+                          1000, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.allowsCoreThreadTimeOut());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(true) causes idle threads to time out
+     */
+    public void testAllowCoreThreadTimeOut_true() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 10,
+                          keepAliveTime, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(true);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                }});
+            await(threadStarted);
+            delay(keepAliveTime);
+            long startTime = System.nanoTime();
+            while (p.getPoolSize() > 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            assertEquals(0, p.getPoolSize());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(false) causes idle threads not to time out
+     */
+    public void testAllowCoreThreadTimeOut_false() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new CustomTPE(2, 10,
+                          keepAliveTime, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(false);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertTrue(p.getPoolSize() >= 1);
+                }});
+            delay(2 * keepAliveTime);
+            assertTrue(p.getPoolSize() >= 1);
+        }
+    }
+
+    /**
+     * get(cancelled task) throws CancellationException
+     * (in part, a test of CustomTPE itself)
+     */
+    public void testGet_cancelled() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ExecutorService e =
+            new CustomTPE(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new LinkedBlockingQueue<Runnable>());
+        try (PoolCleaner cleaner = cleaner(e, done)) {
+            final CountDownLatch blockerStarted = new CountDownLatch(1);
+            final List<Future<?>> futures = new ArrayList<>();
+            for (int i = 0; i < 2; i++) {
+                Runnable r = new CheckedRunnable() { public void realRun()
+                                                         throws Throwable {
+                    blockerStarted.countDown();
+                    assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS));
+                }};
+                futures.add(e.submit(r));
+            }
+            await(blockerStarted);
+            for (Future<?> future : futures) future.cancel(false);
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                try {
+                    future.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                assertTrue(future.isCancelled());
+                assertTrue(future.isDone());
+            }
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java b/ojluni/src/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java
new file mode 100644
index 0000000..90f9eb3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ThreadPoolExecutorTest.java
@@ -0,0 +1,2093 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadPoolExecutorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(ThreadPoolExecutorTest.class);
+    }
+
+    static class ExtendedTPE extends ThreadPoolExecutor {
+        final CountDownLatch beforeCalled = new CountDownLatch(1);
+        final CountDownLatch afterCalled = new CountDownLatch(1);
+        final CountDownLatch terminatedCalled = new CountDownLatch(1);
+
+        public ExtendedTPE() {
+            super(1, 1, LONG_DELAY_MS, MILLISECONDS, new SynchronousQueue<Runnable>());
+        }
+        protected void beforeExecute(Thread t, Runnable r) {
+            beforeCalled.countDown();
+        }
+        protected void afterExecute(Runnable r, Throwable t) {
+            afterCalled.countDown();
+        }
+        protected void terminated() {
+            terminatedCalled.countDown();
+        }
+
+        public boolean beforeCalled() {
+            return beforeCalled.getCount() == 0;
+        }
+        public boolean afterCalled() {
+            return afterCalled.getCount() == 0;
+        }
+        public boolean terminatedCalled() {
+            return terminatedCalled.getCount() == 0;
+        }
+    }
+
+    static class FailingThreadFactory implements ThreadFactory {
+        int calls = 0;
+        public Thread newThread(Runnable r) {
+            if (++calls > 1) return null;
+            return new Thread(r);
+        }
+    }
+
+    /**
+     * execute successfully executes a runnable
+     */
+    public void testExecute() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Runnable task = new CheckedRunnable() {
+                public void realRun() { done.countDown(); }};
+            p.execute(task);
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * getActiveCount increases but doesn't overestimate, when a
+     * thread becomes active
+     */
+    public void testGetActiveCount() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getActiveCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getActiveCount());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getActiveCount());
+        }
+    }
+
+    /**
+     * prestartCoreThread starts a thread if under corePoolSize, else doesn't
+     */
+    public void testPrestartCoreThread() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 6,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(1, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            assertTrue(p.prestartCoreThread());
+            assertEquals(3, p.getPoolSize());
+            assertTrue(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+            assertFalse(p.prestartCoreThread());
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * prestartAllCoreThreads starts all corePoolSize threads
+     */
+    public void testPrestartAllCoreThreads() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 6,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(0, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(2, p.getPoolSize());
+            p.setCorePoolSize(4);
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+            p.prestartAllCoreThreads();
+            assertEquals(4, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getCompletedTaskCount increases, but doesn't overestimate,
+     * when tasks complete
+     */
+    public void testGetCompletedTaskCount() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch threadProceed = new CountDownLatch(1);
+            final CountDownLatch threadDone = new CountDownLatch(1);
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(0, p.getCompletedTaskCount());
+                    threadProceed.await();
+                    threadDone.countDown();
+                }});
+            await(threadStarted);
+            assertEquals(0, p.getCompletedTaskCount());
+            threadProceed.countDown();
+            threadDone.await();
+            long startTime = System.nanoTime();
+            while (p.getCompletedTaskCount() != 1) {
+                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
+                    fail("timed out");
+                Thread.yield();
+            }
+        }
+    }
+
+    /**
+     * getCorePoolSize returns size given in constructor if not otherwise set
+     */
+    public void testGetCorePoolSize() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getCorePoolSize());
+        }
+    }
+
+    /**
+     * getKeepAliveTime returns value given in constructor if not otherwise set
+     */
+    public void testGetKeepAliveTime() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   1000, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(1, p.getKeepAliveTime(SECONDS));
+        }
+    }
+
+    /**
+     * getThreadFactory returns factory in constructor if not set
+     */
+    public void testGetThreadFactory() {
+        ThreadFactory threadFactory = new SimpleThreadFactory();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   threadFactory,
+                                   new NoOpREHandler());
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory sets the thread factory returned by getThreadFactory
+     */
+    public void testSetThreadFactory() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            ThreadFactory threadFactory = new SimpleThreadFactory();
+            p.setThreadFactory(threadFactory);
+            assertSame(threadFactory, p.getThreadFactory());
+        }
+    }
+
+    /**
+     * setThreadFactory(null) throws NPE
+     */
+    public void testSetThreadFactoryNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setThreadFactory(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getRejectedExecutionHandler returns handler in constructor if not set
+     */
+    public void testGetRejectedExecutionHandler() {
+        final RejectedExecutionHandler handler = new NoOpREHandler();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   handler);
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler sets the handler returned by
+     * getRejectedExecutionHandler
+     */
+    public void testSetRejectedExecutionHandler() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            RejectedExecutionHandler handler = new NoOpREHandler();
+            p.setRejectedExecutionHandler(handler);
+            assertSame(handler, p.getRejectedExecutionHandler());
+        }
+    }
+
+    /**
+     * setRejectedExecutionHandler(null) throws NPE
+     */
+    public void testSetRejectedExecutionHandlerNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setRejectedExecutionHandler(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * getLargestPoolSize increases, but doesn't overestimate, when
+     * multiple threads active
+     */
+    public void testGetLargestPoolSize() throws InterruptedException {
+        final int THREADS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(THREADS, THREADS,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getLargestPoolSize());
+            final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
+            for (int i = 0; i < THREADS; i++)
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadsStarted.countDown();
+                        await(done);
+                        assertEquals(THREADS, p.getLargestPoolSize());
+                    }});
+            await(threadsStarted);
+            assertEquals(THREADS, p.getLargestPoolSize());
+        }
+        assertEquals(THREADS, p.getLargestPoolSize());
+    }
+
+    /**
+     * getMaximumPoolSize returns value given in constructor if not
+     * otherwise set
+     */
+    public void testGetMaximumPoolSize() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertEquals(3, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(5);
+            assertEquals(5, p.getMaximumPoolSize());
+            p.setMaximumPoolSize(4);
+            assertEquals(4, p.getMaximumPoolSize());
+        }
+    }
+
+    /**
+     * getPoolSize increases, but doesn't overestimate, when threads
+     * become active
+     */
+    public void testGetPoolSize() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(0, p.getPoolSize());
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getPoolSize());
+        }
+    }
+
+    /**
+     * getTaskCount increases, but doesn't overestimate, when tasks submitted
+     */
+    public void testGetTaskCount() throws InterruptedException {
+        final int TASKS = 3;
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            assertEquals(0, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertEquals(1, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+            for (int i = 0; i < TASKS; i++) {
+                assertEquals(1 + i, p.getTaskCount());
+                p.execute(new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertEquals(1 + TASKS, p.getTaskCount());
+                        await(done);
+                    }});
+            }
+            assertEquals(1 + TASKS, p.getTaskCount());
+            assertEquals(0, p.getCompletedTaskCount());
+        }
+        assertEquals(1 + TASKS, p.getTaskCount());
+        assertEquals(1 + TASKS, p.getCompletedTaskCount());
+    }
+
+    /**
+     * isShutdown is false before shutdown, true after
+     */
+    public void testIsShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isShutdown());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * awaitTermination on a non-shutdown pool times out
+     */
+    public void testAwaitTermination_timesOut() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.isTerminated());
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, NANOSECONDS));
+            assertFalse(p.awaitTermination(Long.MIN_VALUE, MILLISECONDS));
+            assertFalse(p.awaitTermination(-1L, NANOSECONDS));
+            assertFalse(p.awaitTermination(-1L, MILLISECONDS));
+            assertFalse(p.awaitTermination(0L, NANOSECONDS));
+            assertFalse(p.awaitTermination(0L, MILLISECONDS));
+            long timeoutNanos = 999999L;
+            long startTime = System.nanoTime();
+            assertFalse(p.awaitTermination(timeoutNanos, NANOSECONDS));
+            assertTrue(System.nanoTime() - startTime >= timeoutNanos);
+            assertFalse(p.isTerminated());
+            startTime = System.nanoTime();
+            long timeoutMillis = timeoutMillis();
+            assertFalse(p.awaitTermination(timeoutMillis, MILLISECONDS));
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
+            assertFalse(p.isTerminated());
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+        }
+    }
+
+    /**
+     * isTerminated is false before termination, true after
+     */
+    public void testIsTerminated() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * isTerminating is not true when running or when terminated
+     */
+    public void testIsTerminating() throws InterruptedException {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            final CountDownLatch done = new CountDownLatch(1);
+            assertFalse(p.isTerminating());
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    assertFalse(p.isTerminating());
+                    threadStarted.countDown();
+                    await(done);
+                }});
+            await(threadStarted);
+            assertFalse(p.isTerminating());
+            done.countDown();
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+            assertTrue(p.isTerminated());
+            assertFalse(p.isTerminating());
+        }
+    }
+
+    /**
+     * getQueue returns the work queue, which contains queued tasks
+     */
+    public void testGetQueue() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        assertSame(q, p.getQueue());
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertSame(q, p.getQueue());
+            assertFalse(q.contains(tasks[0]));
+            assertTrue(q.contains(tasks[tasks.length - 1]));
+            assertEquals(tasks.length - 1, q.size());
+        }
+    }
+
+    /**
+     * remove(task) removes queued task, and fails to remove active task
+     */
+    public void testRemove() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable[] tasks = new Runnable[6];
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            for (int i = 0; i < tasks.length; i++) {
+                tasks[i] = new CheckedRunnable() {
+                    public void realRun() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                    }};
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertFalse(p.remove(tasks[0]));
+            assertTrue(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[4]));
+            assertFalse(p.remove(tasks[4]));
+            assertFalse(q.contains(tasks[4]));
+            assertTrue(q.contains(tasks[3]));
+            assertTrue(p.remove(tasks[3]));
+            assertFalse(q.contains(tasks[3]));
+        }
+    }
+
+    /**
+     * purge removes cancelled tasks from the queue
+     */
+    public void testPurge() throws InterruptedException {
+        final CountDownLatch threadStarted = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(1);
+        final BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(10);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   q);
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            FutureTask[] tasks = new FutureTask[5];
+            for (int i = 0; i < tasks.length; i++) {
+                Callable task = new CheckedCallable<Boolean>() {
+                    public Boolean realCall() throws InterruptedException {
+                        threadStarted.countDown();
+                        await(done);
+                        return Boolean.TRUE;
+                    }};
+                tasks[i] = new FutureTask(task);
+                p.execute(tasks[i]);
+            }
+            await(threadStarted);
+            assertEquals(tasks.length, p.getTaskCount());
+            assertEquals(tasks.length - 1, q.size());
+            assertEquals(1L, p.getActiveCount());
+            assertEquals(0L, p.getCompletedTaskCount());
+            tasks[4].cancel(true);
+            tasks[3].cancel(false);
+            p.purge();
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+            p.purge();         // Nothing to do
+            assertEquals(tasks.length - 3, q.size());
+            assertEquals(tasks.length - 2, p.getTaskCount());
+        }
+    }
+
+    /**
+     * shutdownNow returns a list containing tasks that were not run,
+     * and those tasks are drained from the queue
+     */
+    public void testShutdownNow() throws InterruptedException {
+        final int poolSize = 2;
+        final int count = 5;
+        final AtomicInteger ran = new AtomicInteger(0);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(poolSize, poolSize,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        final CountDownLatch threadsStarted = new CountDownLatch(poolSize);
+        Runnable waiter = new CheckedRunnable() { public void realRun() {
+            threadsStarted.countDown();
+            try {
+                MILLISECONDS.sleep(2 * LONG_DELAY_MS);
+            } catch (InterruptedException success) {}
+            ran.getAndIncrement();
+        }};
+        for (int i = 0; i < count; i++)
+            p.execute(waiter);
+        await(threadsStarted);
+        assertEquals(poolSize, p.getActiveCount());
+        assertEquals(0, p.getCompletedTaskCount());
+        final List<Runnable> queuedTasks;
+        try {
+            queuedTasks = p.shutdownNow();
+        } catch (SecurityException ok) {
+            return; // Allowed in case test doesn't have privs
+        }
+        assertTrue(p.isShutdown());
+        assertTrue(p.getQueue().isEmpty());
+        assertEquals(count - poolSize, queuedTasks.size());
+        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
+        assertTrue(p.isTerminated());
+        assertEquals(poolSize, ran.get());
+        assertEquals(poolSize, p.getCompletedTaskCount());
+    }
+
+    // Exception Tests
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor1() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor2() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor3() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor4() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor5() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor6() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor7() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor8() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor9() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor10() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException2() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null,
+                                   new SimpleThreadFactory());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if threadFactory is set to null
+     */
+    public void testConstructorNullPointerException3() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (ThreadFactory) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor11() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor12() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor13() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor14() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor15() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is set to null
+     */
+    public void testConstructorNullPointerException4() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null,
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is set to null
+     */
+    public void testConstructorNullPointerException5() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize argument is less than zero
+     */
+    public void testConstructor16() {
+        try {
+            new ThreadPoolExecutor(-1, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is less than zero
+     */
+    public void testConstructor17() {
+        try {
+            new ThreadPoolExecutor(1, -1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if maximumPoolSize is equal to zero
+     */
+    public void testConstructor18() {
+        try {
+            new ThreadPoolExecutor(1, 0, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if keepAliveTime is less than zero
+     */
+    public void testConstructor19() {
+        try {
+            new ThreadPoolExecutor(1, 2, -1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if corePoolSize is greater than the maximumPoolSize
+     */
+    public void testConstructor20() {
+        try {
+            new ThreadPoolExecutor(2, 1, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (IllegalArgumentException success) {}
+    }
+
+    /**
+     * Constructor throws if workQueue is null
+     */
+    public void testConstructorNullPointerException6() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   (BlockingQueue) null,
+                                   new SimpleThreadFactory(),
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if handler is null
+     */
+    public void testConstructorNullPointerException7() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   new SimpleThreadFactory(),
+                                   (RejectedExecutionHandler) null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Constructor throws if ThreadFactory is null
+     */
+    public void testConstructorNullPointerException8() {
+        try {
+            new ThreadPoolExecutor(1, 2, 1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10),
+                                   (ThreadFactory) null,
+                                   new NoOpREHandler());
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * get of submitted callable throws InterruptedException if interrupted
+     */
+    public void testInterruptedSubmit() throws InterruptedException {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   60, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            Thread t = newStartedThread(new CheckedInterruptedRunnable() {
+                public void realRun() throws Exception {
+                    Callable task = new CheckedCallable<Boolean>() {
+                        public Boolean realCall() throws InterruptedException {
+                            threadStarted.countDown();
+                            await(done);
+                            return Boolean.TRUE;
+                        }};
+                    p.submit(task).get();
+                }});
+
+            await(threadStarted);
+            t.interrupt();
+            awaitTermination(t);
+        }
+    }
+
+    /**
+     * execute throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedExecute() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.execute(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * submit(runnable) throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedSubmitRunnable() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.submit(task);
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * submit(callable) throws RejectedExecutionException if saturated.
+     */
+    public void testSaturatedSubmitCallable() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            Runnable task = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            for (int i = 0; i < 2; ++i)
+                p.submit(Executors.callable(task));
+            for (int i = 0; i < 2; ++i) {
+                try {
+                    p.execute(task);
+                    shouldThrow();
+                } catch (RejectedExecutionException success) {}
+                assertTrue(p.getTaskCount() <= 2);
+            }
+        }
+    }
+
+    /**
+     * executor using CallerRunsPolicy runs task if saturated.
+     */
+    public void testSaturatedExecute2() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS,
+                                   MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.CallerRunsPolicy());
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            Runnable blocker = new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    await(done);
+                }};
+            p.execute(blocker);
+            TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+            for (int i = 0; i < tasks.length; i++)
+                tasks[i] = new TrackedNoOpRunnable();
+            for (int i = 0; i < tasks.length; i++)
+                p.execute(tasks[i]);
+            for (int i = 1; i < tasks.length; i++)
+                assertTrue(tasks[i].done);
+            assertFalse(tasks[0].done); // waiting in queue
+            done.countDown();
+        }
+    }
+
+    /**
+     * executor using DiscardPolicy drops task if saturated.
+     */
+    public void testSaturatedExecute3() {
+        final CountDownLatch done = new CountDownLatch(1);
+        final TrackedNoOpRunnable[] tasks = new TrackedNoOpRunnable[5];
+        for (int i = 0; i < tasks.length; ++i)
+            tasks[i] = new TrackedNoOpRunnable();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                          LONG_DELAY_MS, MILLISECONDS,
+                          new ArrayBlockingQueue<Runnable>(1),
+                          new ThreadPoolExecutor.DiscardPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            p.execute(awaiter(done));
+
+            for (TrackedNoOpRunnable task : tasks)
+                p.execute(task);
+            for (int i = 1; i < tasks.length; i++)
+                assertFalse(tasks[i].done);
+        }
+        for (int i = 1; i < tasks.length; i++)
+            assertFalse(tasks[i].done);
+        assertTrue(tasks[0].done); // was waiting in queue
+    }
+
+    /**
+     * executor using DiscardOldestPolicy drops oldest task if saturated.
+     */
+    public void testSaturatedExecute4() {
+        final CountDownLatch done = new CountDownLatch(1);
+        LatchAwaiter r1 = awaiter(done);
+        LatchAwaiter r2 = awaiter(done);
+        LatchAwaiter r3 = awaiter(done);
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.DiscardOldestPolicy());
+        try (PoolCleaner cleaner = cleaner(p, done)) {
+            assertEquals(LatchAwaiter.NEW, r1.state);
+            assertEquals(LatchAwaiter.NEW, r2.state);
+            assertEquals(LatchAwaiter.NEW, r3.state);
+            p.execute(r1);
+            p.execute(r2);
+            assertTrue(p.getQueue().contains(r2));
+            p.execute(r3);
+            assertFalse(p.getQueue().contains(r2));
+            assertTrue(p.getQueue().contains(r3));
+        }
+        assertEquals(LatchAwaiter.DONE, r1.state);
+        assertEquals(LatchAwaiter.NEW, r2.state);
+        assertEquals(LatchAwaiter.DONE, r3.state);
+    }
+
+    /**
+     * execute throws RejectedExecutionException if shutdown
+     */
+    public void testRejectedExecutionExceptionOnShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1));
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(new NoOpRunnable());
+                shouldThrow();
+            } catch (RejectedExecutionException success) {}
+        }
+    }
+
+    /**
+     * execute using CallerRunsPolicy drops task on shutdown
+     */
+    public void testCallerRunsOnShutdown() {
+        RejectedExecutionHandler h = new ThreadPoolExecutor.CallerRunsPolicy();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1), h);
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardPolicy drops task on shutdown
+     */
+    public void testDiscardOnShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.DiscardPolicy());
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute using DiscardOldestPolicy drops task on shutdown
+     */
+    public void testDiscardOldestOnShutdown() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(1),
+                                   new ThreadPoolExecutor.DiscardOldestPolicy());
+
+        try { p.shutdown(); } catch (SecurityException ok) { return; }
+        try (PoolCleaner cleaner = cleaner(p)) {
+            TrackedNoOpRunnable r = new TrackedNoOpRunnable();
+            p.execute(r);
+            assertFalse(r.done);
+        }
+    }
+
+    /**
+     * execute(null) throws NPE
+     */
+    public void testExecuteNull() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   1L, SECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.execute(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * setCorePoolSize of negative value throws IllegalArgumentException
+     */
+    public void testCorePoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setCorePoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize(int) throws IllegalArgumentException if
+     * given a value less the core pool size
+     */
+    public void testMaximumPoolSizeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * setMaximumPoolSize throws IllegalArgumentException
+     * if given a negative value
+     */
+    public void testMaximumPoolSizeIllegalArgumentException2() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setMaximumPoolSize(-1);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * Configuration changes that allow core pool size greater than
+     * max pool size result in IllegalArgumentException.
+     */
+    public void testPoolSizeInvariants() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int s = 1; s < 5; s++) {
+                p.setMaximumPoolSize(s);
+                p.setCorePoolSize(s);
+                try {
+                    p.setMaximumPoolSize(s - 1);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+                assertEquals(s, p.getCorePoolSize());
+                assertEquals(s, p.getMaximumPoolSize());
+                try {
+                    p.setCorePoolSize(s + 1);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+                assertEquals(s, p.getCorePoolSize());
+                assertEquals(s, p.getMaximumPoolSize());
+            }
+        }
+    }
+
+    /**
+     * setKeepAliveTime throws IllegalArgumentException
+     * when given a negative value
+     */
+    public void testKeepAliveTimeIllegalArgumentException() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 3,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try {
+                p.setKeepAliveTime(-1, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * terminated() is called on termination
+     */
+    public void testTerminated() {
+        ExtendedTPE p = new ExtendedTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            try { p.shutdown(); } catch (SecurityException ok) { return; }
+            assertTrue(p.terminatedCalled());
+            assertTrue(p.isShutdown());
+        }
+    }
+
+    /**
+     * beforeExecute and afterExecute are called when executing task
+     */
+    public void testBeforeAfter() throws InterruptedException {
+        ExtendedTPE p = new ExtendedTPE();
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch done = new CountDownLatch(1);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    done.countDown();
+                }});
+            await(p.afterCalled);
+            assertEquals(0, done.getCount());
+            assertTrue(p.afterCalled());
+            assertTrue(p.beforeCalled());
+        }
+    }
+
+    /**
+     * completed submit of callable returns result
+     */
+    public void testSubmitCallable() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new StringTask());
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * completed submit of runnable returns successfully
+     */
+    public void testSubmitRunnable() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<?> future = e.submit(new NoOpRunnable());
+            future.get();
+            assertTrue(future.isDone());
+        }
+    }
+
+    /**
+     * completed submit of (runnable, result) returns result
+     */
+    public void testSubmitRunnable2() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
+            String result = future.get();
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAny(null) throws NPE
+     */
+    public void testInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(empty collection) throws IAE
+     */
+    public void testInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>());
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * invokeAny(c) throws NPE if c has null elements
+     */
+    public void testInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAny(c) returns result of some task
+     */
+    public void testInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l);
+            assertSame(TEST_STRING, result);
+        }
+    }
+
+    /**
+     * invokeAll(null) throws NPE
+     */
+    public void testInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * invokeAll(empty collection) returns empty collection
+     */
+    public void testInvokeAll2() throws InterruptedException {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * invokeAll(c) throws NPE if c has null elements
+     */
+    public void testInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * invokeAll(c) returns results of all completed tasks
+     */
+    public void testInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures = e.invokeAll(l);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAny(null) throws NPE
+     */
+    public void testTimedInvokeAny1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(,,null) throws NPE
+     */
+    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(empty collection) throws IAE
+     */
+    public void testTimedInvokeAny2() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAny(new ArrayList<Callable<String>>(),
+                            MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (IllegalArgumentException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAny3() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(latchAwaitingStringTask(latch));
+            l.add(null);
+            try {
+                e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+            latch.countDown();
+        }
+    }
+
+    /**
+     * timed invokeAny(c) throws ExecutionException if no task completes
+     */
+    public void testTimedInvokeAny4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            try {
+                e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAny(c) returns result of some task
+     */
+    public void testTimedInvokeAny5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            long startTime = System.nanoTime();
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            String result = e.invokeAny(l, LONG_DELAY_MS, MILLISECONDS);
+            assertSame(TEST_STRING, result);
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+        }
+    }
+
+    /**
+     * timed invokeAll(null) throws NPE
+     */
+    public void testTimedInvokeAll1() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            try {
+                e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(,,null) throws NPE
+     */
+    public void testTimedInvokeAllNullTimeUnit() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, null);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * timed invokeAll(empty collection) returns empty collection
+     */
+    public void testTimedInvokeAll2() throws InterruptedException {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(),
+                                                 MEDIUM_DELAY_MS, MILLISECONDS);
+            assertTrue(r.isEmpty());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) throws NPE if c has null elements
+     */
+    public void testTimedInvokeAll3() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(null);
+            try {
+                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
+                shouldThrow();
+            } catch (NullPointerException success) {}
+        }
+    }
+
+    /**
+     * get of element of invokeAll(c) throws exception on failed task
+     */
+    public void testTimedInvokeAll4() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new NPETask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(1, futures.size());
+            try {
+                futures.get(0).get();
+                shouldThrow();
+            } catch (ExecutionException success) {
+                assertTrue(success.getCause() instanceof NullPointerException);
+            }
+        }
+    }
+
+    /**
+     * timed invokeAll(c) returns results of all completed tasks
+     */
+    public void testTimedInvokeAll5() throws Exception {
+        final ExecutorService e =
+            new ThreadPoolExecutor(2, 2,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(e)) {
+            List<Callable<String>> l = new ArrayList<>();
+            l.add(new StringTask());
+            l.add(new StringTask());
+            List<Future<String>> futures =
+                e.invokeAll(l, LONG_DELAY_MS, MILLISECONDS);
+            assertEquals(2, futures.size());
+            for (Future<String> future : futures)
+                assertSame(TEST_STRING, future.get());
+        }
+    }
+
+    /**
+     * timed invokeAll(c) cancels tasks not completed by timeout
+     */
+    public void testTimedInvokeAll6() throws Exception {
+        for (long timeout = timeoutMillis();;) {
+            final CountDownLatch done = new CountDownLatch(1);
+            final Callable<String> waiter = new CheckedCallable<String>() {
+                public String realCall() {
+                    try { done.await(LONG_DELAY_MS, MILLISECONDS); }
+                    catch (InterruptedException ok) {}
+                    return "1"; }};
+            final ExecutorService p =
+                new ThreadPoolExecutor(2, 2,
+                                       LONG_DELAY_MS, MILLISECONDS,
+                                       new ArrayBlockingQueue<Runnable>(10));
+            try (PoolCleaner cleaner = cleaner(p, done)) {
+                List<Callable<String>> tasks = new ArrayList<>();
+                tasks.add(new StringTask("0"));
+                tasks.add(waiter);
+                tasks.add(new StringTask("2"));
+                long startTime = System.nanoTime();
+                List<Future<String>> futures =
+                    p.invokeAll(tasks, timeout, MILLISECONDS);
+                assertEquals(tasks.size(), futures.size());
+                assertTrue(millisElapsedSince(startTime) >= timeout);
+                for (Future future : futures)
+                    assertTrue(future.isDone());
+                assertTrue(futures.get(1).isCancelled());
+                try {
+                    assertEquals("0", futures.get(0).get());
+                    assertEquals("2", futures.get(2).get());
+                    break;
+                } catch (CancellationException retryWithLongerTimeout) {
+                    timeout *= 2;
+                    if (timeout >= LONG_DELAY_MS / 2)
+                        fail("expected exactly one task to be cancelled");
+                }
+            }
+        }
+    }
+
+    /**
+     * Execution continues if there is at least one thread even if
+     * thread factory fails to create more
+     */
+    public void testFailingThreadFactory() throws InterruptedException {
+        final ExecutorService e =
+            new ThreadPoolExecutor(100, 100,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new LinkedBlockingQueue<Runnable>(),
+                                   new FailingThreadFactory());
+        try (PoolCleaner cleaner = cleaner(e)) {
+            final int TASKS = 100;
+            final CountDownLatch done = new CountDownLatch(TASKS);
+            for (int k = 0; k < TASKS; ++k)
+                e.execute(new CheckedRunnable() {
+                    public void realRun() {
+                        done.countDown();
+                    }});
+            assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * allowsCoreThreadTimeOut is by default false.
+     */
+    public void testAllowsCoreThreadTimeOut() {
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 2,
+                                   1000, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            assertFalse(p.allowsCoreThreadTimeOut());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(true) causes idle threads to time out
+     */
+    public void testAllowCoreThreadTimeOut_true() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 10,
+                                   keepAliveTime, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(true);
+            p.execute(new CheckedRunnable() {
+                public void realRun() {
+                    threadStarted.countDown();
+                    assertEquals(1, p.getPoolSize());
+                }});
+            await(threadStarted);
+            delay(keepAliveTime);
+            long startTime = System.nanoTime();
+            while (p.getPoolSize() > 0
+                   && millisElapsedSince(startTime) < LONG_DELAY_MS)
+                Thread.yield();
+            assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS);
+            assertEquals(0, p.getPoolSize());
+        }
+    }
+
+    /**
+     * allowCoreThreadTimeOut(false) causes idle threads not to time out
+     */
+    public void testAllowCoreThreadTimeOut_false() throws Exception {
+        long keepAliveTime = timeoutMillis();
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(2, 10,
+                                   keepAliveTime, MILLISECONDS,
+                                   new ArrayBlockingQueue<Runnable>(10));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            final CountDownLatch threadStarted = new CountDownLatch(1);
+            p.allowCoreThreadTimeOut(false);
+            p.execute(new CheckedRunnable() {
+                public void realRun() throws InterruptedException {
+                    threadStarted.countDown();
+                    assertTrue(p.getPoolSize() >= 1);
+                }});
+            delay(2 * keepAliveTime);
+            assertTrue(p.getPoolSize() >= 1);
+        }
+    }
+
+    /**
+     * execute allows the same task to be submitted multiple times, even
+     * if rejected
+     */
+    public void testRejectedRecycledTask() throws InterruptedException {
+        final int nTasks = 1000;
+        final CountDownLatch done = new CountDownLatch(nTasks);
+        final Runnable recycledTask = new Runnable() {
+            public void run() {
+                done.countDown();
+            }};
+        final ThreadPoolExecutor p =
+            new ThreadPoolExecutor(1, 30,
+                                   60, SECONDS,
+                                   new ArrayBlockingQueue(30));
+        try (PoolCleaner cleaner = cleaner(p)) {
+            for (int i = 0; i < nTasks; ++i) {
+                for (;;) {
+                    try {
+                        p.execute(recycledTask);
+                        break;
+                    }
+                    catch (RejectedExecutionException ignore) {}
+                }
+            }
+            // enough time to run all tasks
+            assertTrue(done.await(nTasks * SHORT_DELAY_MS, MILLISECONDS));
+        }
+    }
+
+    /**
+     * get(cancelled task) throws CancellationException
+     */
+    public void testGet_cancelled() throws Exception {
+        final CountDownLatch done = new CountDownLatch(1);
+        final ExecutorService e =
+            new ThreadPoolExecutor(1, 1,
+                                   LONG_DELAY_MS, MILLISECONDS,
+                                   new LinkedBlockingQueue<Runnable>());
+        try (PoolCleaner cleaner = cleaner(e, done)) {
+            final CountDownLatch blockerStarted = new CountDownLatch(1);
+            final List<Future<?>> futures = new ArrayList<>();
+            for (int i = 0; i < 2; i++) {
+                Runnable r = new CheckedRunnable() { public void realRun()
+                                                         throws Throwable {
+                    blockerStarted.countDown();
+                    assertTrue(done.await(2 * LONG_DELAY_MS, MILLISECONDS));
+                }};
+                futures.add(e.submit(r));
+            }
+            await(blockerStarted);
+            for (Future<?> future : futures) future.cancel(false);
+            for (Future<?> future : futures) {
+                try {
+                    future.get();
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                try {
+                    future.get(LONG_DELAY_MS, MILLISECONDS);
+                    shouldThrow();
+                } catch (CancellationException success) {}
+                assertTrue(future.isCancelled());
+                assertTrue(future.isDone());
+            }
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/ThreadTest.java b/ojluni/src/test/java/util/concurrent/tck/ThreadTest.java
new file mode 100644
index 0000000..d15ef92
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/ThreadTest.java
@@ -0,0 +1,101 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class ThreadTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(ThreadTest.class);
+    }
+
+    static class MyHandler implements Thread.UncaughtExceptionHandler {
+        public void uncaughtException(Thread t, Throwable e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * getUncaughtExceptionHandler returns ThreadGroup unless set,
+     * otherwise returning value of last setUncaughtExceptionHandler.
+     */
+    public void testGetAndSetUncaughtExceptionHandler() {
+        // these must be done all at once to avoid state
+        // dependencies across tests
+        Thread current = Thread.currentThread();
+        ThreadGroup tg = current.getThreadGroup();
+        MyHandler eh = new MyHandler();
+        assertSame(tg, current.getUncaughtExceptionHandler());
+        current.setUncaughtExceptionHandler(eh);
+        try {
+            assertSame(eh, current.getUncaughtExceptionHandler());
+        } finally {
+            current.setUncaughtExceptionHandler(null);
+        }
+        assertSame(tg, current.getUncaughtExceptionHandler());
+    }
+
+    /**
+     * getDefaultUncaughtExceptionHandler returns value of last
+     * setDefaultUncaughtExceptionHandler.
+     */
+    public void testGetAndSetDefaultUncaughtExceptionHandler() {
+        assertEquals(null, Thread.getDefaultUncaughtExceptionHandler());
+        // failure due to SecurityException is OK.
+        // Would be nice to explicitly test both ways, but cannot yet.
+        Thread.UncaughtExceptionHandler defaultHandler
+            = Thread.getDefaultUncaughtExceptionHandler();
+        MyHandler eh = new MyHandler();
+        try {
+            Thread.setDefaultUncaughtExceptionHandler(eh);
+            try {
+                assertSame(eh, Thread.getDefaultUncaughtExceptionHandler());
+            } finally {
+                Thread.setDefaultUncaughtExceptionHandler(defaultHandler);
+            }
+        } catch (SecurityException ok) {
+            assertNotNull(System.getSecurityManager());
+        }
+        assertSame(defaultHandler, Thread.getDefaultUncaughtExceptionHandler());
+    }
+
+    // How to test actually using UEH within junit?
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/TimeUnitTest.java b/ojluni/src/test/java/util/concurrent/tck/TimeUnitTest.java
new file mode 100644
index 0000000..8ff88597
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/TimeUnitTest.java
@@ -0,0 +1,634 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/
+ * Other contributors include Andrew Wright, Jeffrey Hayes,
+ * Pat Fisher, Mike Judd.
+ */
+
+package test.java.util.concurrent.tck;
+import static java.util.concurrent.TimeUnit.DAYS;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TimeUnitTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TimeUnitTest.class);
+    }
+
+    // (loops to 88888 check increments at all time divisions.)
+
+    /**
+     * convert correctly converts sample values across the units
+     */
+    public void testConvert() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*60*24,
+                         SECONDS.convert(t, DAYS));
+            assertEquals(t*60*60,
+                         SECONDS.convert(t, HOURS));
+            assertEquals(t*60,
+                         SECONDS.convert(t, MINUTES));
+            assertEquals(t,
+                         SECONDS.convert(t, SECONDS));
+            assertEquals(t,
+                         SECONDS.convert(1000L*t, MILLISECONDS));
+            assertEquals(t,
+                         SECONDS.convert(1000000L*t, MICROSECONDS));
+            assertEquals(t,
+                         SECONDS.convert(1000000000L*t, NANOSECONDS));
+
+            assertEquals(1000L*t*60*60*24,
+                         MILLISECONDS.convert(t, DAYS));
+            assertEquals(1000L*t*60*60,
+                         MILLISECONDS.convert(t, HOURS));
+            assertEquals(1000L*t*60,
+                         MILLISECONDS.convert(t, MINUTES));
+            assertEquals(1000L*t,
+                         MILLISECONDS.convert(t, SECONDS));
+            assertEquals(t,
+                         MILLISECONDS.convert(t, MILLISECONDS));
+            assertEquals(t,
+                         MILLISECONDS.convert(1000L*t, MICROSECONDS));
+            assertEquals(t,
+                         MILLISECONDS.convert(1000000L*t, NANOSECONDS));
+
+            assertEquals(1000000L*t*60*60*24,
+                         MICROSECONDS.convert(t, DAYS));
+            assertEquals(1000000L*t*60*60,
+                         MICROSECONDS.convert(t, HOURS));
+            assertEquals(1000000L*t*60,
+                         MICROSECONDS.convert(t, MINUTES));
+            assertEquals(1000000L*t,
+                         MICROSECONDS.convert(t, SECONDS));
+            assertEquals(1000L*t,
+                         MICROSECONDS.convert(t, MILLISECONDS));
+            assertEquals(t,
+                         MICROSECONDS.convert(t, MICROSECONDS));
+            assertEquals(t,
+                         MICROSECONDS.convert(1000L*t, NANOSECONDS));
+
+            assertEquals(1000000000L*t*60*60*24,
+                         NANOSECONDS.convert(t, DAYS));
+            assertEquals(1000000000L*t*60*60,
+                         NANOSECONDS.convert(t, HOURS));
+            assertEquals(1000000000L*t*60,
+                         NANOSECONDS.convert(t, MINUTES));
+            assertEquals(1000000000L*t,
+                         NANOSECONDS.convert(t, SECONDS));
+            assertEquals(1000000L*t,
+                         NANOSECONDS.convert(t, MILLISECONDS));
+            assertEquals(1000L*t,
+                         NANOSECONDS.convert(t, MICROSECONDS));
+            assertEquals(t,
+                         NANOSECONDS.convert(t, NANOSECONDS));
+        }
+
+        for (TimeUnit x : TimeUnit.values()) {
+            long[] zs = {
+                0, 1, -1,
+                Integer.MAX_VALUE, Integer.MIN_VALUE,
+                Long.MAX_VALUE, Long.MIN_VALUE,
+            };
+            for (long z : zs) assertEquals(z, x.convert(z, x));
+        }
+    }
+
+    /**
+     * toNanos correctly converts sample values in different units to
+     * nanoseconds
+     */
+    public void testToNanos() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000000000L*60*60*24,
+                         DAYS.toNanos(t));
+            assertEquals(t*1000000000L*60*60,
+                         HOURS.toNanos(t));
+            assertEquals(t*1000000000L*60,
+                         MINUTES.toNanos(t));
+            assertEquals(1000000000L*t,
+                         SECONDS.toNanos(t));
+            assertEquals(1000000L*t,
+                         MILLISECONDS.toNanos(t));
+            assertEquals(1000L*t,
+                         MICROSECONDS.toNanos(t));
+            assertEquals(t,
+                         NANOSECONDS.toNanos(t));
+        }
+    }
+
+    /**
+     * toMicros correctly converts sample values in different units to
+     * microseconds
+     */
+    public void testToMicros() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000000L*60*60*24,
+                         DAYS.toMicros(t));
+            assertEquals(t*1000000L*60*60,
+                         HOURS.toMicros(t));
+            assertEquals(t*1000000L*60,
+                         MINUTES.toMicros(t));
+            assertEquals(1000000L*t,
+                         SECONDS.toMicros(t));
+            assertEquals(1000L*t,
+                         MILLISECONDS.toMicros(t));
+            assertEquals(t,
+                         MICROSECONDS.toMicros(t));
+            assertEquals(t,
+                         NANOSECONDS.toMicros(t*1000L));
+        }
+    }
+
+    /**
+     * toMillis correctly converts sample values in different units to
+     * milliseconds
+     */
+    public void testToMillis() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*1000L*60*60*24,
+                         DAYS.toMillis(t));
+            assertEquals(t*1000L*60*60,
+                         HOURS.toMillis(t));
+            assertEquals(t*1000L*60,
+                         MINUTES.toMillis(t));
+            assertEquals(1000L*t,
+                         SECONDS.toMillis(t));
+            assertEquals(t,
+                         MILLISECONDS.toMillis(t));
+            assertEquals(t,
+                         MICROSECONDS.toMillis(t*1000L));
+            assertEquals(t,
+                         NANOSECONDS.toMillis(t*1000000L));
+        }
+    }
+
+    /**
+     * toSeconds correctly converts sample values in different units to
+     * seconds
+     */
+    public void testToSeconds() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*60*24,
+                         DAYS.toSeconds(t));
+            assertEquals(t*60*60,
+                         HOURS.toSeconds(t));
+            assertEquals(t*60,
+                         MINUTES.toSeconds(t));
+            assertEquals(t,
+                         SECONDS.toSeconds(t));
+            assertEquals(t,
+                         MILLISECONDS.toSeconds(t*1000L));
+            assertEquals(t,
+                         MICROSECONDS.toSeconds(t*1000000L));
+            assertEquals(t,
+                         NANOSECONDS.toSeconds(t*1000000000L));
+        }
+    }
+
+    /**
+     * toMinutes correctly converts sample values in different units to
+     * minutes
+     */
+    public void testToMinutes() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*60*24,
+                         DAYS.toMinutes(t));
+            assertEquals(t*60,
+                         HOURS.toMinutes(t));
+            assertEquals(t,
+                         MINUTES.toMinutes(t));
+            assertEquals(t,
+                         SECONDS.toMinutes(t*60));
+            assertEquals(t,
+                         MILLISECONDS.toMinutes(t*1000L*60));
+            assertEquals(t,
+                         MICROSECONDS.toMinutes(t*1000000L*60));
+            assertEquals(t,
+                         NANOSECONDS.toMinutes(t*1000000000L*60));
+        }
+    }
+
+    /**
+     * toHours correctly converts sample values in different units to
+     * hours
+     */
+    public void testToHours() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t*24,
+                         DAYS.toHours(t));
+            assertEquals(t,
+                         HOURS.toHours(t));
+            assertEquals(t,
+                         MINUTES.toHours(t*60));
+            assertEquals(t,
+                         SECONDS.toHours(t*60*60));
+            assertEquals(t,
+                         MILLISECONDS.toHours(t*1000L*60*60));
+            assertEquals(t,
+                         MICROSECONDS.toHours(t*1000000L*60*60));
+            assertEquals(t,
+                         NANOSECONDS.toHours(t*1000000000L*60*60));
+        }
+    }
+
+    /**
+     * toDays correctly converts sample values in different units to
+     * days
+     */
+    public void testToDays() {
+        for (long t = 0; t < 88888; ++t) {
+            assertEquals(t,
+                         DAYS.toDays(t));
+            assertEquals(t,
+                         HOURS.toDays(t*24));
+            assertEquals(t,
+                         MINUTES.toDays(t*60*24));
+            assertEquals(t,
+                         SECONDS.toDays(t*60*60*24));
+            assertEquals(t,
+                         MILLISECONDS.toDays(t*1000L*60*60*24));
+            assertEquals(t,
+                         MICROSECONDS.toDays(t*1000000L*60*60*24));
+            assertEquals(t,
+                         NANOSECONDS.toDays(t*1000000000L*60*60*24));
+        }
+    }
+
+    /**
+     * convert saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testConvertSaturate() {
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, SECONDS));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, SECONDS));
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, MINUTES));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, MINUTES));
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, HOURS));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, HOURS));
+        assertEquals(Long.MAX_VALUE,
+                     NANOSECONDS.convert(Long.MAX_VALUE / 2, DAYS));
+        assertEquals(Long.MIN_VALUE,
+                     NANOSECONDS.convert(-Long.MAX_VALUE / 4, DAYS));
+
+        for (TimeUnit x : TimeUnit.values())
+            for (TimeUnit y : TimeUnit.values()) {
+                long ratio = x.toNanos(1) / y.toNanos(1);
+                if (ratio >= 1) {
+                    assertEquals(ratio, y.convert(1, x));
+                    assertEquals(1, x.convert(ratio, y));
+                    long max = Long.MAX_VALUE/ratio;
+                    assertEquals(max * ratio, y.convert(max, x));
+                    assertEquals(-max * ratio, y.convert(-max, x));
+                    assertEquals(max, x.convert(max * ratio, y));
+                    assertEquals(-max, x.convert(-max * ratio, y));
+                    if (max < Long.MAX_VALUE) {
+                        assertEquals(Long.MAX_VALUE, y.convert(max + 1, x));
+                        assertEquals(Long.MIN_VALUE, y.convert(-max - 1, x));
+                        assertEquals(Long.MIN_VALUE, y.convert(Long.MIN_VALUE + 1, x));
+                    }
+                    assertEquals(Long.MAX_VALUE, y.convert(Long.MAX_VALUE, x));
+                    assertEquals(Long.MIN_VALUE, y.convert(Long.MIN_VALUE, x));
+                }
+            }
+    }
+
+    /**
+     * toNanos saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToNanosSaturate() {
+        assertEquals(Long.MAX_VALUE,
+                     MILLISECONDS.toNanos(Long.MAX_VALUE / 2));
+        assertEquals(Long.MIN_VALUE,
+                     MILLISECONDS.toNanos(-Long.MAX_VALUE / 3));
+
+        for (TimeUnit x : TimeUnit.values()) {
+            long ratio = x.toNanos(1) / NANOSECONDS.toNanos(1);
+            if (ratio >= 1) {
+                long max = Long.MAX_VALUE/ratio;
+                for (long z : new long[] {0, 1, -1, max, -max})
+                    assertEquals(z * ratio, x.toNanos(z));
+                if (max < Long.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toNanos(max + 1));
+                    assertEquals(Long.MIN_VALUE, x.toNanos(-max - 1));
+                    assertEquals(Long.MIN_VALUE, x.toNanos(Long.MIN_VALUE + 1));
+                }
+                assertEquals(Long.MAX_VALUE, x.toNanos(Long.MAX_VALUE));
+                assertEquals(Long.MIN_VALUE, x.toNanos(Long.MIN_VALUE));
+                if (max < Integer.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toNanos(Integer.MAX_VALUE));
+                    assertEquals(Long.MIN_VALUE, x.toNanos(Integer.MIN_VALUE));
+                }
+            }
+        }
+    }
+
+    /**
+     * toMicros saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToMicrosSaturate() {
+        for (TimeUnit x : TimeUnit.values()) {
+            long ratio = x.toNanos(1) / MICROSECONDS.toNanos(1);
+            if (ratio >= 1) {
+                long max = Long.MAX_VALUE/ratio;
+                for (long z : new long[] {0, 1, -1, max, -max})
+                    assertEquals(z * ratio, x.toMicros(z));
+                if (max < Long.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toMicros(max + 1));
+                    assertEquals(Long.MIN_VALUE, x.toMicros(-max - 1));
+                    assertEquals(Long.MIN_VALUE, x.toMicros(Long.MIN_VALUE + 1));
+                }
+                assertEquals(Long.MAX_VALUE, x.toMicros(Long.MAX_VALUE));
+                assertEquals(Long.MIN_VALUE, x.toMicros(Long.MIN_VALUE));
+                if (max < Integer.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toMicros(Integer.MAX_VALUE));
+                    assertEquals(Long.MIN_VALUE, x.toMicros(Integer.MIN_VALUE));
+                }
+            }
+        }
+    }
+
+    /**
+     * toMillis saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToMillisSaturate() {
+        for (TimeUnit x : TimeUnit.values()) {
+            long ratio = x.toNanos(1) / MILLISECONDS.toNanos(1);
+            if (ratio >= 1) {
+                long max = Long.MAX_VALUE/ratio;
+                for (long z : new long[] {0, 1, -1, max, -max})
+                    assertEquals(z * ratio, x.toMillis(z));
+                if (max < Long.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toMillis(max + 1));
+                    assertEquals(Long.MIN_VALUE, x.toMillis(-max - 1));
+                    assertEquals(Long.MIN_VALUE, x.toMillis(Long.MIN_VALUE + 1));
+                }
+                assertEquals(Long.MAX_VALUE, x.toMillis(Long.MAX_VALUE));
+                assertEquals(Long.MIN_VALUE, x.toMillis(Long.MIN_VALUE));
+                if (max < Integer.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toMillis(Integer.MAX_VALUE));
+                    assertEquals(Long.MIN_VALUE, x.toMillis(Integer.MIN_VALUE));
+                }
+            }
+        }
+    }
+
+    /**
+     * toSeconds saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToSecondsSaturate() {
+        for (TimeUnit x : TimeUnit.values()) {
+            long ratio = x.toNanos(1) / SECONDS.toNanos(1);
+            if (ratio >= 1) {
+                long max = Long.MAX_VALUE/ratio;
+                for (long z : new long[] {0, 1, -1, max, -max})
+                    assertEquals(z * ratio, x.toSeconds(z));
+                if (max < Long.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toSeconds(max + 1));
+                    assertEquals(Long.MIN_VALUE, x.toSeconds(-max - 1));
+                    assertEquals(Long.MIN_VALUE, x.toSeconds(Long.MIN_VALUE + 1));
+                }
+                assertEquals(Long.MAX_VALUE, x.toSeconds(Long.MAX_VALUE));
+                assertEquals(Long.MIN_VALUE, x.toSeconds(Long.MIN_VALUE));
+                if (max < Integer.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toSeconds(Integer.MAX_VALUE));
+                    assertEquals(Long.MIN_VALUE, x.toSeconds(Integer.MIN_VALUE));
+                }
+            }
+        }
+    }
+
+    /**
+     * toMinutes saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToMinutesSaturate() {
+        for (TimeUnit x : TimeUnit.values()) {
+            long ratio = x.toNanos(1) / MINUTES.toNanos(1);
+            if (ratio > 1) {
+                long max = Long.MAX_VALUE/ratio;
+                for (long z : new long[] {0, 1, -1, max, -max})
+                    assertEquals(z * ratio, x.toMinutes(z));
+                assertEquals(Long.MAX_VALUE, x.toMinutes(max + 1));
+                assertEquals(Long.MIN_VALUE, x.toMinutes(-max - 1));
+                assertEquals(Long.MAX_VALUE, x.toMinutes(Long.MAX_VALUE));
+                assertEquals(Long.MIN_VALUE, x.toMinutes(Long.MIN_VALUE));
+                assertEquals(Long.MIN_VALUE, x.toMinutes(Long.MIN_VALUE + 1));
+            }
+        }
+    }
+
+    /**
+     * toHours saturates positive too-large values to Long.MAX_VALUE
+     * and negative to LONG.MIN_VALUE
+     */
+    public void testToHoursSaturate() {
+        for (TimeUnit x : TimeUnit.values()) {
+            long ratio = x.toNanos(1) / HOURS.toNanos(1);
+            if (ratio >= 1) {
+                long max = Long.MAX_VALUE/ratio;
+                for (long z : new long[] {0, 1, -1, max, -max})
+                    assertEquals(z * ratio, x.toHours(z));
+                if (max < Long.MAX_VALUE) {
+                    assertEquals(Long.MAX_VALUE, x.toHours(max + 1));
+                    assertEquals(Long.MIN_VALUE, x.toHours(-max - 1));
+                    assertEquals(Long.MIN_VALUE, x.toHours(Long.MIN_VALUE + 1));
+                }
+                assertEquals(Long.MAX_VALUE, x.toHours(Long.MAX_VALUE));
+                assertEquals(Long.MIN_VALUE, x.toHours(Long.MIN_VALUE));
+            }
+        }
+    }
+
+    /**
+     * toString returns name of unit
+     */
+    public void testToString() {
+        assertEquals("SECONDS", SECONDS.toString());
+    }
+
+    /**
+     * name returns name of unit
+     */
+    public void testName() {
+        assertEquals("SECONDS", SECONDS.name());
+    }
+
+    /**
+     * Timed wait without holding lock throws
+     * IllegalMonitorStateException
+     */
+    public void testTimedWait_IllegalMonitorException() {
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Object o = new Object();
+                TimeUnit tu = MILLISECONDS;
+
+                try {
+                    tu.timedWait(o, LONG_DELAY_MS);
+                    threadShouldThrow();
+                } catch (IllegalMonitorStateException success) {}
+            }});
+
+        awaitTermination(t);
+    }
+
+    /**
+     * timedWait throws InterruptedException when interrupted
+     */
+    public void testTimedWait_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                Object o = new Object();
+                TimeUnit tu = MILLISECONDS;
+
+                Thread.currentThread().interrupt();
+                try {
+                    synchronized (o) {
+                        tu.timedWait(o, LONG_DELAY_MS);
+                    }
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    synchronized (o) {
+                        tu.timedWait(o, LONG_DELAY_MS);
+                    }
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * timedJoin throws InterruptedException when interrupted
+     */
+    public void testTimedJoin_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        final Thread s = newStartedThread(new CheckedInterruptedRunnable() {
+            public void realRun() throws InterruptedException {
+                Thread.sleep(LONG_DELAY_MS);
+            }});
+        final Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                TimeUnit tu = MILLISECONDS;
+                Thread.currentThread().interrupt();
+                try {
+                    tu.timedJoin(s, LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    tu.timedJoin(s, LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+        s.interrupt();
+        awaitTermination(s);
+    }
+
+    /**
+     * timedSleep throws InterruptedException when interrupted
+     */
+    public void testTimedSleep_Interruptible() {
+        final CountDownLatch pleaseInterrupt = new CountDownLatch(1);
+        Thread t = newStartedThread(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                TimeUnit tu = MILLISECONDS;
+                Thread.currentThread().interrupt();
+                try {
+                    tu.sleep(LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+
+                pleaseInterrupt.countDown();
+                try {
+                    tu.sleep(LONG_DELAY_MS);
+                    shouldThrow();
+                } catch (InterruptedException success) {}
+                assertFalse(Thread.interrupted());
+            }});
+
+        await(pleaseInterrupt);
+        assertThreadStaysAlive(t);
+        t.interrupt();
+        awaitTermination(t);
+    }
+
+    /**
+     * a deserialized serialized unit is the same instance
+     */
+    public void testSerialization() throws Exception {
+        for (TimeUnit x : TimeUnit.values())
+            assertSame(x, serialClone(x));
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/TreeMapTest.java b/ojluni/src/test/java/util/concurrent/tck/TreeMapTest.java
new file mode 100644
index 0000000..0953302
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/TreeMapTest.java
@@ -0,0 +1,1112 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static TreeMap map5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        TreeMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * copy constructor creates map equal to source map
+     */
+    public void testConstructFromSorted() {
+        TreeMap map = map5();
+        TreeMap map2 = new TreeMap(map);
+        assertEquals(map, map2);
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        TreeMap map1 = map5();
+        TreeMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        TreeMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        TreeMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        TreeMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        TreeMap empty = new TreeMap();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        TreeMap empty = new TreeMap();
+        TreeMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        TreeMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        TreeMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testKeySetToArray() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingKeySetToArray() {
+        TreeMap map = map5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        TreeMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of key set is inverse ordered
+     */
+    public void testKeySetDescendingIteratorOrder() {
+        TreeMap map = map5();
+        NavigableSet s = map.navigableKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descendingKeySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        TreeMap map = map5();
+        Set s = map.descendingKeySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, five);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * descending iterator of descendingKeySet is ordered
+     */
+    public void testDescendingKeySetDescendingIteratorOrder() {
+        TreeMap map = map5();
+        NavigableSet s = map.descendingKeySet();
+        Iterator i = s.descendingIterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        int count = 1;
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+            ++count;
+        }
+        assertEquals(5, count);
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        TreeMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        TreeMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * descendingEntrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        TreeMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * entrySet.toArray contains all entries
+     */
+    public void testEntrySetToArray() {
+        TreeMap map = map5();
+        Set s = map.entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * descendingEntrySet.toArray contains all entries
+     */
+    public void testDescendingEntrySetToArray() {
+        TreeMap map = map5();
+        Set s = map.descendingMap().entrySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        for (int i = 0; i < 5; ++i) {
+            assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey()));
+            assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue()));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        TreeMap empty = new TreeMap();
+        TreeMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        TreeMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        TreeMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * lowerKey returns preceding element
+     */
+    public void testLowerKey() {
+        TreeMap q = map5();
+        Object e1 = q.lowerKey(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lowerKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lowerKey(one);
+        assertNull(e3);
+
+        Object e4 = q.lowerKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherKey returns next element
+     */
+    public void testHigherKey() {
+        TreeMap q = map5();
+        Object e1 = q.higherKey(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higherKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higherKey(five);
+        assertNull(e3);
+
+        Object e4 = q.higherKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorKey returns preceding element
+     */
+    public void testFloorKey() {
+        TreeMap q = map5();
+        Object e1 = q.floorKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floorKey(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floorKey(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floorKey(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingKey returns next element
+     */
+    public void testCeilingKey() {
+        TreeMap q = map5();
+        Object e1 = q.ceilingKey(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceilingKey(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceilingKey(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceilingKey(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        TreeMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        TreeMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        TreeMap map = map5();
+        TreeMap empty = new TreeMap();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        TreeMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        TreeMap c = map5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        TreeMap c = map5();
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE for nonempty map
+     */
+    public void testRemove1_NullPointerException() {
+        TreeMap c = new TreeMap();
+        c.put("sadsdf", "asdads");
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.subMap(two, true, four, false);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        TreeMap map = map5();
+        NavigableMap sm = map.subMap(two, true, three, false);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.headMap(four, false);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        TreeMap map = map5();
+        NavigableMap sm = map.tailMap(two, true);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+        Iterator r = sm.descendingKeySet().iterator();
+        k = (Integer)(r.next());
+        assertEquals(five, k);
+        k = (Integer)(r.next());
+        assertEquals(four, k);
+        k = (Integer)(r.next());
+        assertEquals(three, k);
+        k = (Integer)(r.next());
+        assertEquals(two, k);
+        assertFalse(r.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        NavigableMap ssm = sm.tailMap(four, true);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Submaps of submaps subdivide correctly
+     */
+    public void testRecursiveSubMaps() throws Exception {
+        int mapSize = expensiveTests ? 1000 : 100;
+        Class cl = TreeMap.class;
+        NavigableMap<Integer, Integer> map = newMap(cl);
+        bs = new BitSet(mapSize);
+
+        populate(map, mapSize);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        mutateMap(map, 0, mapSize - 1);
+        check(map,                 0, mapSize - 1, true);
+        check(map.descendingMap(), 0, mapSize - 1, false);
+
+        bashSubMap(map.subMap(0, true, mapSize, false),
+                   0, mapSize - 1, true);
+    }
+
+    static NavigableMap<Integer, Integer> newMap(Class cl) throws Exception {
+        NavigableMap<Integer, Integer> result
+            = (NavigableMap<Integer, Integer>) cl.getConstructor().newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.keySet().iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableMap<Integer, Integer> map, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int key = rnd.nextInt(limit);
+            put(map, key);
+        }
+    }
+
+    void mutateMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min + rnd.nextInt(rangeSize);
+            assertTrue(key >= min && key <= max);
+            put(map, key);
+        }
+    }
+
+    void mutateSubMap(NavigableMap<Integer, Integer> map, int min, int max) {
+        int size = map.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(map, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = map.keySet().iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (map.size() < size) {
+            int key = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (key >= min && key <= max) {
+                put(map, key);
+            } else {
+                try {
+                    map.put(key, 2 * key);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableMap<Integer, Integer> map, int key) {
+        if (map.put(key, 2 * key) == null)
+            bs.set(key);
+    }
+
+    void remove(NavigableMap<Integer, Integer> map, int key) {
+        if (map.remove(key) != null)
+            bs.clear(key);
+    }
+
+    void bashSubMap(NavigableMap<Integer, Integer> map,
+                    int min, int max, boolean ascending) {
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        mutateSubMap(map, min, max);
+        check(map, min, max, ascending);
+        check(map.descendingMap(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headMap - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> hm = map.headMap(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubMap(hm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubMap(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubMap(hm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailMap - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableMap<Integer,Integer> tm = map.tailMap(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubMap(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubMap(tm.descendingMap(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubMap(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubMap(tm.descendingMap(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subMap - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableMap<Integer,Integer> sm = map.subMap(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubMap(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubMap(sm.descendingMap(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableMap<Integer, Integer> map,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int key) {
+                return ascending ? lowerAscending(key) : higherAscending(key);
+            }
+            int floor(int key) {
+                return ascending ? floorAscending(key) : ceilingAscending(key);
+            }
+            int ceiling(int key) {
+                return ascending ? ceilingAscending(key) : floorAscending(key);
+            }
+            int higher(int key) {
+                return ascending ? higherAscending(key) : lowerAscending(key);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int key) {
+                return floorAscending(key - 1);
+            }
+            int floorAscending(int key) {
+                if (key < min)
+                    return -1;
+                else if (key > max)
+                    key = max;
+
+                // BitSet should support this! Test would run much faster
+                while (key >= min) {
+                    if (bs.get(key))
+                        return key;
+                    key--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int key) {
+                if (key < min)
+                    key = min;
+                else if (key > max)
+                    return -1;
+                int result = bs.nextSetBit(key);
+                return result > max ? -1 : result;
+            }
+            int higherAscending(int key) {
+                return ceilingAscending(key + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return result > max ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return result < min ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsKey
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, map.containsKey(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, map.size());
+
+        // Test contents using contains keySet iterator
+        int size2 = 0;
+        int previousKey = -1;
+        for (int key : map.keySet()) {
+            assertTrue(bs.get(key));
+            size2++;
+            assertTrue(previousKey < 0 ||
+                (ascending ? key - previousKey > 0 : key - previousKey < 0));
+            previousKey = key;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int key = min - 1; key <= max + 1; key++) {
+            assertEq(map.lowerKey(key), rs.lower(key));
+            assertEq(map.floorKey(key), rs.floor(key));
+            assertEq(map.higherKey(key), rs.higher(key));
+            assertEq(map.ceilingKey(key), rs.ceiling(key));
+        }
+
+        // Test extrema
+        if (map.size() != 0) {
+            assertEq(map.firstKey(), rs.first());
+            assertEq(map.lastKey(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                map.firstKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                map.lastKey();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return i == null ? j == -1 : i == j;
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/TreeSetTest.java b/ojluni/src/test/java/util/concurrent/tck/TreeSetTest.java
new file mode 100644
index 0000000..9f40007
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/TreeSetTest.java
@@ -0,0 +1,1010 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.NoSuchElementException;
+import java.util.Random;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * The number of elements to place in collections, arrays, etc.
+     */
+    static final int SIZE = 20;
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private TreeSet<Integer> populatedSet(int n) {
+        TreeSet<Integer> q = new TreeSet<>();
+        assertTrue(q.isEmpty());
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertFalse(q.isEmpty());
+        assertEquals(n, q.size());
+        return q;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private TreeSet set5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        assertEquals(5, q.size());
+        return q;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, new TreeSet().size());
+    }
+
+    /**
+     * Initializing from null Collection throws NPE
+     */
+    public void testConstructor3() {
+        try {
+            new TreeSet((Collection)null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection of null elements throws NPE
+     */
+    public void testConstructor4() {
+        try {
+            new TreeSet(Arrays.asList(new Integer[SIZE]));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Initializing from Collection with some null elements throws NPE
+     */
+    public void testConstructor5() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            new TreeSet(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of collection used to initialize
+     */
+    public void testConstructor6() {
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        TreeSet q = new TreeSet(Arrays.asList(ints));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * The comparator used in constructor is used
+     */
+    public void testConstructor7() {
+        MyReverseComparator cmp = new MyReverseComparator();
+        TreeSet q = new TreeSet(cmp);
+        assertEquals(cmp, q.comparator());
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(i);
+        q.addAll(Arrays.asList(ints));
+        for (int i = SIZE - 1; i >= 0; --i)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.add(new Integer(2));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE if nonempty
+     */
+    public void testAddNull() {
+        TreeSet q = populatedSet(SIZE);
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.add(zero));
+        assertTrue(q.add(one));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.add(zero));
+        assertFalse(q.add(zero));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        TreeSet q = new TreeSet();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        TreeSet q = new TreeSet();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        TreeSet q = new TreeSet();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        TreeSet q = new TreeSet();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        TreeSet q = new TreeSet();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * pollFirst succeeds unless empty
+     */
+    public void testPollFirst() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * pollLast succeeds unless empty
+     */
+    public void testPollLast() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = SIZE - 1; i >= 0; --i) {
+            assertEquals(i, q.pollLast());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        TreeSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        TreeSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        q.add(new Integer(1));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        TreeSet q = populatedSet(SIZE);
+        TreeSet p = new TreeSet();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        TreeSet q = populatedSet(SIZE);
+        TreeSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            TreeSet q = populatedSet(SIZE);
+            TreeSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        TreeSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        TreeSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        TreeSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        TreeSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        TreeSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        TreeSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        TreeSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(new TreeSet().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final TreeSet q = new TreeSet();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(it.next(), new Integer(2));
+        assertEquals(it.next(), new Integer(3));
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        TreeSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        TreeSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        TreeSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    Random rnd = new Random(666);
+    BitSet bs;
+
+    /**
+     * Subsets of subsets subdivide correctly
+     */
+    public void testRecursiveSubSets() throws Exception {
+        int setSize = expensiveTests ? 1000 : 100;
+        Class cl = TreeSet.class;
+
+        NavigableSet<Integer> set = newSet(cl);
+        bs = new BitSet(setSize);
+
+        populate(set, setSize);
+        check(set,                 0, setSize - 1, true);
+        check(set.descendingSet(), 0, setSize - 1, false);
+
+        mutateSet(set, 0, setSize - 1);
+        check(set,                 0, setSize - 1, true);
+        check(set.descendingSet(), 0, setSize - 1, false);
+
+        bashSubSet(set.subSet(0, true, setSize, false),
+                   0, setSize - 1, true);
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new TreeSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    static NavigableSet<Integer> newSet(Class cl) throws Exception {
+        NavigableSet<Integer> result =
+            (NavigableSet<Integer>) cl.getConstructor().newInstance();
+        assertEquals(0, result.size());
+        assertFalse(result.iterator().hasNext());
+        return result;
+    }
+
+    void populate(NavigableSet<Integer> set, int limit) {
+        for (int i = 0, n = 2 * limit / 3; i < n; i++) {
+            int element = rnd.nextInt(limit);
+            put(set, element);
+        }
+    }
+
+    void mutateSet(NavigableSet<Integer> set, int min, int max) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min + rnd.nextInt(rangeSize);
+            assertTrue(element >= min && element <= max);
+            put(set, element);
+        }
+    }
+
+    void mutateSubSet(NavigableSet<Integer> set, int min, int max) {
+        int size = set.size();
+        int rangeSize = max - min + 1;
+
+        // Remove a bunch of entries directly
+        for (int i = 0, n = rangeSize / 2; i < n; i++) {
+            remove(set, min - 5 + rnd.nextInt(rangeSize + 10));
+        }
+
+        // Remove a bunch of entries with iterator
+        for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
+            if (rnd.nextBoolean()) {
+                bs.clear(it.next());
+                it.remove();
+            }
+        }
+
+        // Add entries till we're back to original size
+        while (set.size() < size) {
+            int element = min - 5 + rnd.nextInt(rangeSize + 10);
+            if (element >= min && element <= max) {
+                put(set, element);
+            } else {
+                try {
+                    set.add(element);
+                    shouldThrow();
+                } catch (IllegalArgumentException success) {}
+            }
+        }
+    }
+
+    void put(NavigableSet<Integer> set, int element) {
+        if (set.add(element))
+            bs.set(element);
+    }
+
+    void remove(NavigableSet<Integer> set, int element) {
+        if (set.remove(element))
+            bs.clear(element);
+    }
+
+    void bashSubSet(NavigableSet<Integer> set,
+                    int min, int max, boolean ascending) {
+        check(set, min, max, ascending);
+        check(set.descendingSet(), min, max, !ascending);
+
+        mutateSubSet(set, min, max);
+        check(set, min, max, ascending);
+        check(set.descendingSet(), min, max, !ascending);
+
+        // Recurse
+        if (max - min < 2)
+            return;
+        int midPoint = (min + max) / 2;
+
+        // headSet - pick direction and endpoint inclusion randomly
+        boolean incl = rnd.nextBoolean();
+        NavigableSet<Integer> hm = set.headSet(midPoint, incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, min, midPoint - (incl ? 0 : 1), true);
+            else
+                bashSubSet(hm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           false);
+        } else {
+            if (rnd.nextBoolean())
+                bashSubSet(hm, midPoint + (incl ? 0 : 1), max, false);
+            else
+                bashSubSet(hm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           true);
+        }
+
+        // tailSet - pick direction and endpoint inclusion randomly
+        incl = rnd.nextBoolean();
+        NavigableSet<Integer> tm = set.tailSet(midPoint,incl);
+        if (ascending) {
+            if (rnd.nextBoolean())
+                bashSubSet(tm, midPoint + (incl ? 0 : 1), max, true);
+            else
+                bashSubSet(tm.descendingSet(), midPoint + (incl ? 0 : 1), max,
+                           false);
+        } else {
+            if (rnd.nextBoolean()) {
+                bashSubSet(tm, min, midPoint - (incl ? 0 : 1), false);
+            } else {
+                bashSubSet(tm.descendingSet(), min, midPoint - (incl ? 0 : 1),
+                           true);
+            }
+        }
+
+        // subSet - pick direction and endpoint inclusion randomly
+        int rangeSize = max - min + 1;
+        int[] endpoints = new int[2];
+        endpoints[0] = min + rnd.nextInt(rangeSize);
+        endpoints[1] = min + rnd.nextInt(rangeSize);
+        Arrays.sort(endpoints);
+        boolean lowIncl = rnd.nextBoolean();
+        boolean highIncl = rnd.nextBoolean();
+        if (ascending) {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[0], lowIncl, endpoints[1], highIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+        } else {
+            NavigableSet<Integer> sm = set.subSet(
+                endpoints[1], highIncl, endpoints[0], lowIncl);
+            if (rnd.nextBoolean())
+                bashSubSet(sm, endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), false);
+            else
+                bashSubSet(sm.descendingSet(), endpoints[0] + (lowIncl ? 0 : 1),
+                           endpoints[1] - (highIncl ? 0 : 1), true);
+        }
+    }
+
+    /**
+     * min and max are both inclusive.  If max < min, interval is empty.
+     */
+    void check(NavigableSet<Integer> set,
+                      final int min, final int max, final boolean ascending) {
+        class ReferenceSet {
+            int lower(int element) {
+                return ascending ?
+                    lowerAscending(element) : higherAscending(element);
+            }
+            int floor(int element) {
+                return ascending ?
+                    floorAscending(element) : ceilingAscending(element);
+            }
+            int ceiling(int element) {
+                return ascending ?
+                    ceilingAscending(element) : floorAscending(element);
+            }
+            int higher(int element) {
+                return ascending ?
+                    higherAscending(element) : lowerAscending(element);
+            }
+            int first() {
+                return ascending ? firstAscending() : lastAscending();
+            }
+            int last() {
+                return ascending ? lastAscending() : firstAscending();
+            }
+            int lowerAscending(int element) {
+                return floorAscending(element - 1);
+            }
+            int floorAscending(int element) {
+                if (element < min)
+                    return -1;
+                else if (element > max)
+                    element = max;
+
+                // BitSet should support this! Test would run much faster
+                while (element >= min) {
+                    if (bs.get(element))
+                        return element;
+                    element--;
+                }
+                return -1;
+            }
+            int ceilingAscending(int element) {
+                if (element < min)
+                    element = min;
+                else if (element > max)
+                    return -1;
+                int result = bs.nextSetBit(element);
+                return (result > max) ? -1 : result;
+            }
+            int higherAscending(int element) {
+                return ceilingAscending(element + 1);
+            }
+            private int firstAscending() {
+                int result = ceilingAscending(min);
+                return (result > max) ? -1 : result;
+            }
+            private int lastAscending() {
+                int result = floorAscending(max);
+                return (result < min) ? -1 : result;
+            }
+        }
+        ReferenceSet rs = new ReferenceSet();
+
+        // Test contents using containsElement
+        int size = 0;
+        for (int i = min; i <= max; i++) {
+            boolean bsContainsI = bs.get(i);
+            assertEquals(bsContainsI, set.contains(i));
+            if (bsContainsI)
+                size++;
+        }
+        assertEquals(size, set.size());
+
+        // Test contents using contains elementSet iterator
+        int size2 = 0;
+        int previousElement = -1;
+        for (int element : set) {
+            assertTrue(bs.get(element));
+            size2++;
+            assertTrue(previousElement < 0 || (ascending ?
+                element - previousElement > 0 : element - previousElement < 0));
+            previousElement = element;
+        }
+        assertEquals(size2, size);
+
+        // Test navigation ops
+        for (int element = min - 1; element <= max + 1; element++) {
+            assertEq(set.lower(element), rs.lower(element));
+            assertEq(set.floor(element), rs.floor(element));
+            assertEq(set.higher(element), rs.higher(element));
+            assertEq(set.ceiling(element), rs.ceiling(element));
+        }
+
+        // Test extrema
+        if (set.size() != 0) {
+            assertEq(set.first(), rs.first());
+            assertEq(set.last(), rs.last());
+        } else {
+            assertEq(rs.first(), -1);
+            assertEq(rs.last(),  -1);
+            try {
+                set.first();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+            try {
+                set.last();
+                shouldThrow();
+            } catch (NoSuchElementException success) {}
+        }
+    }
+
+    static void assertEq(Integer i, int j) {
+        if (i == null)
+            assertEquals(j, -1);
+        else
+            assertEquals((int) i, j);
+    }
+
+    static boolean eq(Integer i, int j) {
+        return (i == null) ? j == -1 : i == j;
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/TreeSubMapTest.java b/ojluni/src/test/java/util/concurrent/tck/TreeSubMapTest.java
new file mode 100644
index 0000000..77ff558
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/TreeSubMapTest.java
@@ -0,0 +1,1139 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeSubMapTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeSubMapTest.class);
+    }
+
+    /**
+     * Returns a new map from Integers 1-5 to Strings "A"-"E".
+     */
+    private static NavigableMap map5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(zero, "Z");
+        map.put(one, "A");
+        map.put(five, "E");
+        map.put(three, "C");
+        map.put(two, "B");
+        map.put(four, "D");
+        map.put(seven, "F");
+        assertFalse(map.isEmpty());
+        assertEquals(7, map.size());
+        return map.subMap(one, true, seven, false);
+    }
+
+    private static NavigableMap map0() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        return map.tailMap(one, true);
+    }
+
+    /**
+     * Returns a new map from Integers -5 to -1 to Strings "A"-"E".
+     */
+    private static NavigableMap dmap5() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        map.put(m1, "A");
+        map.put(m5, "E");
+        map.put(m3, "C");
+        map.put(m2, "B");
+        map.put(m4, "D");
+        assertFalse(map.isEmpty());
+        assertEquals(5, map.size());
+        return map.descendingMap();
+    }
+
+    private static NavigableMap dmap0() {
+        TreeMap map = new TreeMap();
+        assertTrue(map.isEmpty());
+        return map;
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testClear() {
+        NavigableMap map = map5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testEquals() {
+        NavigableMap map1 = map5();
+        NavigableMap map2 = map5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testContainsKey() {
+        NavigableMap map = map5();
+        assertTrue(map.containsKey(one));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testContainsValue() {
+        NavigableMap map = map5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testGet() {
+        NavigableMap map = map5();
+        assertEquals("A", (String)map.get(one));
+        NavigableMap empty = map0();
+        assertNull(empty.get(one));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testIsEmpty() {
+        NavigableMap empty = map0();
+        NavigableMap map = map5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testFirstKey() {
+        NavigableMap map = map5();
+        assertEquals(one, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testLastKey() {
+        NavigableMap map = map5();
+        assertEquals(five, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testKeySet() {
+        NavigableMap map = map5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(one));
+        assertTrue(s.contains(two));
+        assertTrue(s.contains(three));
+        assertTrue(s.contains(four));
+        assertTrue(s.contains(five));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testKeySetOrder() {
+        NavigableMap map = map5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, one);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) < 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testValues() {
+        NavigableMap map = map5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testEntrySet() {
+        NavigableMap map = map5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(one) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(two) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(three) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(four) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(five) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testPutAll() {
+        NavigableMap empty = map0();
+        NavigableMap map = map5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(one));
+        assertTrue(empty.containsKey(two));
+        assertTrue(empty.containsKey(three));
+        assertTrue(empty.containsKey(four));
+        assertTrue(empty.containsKey(five));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testRemove() {
+        NavigableMap map = map5();
+        map.remove(five);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(five));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testLowerEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.lowerEntry(three);
+        assertEquals(two, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(one);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testHigherEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.higherEntry(three);
+        assertEquals(four, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(five);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testFloorEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.floorEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(six);
+        assertEquals(five, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(one);
+        assertEquals(one, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testCeilingEntry() {
+        NavigableMap map = map5();
+        Map.Entry e1 = map.ceilingEntry(three);
+        assertEquals(three, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(one, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(five);
+        assertEquals(five, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(six);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testPollFirstEntry() {
+        NavigableMap map = map5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(two, e.getKey());
+        map.put(one, "A");
+        e = map.pollFirstEntry();
+        assertEquals(one, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(three, e.getKey());
+        map.remove(four);
+        e = map.pollFirstEntry();
+        assertEquals(five, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        assertTrue(map.isEmpty());
+        Map.Entry f = map.firstEntry();
+        assertNull(f);
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testPollLastEntry() {
+        NavigableMap map = map5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(four, e.getKey());
+        map.put(five, "E");
+        e = map.pollLastEntry();
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(three, e.getKey());
+        map.remove(two);
+        e = map.pollLastEntry();
+        assertEquals(one, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testSize() {
+        NavigableMap map = map5();
+        NavigableMap empty = map0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testToString() {
+        NavigableMap map = map5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception tests
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testGet_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * containsKey(null) of nonempty map throws NPE
+     */
+    public void testContainsKey_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.containsKey(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testPut1_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * remove(null) throws NPE
+     */
+    public void testRemove1_NullPointerException() {
+        NavigableMap c = map5();
+        try {
+            c.remove(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testSerialization() throws Exception {
+        NavigableMap x = map5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testSubMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.subMap(two, four);
+        assertEquals(two, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.firstKey());
+        assertEquals(three, sm.lastKey());
+        assertEquals("C", sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testSubMapContents2() {
+        NavigableMap map = map5();
+        SortedMap sm = map.subMap(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.firstKey());
+        assertEquals(two, sm.lastKey());
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertFalse(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(two));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(three), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testHeadMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.headMap(four);
+        assertTrue(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertFalse(sm.containsKey(four));
+        assertFalse(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(four, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testTailMapContents() {
+        NavigableMap map = map5();
+        SortedMap sm = map.tailMap(two);
+        assertFalse(sm.containsKey(one));
+        assertTrue(sm.containsKey(two));
+        assertTrue(sm.containsKey(three));
+        assertTrue(sm.containsKey(four));
+        assertTrue(sm.containsKey(five));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(two, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(three, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(four, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(five, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(four);
+        assertEquals(four, ssm.firstKey());
+        assertEquals(five, ssm.lastKey());
+        assertEquals("D", ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * clear removes all pairs
+     */
+    public void testDescendingClear() {
+        NavigableMap map = dmap5();
+        map.clear();
+        assertEquals(0, map.size());
+    }
+
+    /**
+     * Maps with same contents are equal
+     */
+    public void testDescendingEquals() {
+        NavigableMap map1 = dmap5();
+        NavigableMap map2 = dmap5();
+        assertEquals(map1, map2);
+        assertEquals(map2, map1);
+        map1.clear();
+        assertFalse(map1.equals(map2));
+        assertFalse(map2.equals(map1));
+    }
+
+    /**
+     * containsKey returns true for contained key
+     */
+    public void testDescendingContainsKey() {
+        NavigableMap map = dmap5();
+        assertTrue(map.containsKey(m1));
+        assertFalse(map.containsKey(zero));
+    }
+
+    /**
+     * containsValue returns true for held values
+     */
+    public void testDescendingContainsValue() {
+        NavigableMap map = dmap5();
+        assertTrue(map.containsValue("A"));
+        assertFalse(map.containsValue("Z"));
+    }
+
+    /**
+     * get returns the correct element at the given key,
+     * or null if not present
+     */
+    public void testDescendingGet() {
+        NavigableMap map = dmap5();
+        assertEquals("A", (String)map.get(m1));
+        NavigableMap empty = dmap0();
+        assertNull(empty.get(m1));
+    }
+
+    /**
+     * isEmpty is true of empty map and false for non-empty
+     */
+    public void testDescendingIsEmpty() {
+        NavigableMap empty = dmap0();
+        NavigableMap map = dmap5();
+        assertTrue(empty.isEmpty());
+        assertFalse(map.isEmpty());
+    }
+
+    /**
+     * firstKey returns first key
+     */
+    public void testDescendingFirstKey() {
+        NavigableMap map = dmap5();
+        assertEquals(m1, map.firstKey());
+    }
+
+    /**
+     * lastKey returns last key
+     */
+    public void testDescendingLastKey() {
+        NavigableMap map = dmap5();
+        assertEquals(m5, map.lastKey());
+    }
+
+    /**
+     * keySet returns a Set containing all the keys
+     */
+    public void testDescendingKeySet() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        assertEquals(5, s.size());
+        assertTrue(s.contains(m1));
+        assertTrue(s.contains(m2));
+        assertTrue(s.contains(m3));
+        assertTrue(s.contains(m4));
+        assertTrue(s.contains(m5));
+    }
+
+    /**
+     * keySet is ordered
+     */
+    public void testDescendingKeySetOrder() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        Iterator i = s.iterator();
+        Integer last = (Integer)i.next();
+        assertEquals(last, m1);
+        while (i.hasNext()) {
+            Integer k = (Integer)i.next();
+            assertTrue(last.compareTo(k) > 0);
+            last = k;
+        }
+    }
+
+    /**
+     * values collection contains all values
+     */
+    public void testDescendingValues() {
+        NavigableMap map = dmap5();
+        Collection s = map.values();
+        assertEquals(5, s.size());
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * keySet.toArray returns contains all keys
+     */
+    public void testDescendingAscendingKeySetToArray() {
+        NavigableMap map = dmap5();
+        Set s = map.keySet();
+        Object[] ar = s.toArray();
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        assertEquals(5, ar.length);
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * descendingkeySet.toArray returns contains all keys
+     */
+    public void testDescendingDescendingKeySetToArray() {
+        NavigableMap map = dmap5();
+        Set s = map.descendingKeySet();
+        Object[] ar = s.toArray();
+        assertEquals(5, ar.length);
+        assertTrue(s.containsAll(Arrays.asList(ar)));
+        ar[0] = m10;
+        assertFalse(s.containsAll(Arrays.asList(ar)));
+    }
+
+    /**
+     * Values.toArray contains all values
+     */
+    public void testDescendingValuesToArray() {
+        NavigableMap map = dmap5();
+        Collection v = map.values();
+        Object[] ar = v.toArray();
+        ArrayList s = new ArrayList(Arrays.asList(ar));
+        assertEquals(5, ar.length);
+        assertTrue(s.contains("A"));
+        assertTrue(s.contains("B"));
+        assertTrue(s.contains("C"));
+        assertTrue(s.contains("D"));
+        assertTrue(s.contains("E"));
+    }
+
+    /**
+     * entrySet contains all pairs
+     */
+    public void testDescendingEntrySet() {
+        NavigableMap map = dmap5();
+        Set s = map.entrySet();
+        assertEquals(5, s.size());
+        Iterator it = s.iterator();
+        while (it.hasNext()) {
+            Map.Entry e = (Map.Entry) it.next();
+            assertTrue(
+                       (e.getKey().equals(m1) && e.getValue().equals("A")) ||
+                       (e.getKey().equals(m2) && e.getValue().equals("B")) ||
+                       (e.getKey().equals(m3) && e.getValue().equals("C")) ||
+                       (e.getKey().equals(m4) && e.getValue().equals("D")) ||
+                       (e.getKey().equals(m5) && e.getValue().equals("E")));
+        }
+    }
+
+    /**
+     * putAll adds all key-value pairs from the given map
+     */
+    public void testDescendingPutAll() {
+        NavigableMap empty = dmap0();
+        NavigableMap map = dmap5();
+        empty.putAll(map);
+        assertEquals(5, empty.size());
+        assertTrue(empty.containsKey(m1));
+        assertTrue(empty.containsKey(m2));
+        assertTrue(empty.containsKey(m3));
+        assertTrue(empty.containsKey(m4));
+        assertTrue(empty.containsKey(m5));
+    }
+
+    /**
+     * remove removes the correct key-value pair from the map
+     */
+    public void testDescendingRemove() {
+        NavigableMap map = dmap5();
+        map.remove(m5);
+        assertEquals(4, map.size());
+        assertFalse(map.containsKey(m5));
+    }
+
+    /**
+     * lowerEntry returns preceding entry.
+     */
+    public void testDescendingLowerEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.lowerEntry(m3);
+        assertEquals(m2, e1.getKey());
+
+        Map.Entry e2 = map.lowerEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.lowerEntry(m1);
+        assertNull(e3);
+
+        Map.Entry e4 = map.lowerEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higherEntry returns next entry.
+     */
+    public void testDescendingHigherEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.higherEntry(m3);
+        assertEquals(m4, e1.getKey());
+
+        Map.Entry e2 = map.higherEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.higherEntry(m5);
+        assertNull(e3);
+
+        Map.Entry e4 = map.higherEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floorEntry returns preceding entry.
+     */
+    public void testDescendingFloorEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.floorEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.floorEntry(m6);
+        assertEquals(m5, e2.getKey());
+
+        Map.Entry e3 = map.floorEntry(m1);
+        assertEquals(m1, e3.getKey());
+
+        Map.Entry e4 = map.floorEntry(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceilingEntry returns next entry.
+     */
+    public void testDescendingCeilingEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e1 = map.ceilingEntry(m3);
+        assertEquals(m3, e1.getKey());
+
+        Map.Entry e2 = map.ceilingEntry(zero);
+        assertEquals(m1, e2.getKey());
+
+        Map.Entry e3 = map.ceilingEntry(m5);
+        assertEquals(m5, e3.getKey());
+
+        Map.Entry e4 = map.ceilingEntry(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * pollFirstEntry returns entries in order
+     */
+    public void testDescendingPollFirstEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m2, e.getKey());
+        map.put(m1, "A");
+        e = map.pollFirstEntry();
+        assertEquals(m1, e.getKey());
+        assertEquals("A", e.getValue());
+        e = map.pollFirstEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m4);
+        e = map.pollFirstEntry();
+        assertEquals(m5, e.getKey());
+        try {
+            e.setValue("A");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollFirstEntry();
+        assertNull(e);
+    }
+
+    /**
+     * pollLastEntry returns entries in order
+     */
+    public void testDescendingPollLastEntry() {
+        NavigableMap map = dmap5();
+        Map.Entry e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m4, e.getKey());
+        map.put(m5, "E");
+        e = map.pollLastEntry();
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        e = map.pollLastEntry();
+        assertEquals(m3, e.getKey());
+        map.remove(m2);
+        e = map.pollLastEntry();
+        assertEquals(m1, e.getKey());
+        try {
+            e.setValue("E");
+            shouldThrow();
+        } catch (UnsupportedOperationException success) {}
+        e = map.pollLastEntry();
+        assertNull(e);
+    }
+
+    /**
+     * size returns the correct values
+     */
+    public void testDescendingSize() {
+        NavigableMap map = dmap5();
+        NavigableMap empty = dmap0();
+        assertEquals(0, empty.size());
+        assertEquals(5, map.size());
+    }
+
+    /**
+     * toString contains toString of elements
+     */
+    public void testDescendingToString() {
+        NavigableMap map = dmap5();
+        String s = map.toString();
+        for (int i = 1; i <= 5; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    // Exception testDescendings
+
+    /**
+     * get(null) of nonempty map throws NPE
+     */
+    public void testDescendingGet_NullPointerException() {
+        NavigableMap c = dmap5();
+        try {
+            c.get(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * put(null,x) throws NPE
+     */
+    public void testDescendingPut1_NullPointerException() {
+        NavigableMap c = dmap5();
+        try {
+            c.put(null, "whatever");
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * A deserialized map equals original
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableMap x = dmap5();
+        NavigableMap y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+    /**
+     * subMap returns map with keys in requested range
+     */
+    public void testDescendingSubMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m4);
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals(2, sm.size());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.firstKey());
+        assertEquals(m3, sm.lastKey());
+        assertEquals("C", sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, map.size());
+    }
+
+    public void testDescendingSubMapContents2() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.subMap(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.firstKey());
+        assertEquals(m2, sm.lastKey());
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertFalse(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.keySet().iterator();
+        j.next();
+        j.remove();
+        assertFalse(map.containsKey(m2));
+        assertEquals(4, map.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertSame(sm.remove(m3), null);
+        assertEquals(4, map.size());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingHeadMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.headMap(m4);
+        assertTrue(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertFalse(sm.containsKey(m4));
+        assertFalse(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, map.size());
+        assertEquals(m4, map.firstKey());
+    }
+
+    /**
+     * headMap returns map with keys in requested range
+     */
+    public void testDescendingTailMapContents() {
+        NavigableMap map = dmap5();
+        SortedMap sm = map.tailMap(m2);
+        assertFalse(sm.containsKey(m1));
+        assertTrue(sm.containsKey(m2));
+        assertTrue(sm.containsKey(m3));
+        assertTrue(sm.containsKey(m4));
+        assertTrue(sm.containsKey(m5));
+        Iterator i = sm.keySet().iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        Iterator ei = sm.entrySet().iterator();
+        Map.Entry e;
+        e = (Map.Entry)(ei.next());
+        assertEquals(m2, e.getKey());
+        assertEquals("B", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m3, e.getKey());
+        assertEquals("C", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m4, e.getKey());
+        assertEquals("D", e.getValue());
+        e = (Map.Entry)(ei.next());
+        assertEquals(m5, e.getKey());
+        assertEquals("E", e.getValue());
+        assertFalse(i.hasNext());
+
+        SortedMap ssm = sm.tailMap(m4);
+        assertEquals(m4, ssm.firstKey());
+        assertEquals(m5, ssm.lastKey());
+        assertEquals("D", ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, map.size());
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/TreeSubSetTest.java b/ojluni/src/test/java/util/concurrent/tck/TreeSubSetTest.java
new file mode 100644
index 0000000..8b4dddf
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/TreeSubSetTest.java
@@ -0,0 +1,1140 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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 test.java.util.concurrent.tck;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TreeSubSetTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+    public static Test suite() {
+        return new TestSuite(TreeSubSetTest.class);
+    }
+
+    static class MyReverseComparator implements Comparator {
+        public int compare(Object x, Object y) {
+            return ((Comparable)y).compareTo(x);
+        }
+    }
+
+    /**
+     * Returns a new set of given size containing consecutive
+     * Integers 0 ... n - 1.
+     */
+    private NavigableSet<Integer> populatedSet(int n) {
+        TreeSet<Integer> q = new TreeSet<>();
+        assertTrue(q.isEmpty());
+
+        for (int i = n - 1; i >= 0; i -= 2)
+            assertTrue(q.add(new Integer(i)));
+        for (int i = (n & 1); i < n; i += 2)
+            assertTrue(q.add(new Integer(i)));
+        assertTrue(q.add(new Integer(-n)));
+        assertTrue(q.add(new Integer(n)));
+        NavigableSet s = q.subSet(new Integer(0), true, new Integer(n), false);
+        assertFalse(s.isEmpty());
+        assertEquals(n, s.size());
+        return s;
+    }
+
+    /**
+     * Returns a new set of first 5 ints.
+     */
+    private NavigableSet set5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(one);
+        q.add(two);
+        q.add(three);
+        q.add(four);
+        q.add(five);
+        q.add(zero);
+        q.add(seven);
+        NavigableSet s = q.subSet(one, true, seven, false);
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private NavigableSet dset5() {
+        TreeSet q = new TreeSet();
+        assertTrue(q.isEmpty());
+        q.add(m1);
+        q.add(m2);
+        q.add(m3);
+        q.add(m4);
+        q.add(m5);
+        NavigableSet s = q.descendingSet();
+        assertEquals(5, s.size());
+        return s;
+    }
+
+    private static NavigableSet set0() {
+        TreeSet set = new TreeSet();
+        assertTrue(set.isEmpty());
+        return set.tailSet(m1, false);
+    }
+
+    private static NavigableSet dset0() {
+        TreeSet set = new TreeSet();
+        assertTrue(set.isEmpty());
+        return set;
+    }
+
+    /**
+     * A new set has unbounded capacity
+     */
+    public void testConstructor1() {
+        assertEquals(0, set0().size());
+    }
+
+    /**
+     * isEmpty is true before add, false after
+     */
+    public void testEmpty() {
+        NavigableSet q = set0();
+        assertTrue(q.isEmpty());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        assertTrue(q.add(new Integer(2)));
+        q.pollFirst();
+        q.pollFirst();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * add(null) throws NPE
+     */
+    public void testAddNull() {
+        NavigableSet q = set0();
+        try {
+            q.add(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testAdd() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testAddDup() {
+        NavigableSet q = set0();
+        assertTrue(q.add(six));
+        assertFalse(q.add(six));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testAddNonComparable() {
+        NavigableSet q = set0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testAddAll1() {
+        NavigableSet q = set0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testAddAll2() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testAddAll3() {
+        NavigableSet q = set0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = set0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertTrue(q.contains(i - 1));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.contains(i));
+            assertTrue(q.remove(i));
+            assertFalse(q.contains(i));
+            assertFalse(q.remove(i + 1));
+            assertFalse(q.contains(i + 1));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = set0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testLower() {
+        NavigableSet q = set5();
+        Object e1 = q.lower(three);
+        assertEquals(two, e1);
+
+        Object e2 = q.lower(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.lower(one);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testHigher() {
+        NavigableSet q = set5();
+        Object e1 = q.higher(three);
+        assertEquals(four, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.higher(five);
+        assertNull(e3);
+
+        Object e4 = q.higher(six);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testFloor() {
+        NavigableSet q = set5();
+        Object e1 = q.floor(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.floor(six);
+        assertEquals(five, e2);
+
+        Object e3 = q.floor(one);
+        assertEquals(one, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testCeiling() {
+        NavigableSet q = set5();
+        Object e1 = q.ceiling(three);
+        assertEquals(three, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(one, e2);
+
+        Object e3 = q.ceiling(five);
+        assertEquals(five, e3);
+
+        Object e4 = q.ceiling(six);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements in sorted order
+     */
+    public void testToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        for (int i = 0; i < o.length; i++)
+            assertSame(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements in sorted order
+     */
+    public void testToArray2() {
+        NavigableSet<Integer> q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        Integer[] array = q.toArray(ints);
+        assertSame(ints, array);
+        for (int i = 0; i < ints.length; i++)
+            assertSame(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        Iterator it = q.iterator();
+        int i;
+        for (i = 0; it.hasNext(); i++)
+            assertTrue(q.contains(it.next()));
+        assertEquals(i, SIZE);
+        assertIteratorExhausted(it);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testEmptyIterator() {
+        assertIteratorExhausted(set0().iterator());
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testIteratorRemove() {
+        final NavigableSet q = set0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testSerialization() throws Exception {
+        NavigableSet x = populatedSet(SIZE);
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testSubSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, four);
+        assertEquals(two, sm.first());
+        assertEquals(three, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(three, sm.first());
+        assertEquals(three, sm.last());
+        assertTrue(sm.remove(three));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testSubSetContents2() {
+        NavigableSet set = set5();
+        SortedSet sm = set.subSet(two, three);
+        assertEquals(1, sm.size());
+        assertEquals(two, sm.first());
+        assertEquals(two, sm.last());
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertFalse(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(two));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(three));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testHeadSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.headSet(four);
+        assertTrue(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertFalse(sm.contains(four));
+        assertFalse(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(one, k);
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(four, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testTailSetContents() {
+        NavigableSet set = set5();
+        SortedSet sm = set.tailSet(two);
+        assertFalse(sm.contains(one));
+        assertTrue(sm.contains(two));
+        assertTrue(sm.contains(three));
+        assertTrue(sm.contains(four));
+        assertTrue(sm.contains(five));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(two, k);
+        k = (Integer)(i.next());
+        assertEquals(three, k);
+        k = (Integer)(i.next());
+        assertEquals(four, k);
+        k = (Integer)(i.next());
+        assertEquals(five, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(four);
+        assertEquals(four, ssm.first());
+        assertEquals(five, ssm.last());
+        assertTrue(ssm.remove(four));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * size changes when elements added and removed
+     */
+    public void testDescendingSize() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(SIZE - i, q.size());
+            q.pollFirst();
+        }
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.size());
+            q.add(new Integer(i));
+        }
+    }
+
+    /**
+     * Add of comparable element succeeds
+     */
+    public void testDescendingAdd() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+    }
+
+    /**
+     * Add of duplicate element fails
+     */
+    public void testDescendingAddDup() {
+        NavigableSet q = dset0();
+        assertTrue(q.add(m6));
+        assertFalse(q.add(m6));
+    }
+
+    /**
+     * Add of non-Comparable throws CCE
+     */
+    public void testDescendingAddNonComparable() {
+        NavigableSet q = dset0();
+        try {
+            q.add(new Object());
+            q.add(new Object());
+            shouldThrow();
+        } catch (ClassCastException success) {}
+    }
+
+    /**
+     * addAll(null) throws NPE
+     */
+    public void testDescendingAddAll1() {
+        NavigableSet q = dset0();
+        try {
+            q.addAll(null);
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with null elements throws NPE
+     */
+    public void testDescendingAddAll2() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * addAll of a collection with any null elements throws NPE after
+     * possibly adding some elements
+     */
+    public void testDescendingAddAll3() {
+        NavigableSet q = dset0();
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE - 1; ++i)
+            ints[i] = new Integer(i + SIZE);
+        try {
+            q.addAll(Arrays.asList(ints));
+            shouldThrow();
+        } catch (NullPointerException success) {}
+    }
+
+    /**
+     * Set contains all elements of successful addAll
+     */
+    public void testDescendingAddAll5() {
+        Integer[] empty = new Integer[0];
+        Integer[] ints = new Integer[SIZE];
+        for (int i = 0; i < SIZE; ++i)
+            ints[i] = new Integer(SIZE - 1 - i);
+        NavigableSet q = dset0();
+        assertFalse(q.addAll(Arrays.asList(empty)));
+        assertTrue(q.addAll(Arrays.asList(ints)));
+        for (int i = 0; i < SIZE; ++i)
+            assertEquals(new Integer(i), q.pollFirst());
+    }
+
+    /**
+     * poll succeeds unless empty
+     */
+    public void testDescendingPoll() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertEquals(i, q.pollFirst());
+        }
+        assertNull(q.pollFirst());
+    }
+
+    /**
+     * remove(x) removes x and returns true if present
+     */
+    public void testDescendingRemoveElement() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 1; i < SIZE; i += 2) {
+            assertTrue(q.remove(new Integer(i)));
+        }
+        for (int i = 0; i < SIZE; i += 2) {
+            assertTrue(q.remove(new Integer(i)));
+            assertFalse(q.remove(new Integer(i + 1)));
+        }
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * contains(x) reports true when elements added but not yet removed
+     */
+    public void testDescendingContains() {
+        NavigableSet q = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.contains(new Integer(i)));
+            q.pollFirst();
+            assertFalse(q.contains(new Integer(i)));
+        }
+    }
+
+    /**
+     * clear removes all elements
+     */
+    public void testDescendingClear() {
+        NavigableSet q = populatedSet(SIZE);
+        q.clear();
+        assertTrue(q.isEmpty());
+        assertEquals(0, q.size());
+        assertTrue(q.add(new Integer(1)));
+        assertFalse(q.isEmpty());
+        q.clear();
+        assertTrue(q.isEmpty());
+    }
+
+    /**
+     * containsAll(c) is true when c contains a subset of elements
+     */
+    public void testDescendingContainsAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = dset0();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(q.containsAll(p));
+            assertFalse(p.containsAll(q));
+            p.add(new Integer(i));
+        }
+        assertTrue(p.containsAll(q));
+    }
+
+    /**
+     * retainAll(c) retains only those elements of c and reports true if changed
+     */
+    public void testDescendingRetainAll() {
+        NavigableSet q = populatedSet(SIZE);
+        NavigableSet p = populatedSet(SIZE);
+        for (int i = 0; i < SIZE; ++i) {
+            boolean changed = q.retainAll(p);
+            if (i == 0)
+                assertFalse(changed);
+            else
+                assertTrue(changed);
+
+            assertTrue(q.containsAll(p));
+            assertEquals(SIZE - i, q.size());
+            p.pollFirst();
+        }
+    }
+
+    /**
+     * removeAll(c) removes only those elements of c and reports true if changed
+     */
+    public void testDescendingRemoveAll() {
+        for (int i = 1; i < SIZE; ++i) {
+            NavigableSet q = populatedSet(SIZE);
+            NavigableSet p = populatedSet(i);
+            assertTrue(q.removeAll(p));
+            assertEquals(SIZE - i, q.size());
+            for (int j = 0; j < i; ++j) {
+                Integer x = (Integer)(p.pollFirst());
+                assertFalse(q.contains(x));
+            }
+        }
+    }
+
+    /**
+     * lower returns preceding element
+     */
+    public void testDescendingLower() {
+        NavigableSet q = dset5();
+        Object e1 = q.lower(m3);
+        assertEquals(m2, e1);
+
+        Object e2 = q.lower(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.lower(m1);
+        assertNull(e3);
+
+        Object e4 = q.lower(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * higher returns next element
+     */
+    public void testDescendingHigher() {
+        NavigableSet q = dset5();
+        Object e1 = q.higher(m3);
+        assertEquals(m4, e1);
+
+        Object e2 = q.higher(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.higher(m5);
+        assertNull(e3);
+
+        Object e4 = q.higher(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * floor returns preceding element
+     */
+    public void testDescendingFloor() {
+        NavigableSet q = dset5();
+        Object e1 = q.floor(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.floor(m6);
+        assertEquals(m5, e2);
+
+        Object e3 = q.floor(m1);
+        assertEquals(m1, e3);
+
+        Object e4 = q.floor(zero);
+        assertNull(e4);
+    }
+
+    /**
+     * ceiling returns next element
+     */
+    public void testDescendingCeiling() {
+        NavigableSet q = dset5();
+        Object e1 = q.ceiling(m3);
+        assertEquals(m3, e1);
+
+        Object e2 = q.ceiling(zero);
+        assertEquals(m1, e2);
+
+        Object e3 = q.ceiling(m5);
+        assertEquals(m5, e3);
+
+        Object e4 = q.ceiling(m6);
+        assertNull(e4);
+    }
+
+    /**
+     * toArray contains all elements
+     */
+    public void testDescendingToArray() {
+        NavigableSet q = populatedSet(SIZE);
+        Object[] o = q.toArray();
+        Arrays.sort(o);
+        for (int i = 0; i < o.length; i++)
+            assertEquals(o[i], q.pollFirst());
+    }
+
+    /**
+     * toArray(a) contains all elements
+     */
+    public void testDescendingToArray2() {
+        NavigableSet q = populatedSet(SIZE);
+        Integer[] ints = new Integer[SIZE];
+        assertSame(ints, q.toArray(ints));
+        Arrays.sort(ints);
+        for (int i = 0; i < ints.length; i++)
+            assertEquals(ints[i], q.pollFirst());
+    }
+
+    /**
+     * iterator iterates through all elements
+     */
+    public void testDescendingIterator() {
+        NavigableSet q = populatedSet(SIZE);
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(i, SIZE);
+    }
+
+    /**
+     * iterator of empty set has no elements
+     */
+    public void testDescendingEmptyIterator() {
+        NavigableSet q = dset0();
+        int i = 0;
+        Iterator it = q.iterator();
+        while (it.hasNext()) {
+            assertTrue(q.contains(it.next()));
+            ++i;
+        }
+        assertEquals(0, i);
+    }
+
+    /**
+     * iterator.remove removes current element
+     */
+    public void testDescendingIteratorRemove() {
+        final NavigableSet q = dset0();
+        q.add(new Integer(2));
+        q.add(new Integer(1));
+        q.add(new Integer(3));
+
+        Iterator it = q.iterator();
+        it.next();
+        it.remove();
+
+        it = q.iterator();
+        assertEquals(2, it.next());
+        assertEquals(3, it.next());
+        assertFalse(it.hasNext());
+    }
+
+    /**
+     * toString contains toStrings of elements
+     */
+    public void testDescendingToString() {
+        NavigableSet q = populatedSet(SIZE);
+        String s = q.toString();
+        for (int i = 0; i < SIZE; ++i) {
+            assertTrue(s.contains(String.valueOf(i)));
+        }
+    }
+
+    /**
+     * A deserialized serialized set has same elements
+     */
+    public void testDescendingSerialization() throws Exception {
+        NavigableSet x = dset5();
+        NavigableSet y = serialClone(x);
+
+        assertNotSame(x, y);
+        assertEquals(x.size(), y.size());
+        assertEquals(x.toString(), y.toString());
+        assertEquals(x, y);
+        assertEquals(y, x);
+        while (!x.isEmpty()) {
+            assertFalse(y.isEmpty());
+            assertEquals(x.pollFirst(), y.pollFirst());
+        }
+        assertTrue(y.isEmpty());
+    }
+
+    /**
+     * subSet returns set with keys in requested range
+     */
+    public void testDescendingSubSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m4);
+        assertEquals(m2, sm.first());
+        assertEquals(m3, sm.last());
+        assertEquals(2, sm.size());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(1, sm.size());
+        assertEquals(m3, sm.first());
+        assertEquals(m3, sm.last());
+        assertTrue(sm.remove(m3));
+        assertTrue(sm.isEmpty());
+        assertEquals(3, set.size());
+    }
+
+    public void testDescendingSubSetContents2() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.subSet(m2, m3);
+        assertEquals(1, sm.size());
+        assertEquals(m2, sm.first());
+        assertEquals(m2, sm.last());
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertFalse(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        assertFalse(i.hasNext());
+        Iterator j = sm.iterator();
+        j.next();
+        j.remove();
+        assertFalse(set.contains(m2));
+        assertEquals(4, set.size());
+        assertEquals(0, sm.size());
+        assertTrue(sm.isEmpty());
+        assertFalse(sm.remove(m3));
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * headSet returns set with keys in requested range
+     */
+    public void testDescendingHeadSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.headSet(m4);
+        assertTrue(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertFalse(sm.contains(m4));
+        assertFalse(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m1, k);
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        assertFalse(i.hasNext());
+        sm.clear();
+        assertTrue(sm.isEmpty());
+        assertEquals(2, set.size());
+        assertEquals(m4, set.first());
+    }
+
+    /**
+     * tailSet returns set with keys in requested range
+     */
+    public void testDescendingTailSetContents() {
+        NavigableSet set = dset5();
+        SortedSet sm = set.tailSet(m2);
+        assertFalse(sm.contains(m1));
+        assertTrue(sm.contains(m2));
+        assertTrue(sm.contains(m3));
+        assertTrue(sm.contains(m4));
+        assertTrue(sm.contains(m5));
+        Iterator i = sm.iterator();
+        Object k;
+        k = (Integer)(i.next());
+        assertEquals(m2, k);
+        k = (Integer)(i.next());
+        assertEquals(m3, k);
+        k = (Integer)(i.next());
+        assertEquals(m4, k);
+        k = (Integer)(i.next());
+        assertEquals(m5, k);
+        assertFalse(i.hasNext());
+
+        SortedSet ssm = sm.tailSet(m4);
+        assertEquals(m4, ssm.first());
+        assertEquals(m5, ssm.last());
+        assertTrue(ssm.remove(m4));
+        assertEquals(1, ssm.size());
+        assertEquals(3, sm.size());
+        assertEquals(4, set.size());
+    }
+
+    /**
+     * addAll is idempotent
+     */
+    public void testAddAll_idempotent() throws Exception {
+        Set x = populatedSet(SIZE);
+        Set y = new TreeSet(x);
+        y.addAll(x);
+        assertEquals(x, y);
+        assertEquals(y, x);
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/VectorTest.java b/ojluni/src/test/java/util/concurrent/tck/VectorTest.java
new file mode 100644
index 0000000..4100843
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/VectorTest.java
@@ -0,0 +1,86 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * 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/publicdomain/zero/1.0/
+ */
+
+package test.java.util.concurrent.tck;
+import java.util.Vector;
+import java.util.Collection;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class VectorTest extends JSR166TestCase {
+    public static void main(String[] args) {
+        main(suite(), args);
+    }
+
+    public static Test suite() {
+        class Implementation implements CollectionImplementation {
+            public Class<?> klazz() { return Vector.class; }
+            public List emptyCollection() { return new Vector(); }
+            public Object makeElement(int i) { return i; }
+            public boolean isConcurrent() { return false; }
+            public boolean permitsNulls() { return true; }
+        }
+        class SubListImplementation extends Implementation {
+            public List emptyCollection() {
+                return super.emptyCollection().subList(0, 0);
+            }
+        }
+        return newTestSuite(
+                VectorTest.class,
+                CollectionTest.testSuite(new Implementation()),
+                CollectionTest.testSuite(new SubListImplementation()));
+    }
+
+    /**
+     * tests for setSize()
+     */
+    public void testSetSize() {
+        final Vector v = new Vector();
+        for (int n : new int[] { 100, 5, 50 }) {
+            v.setSize(n);
+            assertEquals(n, v.size());
+            assertNull(v.get(0));
+            assertNull(v.get(n - 1));
+            assertThrows(
+                    ArrayIndexOutOfBoundsException.class,
+                    new Runnable() { public void run() { v.setSize(-1); }});
+            assertEquals(n, v.size());
+            assertNull(v.get(0));
+            assertNull(v.get(n - 1));
+        }
+    }
+
+}
diff --git a/ojluni/src/test/java/util/concurrent/tck/tck.policy b/ojluni/src/test/java/util/concurrent/tck/tck.policy
new file mode 100644
index 0000000..5816ab3
--- /dev/null
+++ b/ojluni/src/test/java/util/concurrent/tck/tck.policy
@@ -0,0 +1,15 @@
+grant {
+    // Permissions j.u.c. needs directly
+    permission java.lang.RuntimePermission "modifyThread";
+    permission java.lang.RuntimePermission "getClassLoader";
+    permission java.lang.RuntimePermission "setContextClassLoader";
+    permission java.util.PropertyPermission "*", "read";
+    // Permissions needed to change permissions!
+    permission java.security.SecurityPermission "getPolicy";
+    permission java.security.SecurityPermission "setPolicy";
+    permission java.security.SecurityPermission "setSecurityManager";
+    // Permissions needed by the junit test harness
+    permission java.lang.RuntimePermission "accessDeclaredMembers";
+    permission java.io.FilePermission "<<ALL FILES>>", "read";
+    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+};
diff --git a/ojluni/src/test/javax/xml/jaxp/transform/SecureProcessingTest.xml b/ojluni/src/test/javax/xml/jaxp/transform/SecureProcessingTest.xml
new file mode 100644
index 0000000..bc916a9
--- /dev/null
+++ b/ojluni/src/test/javax/xml/jaxp/transform/SecureProcessingTest.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<helloWorld/>
diff --git a/ojluni/src/test/javax/xml/jaxp/transform/TestBase.java b/ojluni/src/test/javax/xml/jaxp/transform/TestBase.java
new file mode 100644
index 0000000..77fdccb
--- /dev/null
+++ b/ojluni/src/test/javax/xml/jaxp/transform/TestBase.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+// Android-changed: Added package
+package test.javax.xml.jaxp.transform;
+
+import java.security.Policy;
+
+/**
+ *
+ *
+ * @author huizhe.wang@oracle.com
+ */
+public class TestBase {
+    public static boolean isWindows = false;
+    static {
+        if (System.getProperty("os.name").indexOf("Windows")>-1) {
+            isWindows = true;
+        }
+    };
+
+    String filepath;
+    boolean hasSM;
+    String curDir;
+    Policy origPolicy;
+    String testName;
+    static String errMessage;
+
+    int passed = 0, failed = 0;
+
+    /**
+     * Creates a new instance of StreamReader
+     */
+    public TestBase(String name) {
+        testName = name;
+    }
+
+    //junit @Override
+    protected void setUp() {
+        if (System.getSecurityManager() != null) {
+            hasSM = true;
+            System.setSecurityManager(null);
+        }
+
+        filepath = System.getProperty("test.src");
+        if (filepath == null) {
+            //current directory
+            filepath = System.getProperty("user.dir");
+        }
+        origPolicy = Policy.getPolicy();
+
+    }
+
+    //junit @Override
+    public void tearDown() {
+        // turn off security manager and restore policy
+        System.setSecurityManager(null);
+        Policy.setPolicy(origPolicy);
+        if (hasSM) {
+            System.setSecurityManager(new SecurityManager());
+        }
+        System.out.println("\nNumber of tests passed: " + passed);
+        System.out.println("Number of tests failed: " + failed + "\n");
+
+        if (errMessage != null ) {
+            throw new RuntimeException(errMessage);
+        }
+    }
+
+    void fail(String errMsg) {
+        if (errMessage == null) {
+            errMessage = errMsg;
+        } else {
+            errMessage = errMessage + "\n" + errMsg;
+        }
+        failed++;
+    }
+
+    void success(String msg) {
+        passed++;
+        System.out.println(msg);
+    }
+
+}
diff --git a/ojluni/src/test/javax/xml/jaxp/transform/XPathExFuncTest.java b/ojluni/src/test/javax/xml/jaxp/transform/XPathExFuncTest.java
new file mode 100644
index 0000000..637fb8e
--- /dev/null
+++ b/ojluni/src/test/javax/xml/jaxp/transform/XPathExFuncTest.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/**
+ * @test
+ * @bug 8004476
+ * @summary test XPath extension functions
+ * @run main/othervm XPathExFuncTest
+ */
+// Android-changed: Added package & Test import
+package test.javax.xml.jaxp.transform;
+import org.testng.annotations.Test;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathFactoryConfigurationException;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import javax.xml.xpath.XPathFunctionResolver;
+import org.w3c.dom.Document;
+
+/**
+ * test XPath extension functions
+ *
+ * @author huizhe.wang@oracle.com
+ */
+public class XPathExFuncTest extends TestBase {
+
+    final static String ENABLE_EXTENSION_FUNCTIONS = "http://www.oracle.com/xml/jaxp/properties/enableExtensionFunctions";
+    final static String CLASSNAME = "DocumentBuilderFactoryImpl";
+    final String XPATH_EXPRESSION = "ext:helloWorld()";
+
+    /**
+     * Creates a new instance of StreamReader
+     */
+    public XPathExFuncTest(String name) {
+        super(name);
+    }
+    boolean hasSM;
+    // Android-changed: Load the files from java resources.
+    // String xslFile, xslFileId;
+    // String xmlFile, xmlFileId;
+    InputStream xmlStream;
+
+    protected void setUp() {
+        super.setUp();
+        // Android-changed: Load the files from java resources.
+        // xmlFile = filepath + "/SecureProcessingTest.xml";
+        xmlStream = XPathExFuncTest.class.getResourceAsStream("SecureProcessingTest.xml");
+
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        XPathExFuncTest test = new XPathExFuncTest("OneTest");
+        test.setUp();
+
+        test.testExtFunc();
+        // Android-removed: Android doesn't allow setting a SecurityManager.
+        // test.testExtFuncNotAllowed();
+        // Android-added: Call setUp() before each test
+        test.setUp();
+        test.testEnableExtFunc();
+        test.tearDown();
+
+    }
+
+    /**
+     * by default, extension function is enabled
+     */
+    public void testExtFunc() {
+
+        try {
+            evaluate(false);
+            System.out.println("testExtFunc: OK");
+        } catch (XPathFactoryConfigurationException e) {
+            fail(e.getMessage());
+        } catch (XPathExpressionException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    /**
+     * Security is enabled, extension function not allowed
+     */
+    public void testExtFuncNotAllowed() {
+         Policy p = new SimplePolicy(new AllPermission());
+         Policy.setPolicy(p);
+         System.setSecurityManager(new SecurityManager());
+
+        try {
+            evaluate(false);
+        } catch (XPathFactoryConfigurationException e) {
+            fail(e.getMessage());
+        } catch (XPathExpressionException ex) {
+            //expected since extension function is disallowed
+            System.out.println("testExtFuncNotAllowed: OK");
+        } finally {
+            System.setSecurityManager(null);
+        }
+    }
+
+    /**
+     * Security is enabled, use new feature: enableExtensionFunctions
+     */
+    public void testEnableExtFunc() {
+        // Android-removed: Android doesn't use SecurityManager.
+        // Policy p = new SimplePolicy(new AllPermission());
+        // Policy.setPolicy(p);
+        // System.setSecurityManager(new SecurityManager());
+
+
+        try {
+            evaluate(true);
+            System.out.println("testEnableExt: OK");
+        } catch (XPathFactoryConfigurationException e) {
+            fail(e.getMessage());
+        } catch (XPathExpressionException e) {
+            fail(e.getMessage());
+        } finally {
+            System.setSecurityManager(null);
+        }
+    }
+
+    Document getDocument() {
+        // the xml source
+        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder documentBuilder = null;
+        Document document = null;
+
+        try {
+            documentBuilder = documentBuilderFactory.newDocumentBuilder();
+            // Android-changed: Load the xml file from java resources.
+            // InputStream xmlStream = new FileInputStream(xmlFile);
+            InputStream xmlStream = this.xmlStream; // new FileInputStream(xmlFile);
+            document = documentBuilder.parse(xmlStream);
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+        return document;
+    }
+
+    void evaluate(boolean enableExt) throws XPathFactoryConfigurationException, XPathExpressionException {
+        Document document = getDocument();
+
+        XPathFactory xPathFactory = XPathFactory.newInstance();
+        /**
+         * Use of the extension function 'http://exslt.org/strings:tokenize' is
+         * not allowed when the secure processing feature is set to true.
+         * Attempt to use the new property to enable extension function
+         */
+        if (enableExt) {
+            boolean isExtensionSupported = enableExtensionFunction(xPathFactory);
+        }
+
+        xPathFactory.setXPathFunctionResolver(new MyXPathFunctionResolver());
+        if (System.getSecurityManager() == null) {
+            xPathFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
+        }
+
+        XPath xPath = xPathFactory.newXPath();
+        xPath.setNamespaceContext(new MyNamespaceContext());
+
+        String xPathResult = xPath.evaluate(XPATH_EXPRESSION, document);
+        System.out.println(
+                "XPath result (enableExtensionFunction == " + enableExt + ") = \""
+                + xPathResult
+                + "\"");
+    }
+
+    public class MyXPathFunctionResolver
+            implements XPathFunctionResolver {
+
+        public XPathFunction resolveFunction(QName functionName, int arity) {
+
+            // not a real ewsolver, always return a default XPathFunction
+            return new MyXPathFunction();
+        }
+    }
+
+    public class MyXPathFunction
+            implements XPathFunction {
+
+        public Object evaluate(List list) throws XPathFunctionException {
+
+            return "Hello World";
+        }
+    }
+
+    public class MyNamespaceContext implements NamespaceContext {
+
+        public String getNamespaceURI(String prefix) {
+            if (prefix == null) {
+                throw new IllegalArgumentException("The prefix cannot be null.");
+            }
+
+            if (prefix.equals("ext")) {
+                return "http://ext.com";
+            } else {
+                return null;
+            }
+        }
+
+        public String getPrefix(String namespace) {
+
+            if (namespace == null) {
+                throw new IllegalArgumentException("The namespace uri cannot be null.");
+            }
+
+            if (namespace.equals("http://ext.com")) {
+                return "ext";
+            } else {
+                return null;
+            }
+        }
+
+        public Iterator getPrefixes(String namespace) {
+            return null;
+        }
+    }
+
+    boolean enableExtensionFunction(XPathFactory factory) {
+        boolean isSupported = true;
+        try {
+            factory.setFeature(ENABLE_EXTENSION_FUNCTIONS, true);
+        } catch (XPathFactoryConfigurationException ex) {
+            isSupported = false;
+        }
+        return isSupported;
+    }
+
+    class SimplePolicy extends Policy {
+
+        private final Permissions perms;
+
+        public SimplePolicy(Permission... permissions) {
+            perms = new Permissions();
+            for (Permission permission : permissions) {
+                perms.add(permission);
+            }
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource cs) {
+            return perms;
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain pd) {
+            return perms;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain pd, Permission p) {
+            return perms.implies(p);
+        }
+
+        //for older jdk
+        @Override
+        public void refresh() {
+        }
+    }
+}
diff --git a/ojluni/src/test/javax/xml/jaxp/transform/XSLTExFuncTest.java b/ojluni/src/test/javax/xml/jaxp/transform/XSLTExFuncTest.java
new file mode 100644
index 0000000..90304d2
--- /dev/null
+++ b/ojluni/src/test/javax/xml/jaxp/transform/XSLTExFuncTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/**
+ * @test
+ * @bug 8004476
+ * @summary test XSLT extension functions
+ * @run main/othervm XSLTExFuncTest
+ */
+// Android-changed: Added package & Test import
+package test.javax.xml.jaxp.transform;
+import org.testng.annotations.Test;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import javax.xml.transform.*;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import org.xml.sax.InputSource;
+
+/**
+ * test XSLT extension functions
+ *
+ * @author huizhe.wang@oracle.com
+ */
+public class XSLTExFuncTest extends TestBase {
+
+    final static String ENABLE_EXTENSION_FUNCTIONS = "http://www.oracle.com/xml/jaxp/properties/enableExtensionFunctions";
+    final static String CLASSNAME = "DocumentBuilderFactoryImpl";
+
+    /**
+     * Creates a new instance of StreamReader
+     */
+    public XSLTExFuncTest(String name) {
+        super(name);
+    }
+    boolean hasSM;
+    // Android-changed: Load the files from java resources.
+    // String xslFile, xslFileId;
+    // String xmlFile, xmlFileId;
+    InputStream xslFile, xmlFile;
+    String xslFileId;
+
+    protected void setUp() {
+        super.setUp();
+        // Android-changed: Load the files from java resources.
+        // xmlFile = filepath + "/tokenize.xml";
+        // xslFile = filepath + "/tokenize.xsl";
+        xmlFile = XSLTExFuncTest.class.getResourceAsStream("tokenize.xml");
+        xslFile = XSLTExFuncTest.class.getResourceAsStream("tokenize.xsl");
+
+        /**
+         * On Windows platform it needs triple '/' for valid URL while double '/' is enough on Linux or Solaris.
+         * Here use file:/// directly to make it work on Windows and it will not impact other platforms.
+         */
+        // Android-changed: Load the files from java resources.
+        // xslFileId = "file:///" + xslFile;
+        xslFileId = "file:///" + filepath + "/tokenize.xsl";
+    }
+
+    // Android-changed: Removed args & added @Test
+    @Test
+    public static void main() {
+        XSLTExFuncTest test = new XSLTExFuncTest("OneTest");
+        test.setUp();
+
+        test.testExtFunc();
+        // Android-removed: Android doesn't allow setting a SecurityManager.
+        // test.testExtFuncNotAllowed();
+        // Android-added: Call setUp() before each test
+        test.setUp();
+        test.testEnableExtFunc();
+        test.setUp();
+        test.testTemplatesEnableExtFunc();
+        test.tearDown();
+
+    }
+
+    /**
+     * by default, extension function is enabled
+     */
+    public void testExtFunc() {
+        TransformerFactory factory = TransformerFactory.newInstance();
+
+        try {
+            transform(factory);
+            System.out.println("testExtFunc: OK");
+        } catch (TransformerConfigurationException e) {
+            fail(e.getMessage());
+        } catch (TransformerException ex) {
+            fail(ex.getMessage());
+        }
+    }
+
+    /**
+     * Security is enabled, extension function not allowed
+     */
+    public void testExtFuncNotAllowed() {
+        Policy p = new SimplePolicy(new AllPermission());
+        Policy.setPolicy(p);
+        System.setSecurityManager(new SecurityManager());
+        TransformerFactory factory = TransformerFactory.newInstance();
+
+        try {
+            transform(factory);
+        } catch (TransformerConfigurationException e) {
+            fail(e.getMessage());
+        } catch (TransformerException ex) {
+            //expected since extension function is disallowed
+            System.out.println("testExtFuncNotAllowed: OK");
+        } finally {
+            System.setSecurityManager(null);
+        }
+    }
+
+    /**
+     * Security is enabled, use new feature: enableExtensionFunctions
+     */
+    public void testEnableExtFunc() {
+        // Android-removed: Android doesn't use SecurityManager.
+        // Policy p = new SimplePolicy(new AllPermission());
+        // Policy.setPolicy(p);
+        // System.setSecurityManager(new SecurityManager());
+        TransformerFactory factory = TransformerFactory.newInstance();
+
+        /**
+         * Use of the extension function 'http://exslt.org/strings:tokenize' is
+         * not allowed when the secure processing feature is set to true.
+         * Attempt to use the new property to enable extension function
+         */
+        boolean isExtensionSupported = enableExtensionFunction(factory);
+
+        try {
+            transform(factory);
+            System.out.println("testEnableExt: OK");
+        } catch (TransformerConfigurationException e) {
+            fail(e.getMessage());
+        } catch (TransformerException e) {
+            fail(e.getMessage());
+        } finally {
+            System.setSecurityManager(null);
+        }
+    }
+
+    /**
+     * use Templates template = factory.newTemplates(new StreamSource( new
+     * FileInputStream(xslFilename))); // Use the template to create a
+     * transformer Transformer xformer = template.newTransformer();
+     *
+     * @param factory
+     * @return
+     */
+    /**
+     * Security is enabled, use new feature: enableExtensionFunctions Use the
+     * template to create a transformer
+     */
+    public void testTemplatesEnableExtFunc() {
+        // Android-removed: Android doesn't use SecurityManager.
+        // Policy p = new SimplePolicy(new AllPermission());
+        // Policy.setPolicy(p);
+        // System.setSecurityManager(new SecurityManager());
+        TransformerFactory factory = TransformerFactory.newInstance();
+
+        /**
+         * Use of the extension function 'http://exslt.org/strings:tokenize' is
+         * not allowed when the secure processing feature is set to true.
+         * Attempt to use the new property to enable extension function
+         */
+        boolean isExtensionSupported = enableExtensionFunction(factory);
+
+        try {
+            SAXSource xslSource = new SAXSource(new InputSource(xslFile));
+            xslSource.setSystemId(xslFileId);
+            Templates template = factory.newTemplates(xslSource);
+            Transformer transformer = template.newTransformer();
+            StringWriter stringResult = new StringWriter();
+            Result result = new StreamResult(stringResult);
+            transformer.transform(new SAXSource(new InputSource(xmlFile)), result);
+            System.out.println("testTemplatesEnableExtFunc: OK");
+        } catch (TransformerConfigurationException e) {
+            fail(e.getMessage());
+        } catch (TransformerException e) {
+            fail(e.getMessage());
+        } finally {
+            System.setSecurityManager(null);
+        }
+    }
+
+    boolean enableExtensionFunction(TransformerFactory factory) {
+        boolean isSupported = true;
+        try {
+            factory.setFeature(ENABLE_EXTENSION_FUNCTIONS, true);
+        } catch (TransformerConfigurationException ex) {
+            isSupported = false;
+        }
+        return isSupported;
+    }
+
+    void transform(TransformerFactory factory) throws TransformerConfigurationException, TransformerException {
+        SAXSource xslSource = new SAXSource(new InputSource(xslFile));
+        xslSource.setSystemId(xslFileId);
+        Transformer transformer = factory.newTransformer(xslSource);
+        StringWriter stringResult = new StringWriter();
+        Result result = new StreamResult(stringResult);
+        transformer.transform(new SAXSource(new InputSource(xmlFile)), result);
+    }
+
+    class SimplePolicy extends Policy {
+
+        private final Permissions perms;
+
+        public SimplePolicy(Permission... permissions) {
+            perms = new Permissions();
+            for (Permission permission : permissions) {
+                perms.add(permission);
+            }
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource cs) {
+            return perms;
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain pd) {
+            return perms;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain pd, Permission p) {
+            return perms.implies(p);
+        }
+
+        //for older jdk
+        @Override
+        public void refresh() {
+        }
+    }
+}
diff --git a/ojluni/src/test/javax/xml/jaxp/transform/XslSubstringTest.java b/ojluni/src/test/javax/xml/jaxp/transform/XslSubstringTest.java
new file mode 100644
index 0000000..a95331c
--- /dev/null
+++ b/ojluni/src/test/javax/xml/jaxp/transform/XslSubstringTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8062923 8062924 8074297 8076290
+ * @run testng XslSubstringTest
+ * @summary Test xsl substring function with negative, Inf and
+ * NaN length and few other use cases. Also test proper
+ * processing of supplementary characters by substring function.
+ */
+// Android-changed: Added package
+package test.javax.xml.jaxp.transform;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class XslSubstringTest {
+
+    final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><test></test>";
+    final String xslPre = "<xsl:stylesheet version='1.0'"
+            + " xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>"
+            + "<xsl:output method='xml' indent='yes' omit-xml-declaration='yes'/>"
+            + "<xsl:template match='/'><t>";
+    final String xslPost = "</t></xsl:template></xsl:stylesheet>";
+
+    @DataProvider(name = "GeneralTestsData")
+    private Object[][] xmls() {
+        return new Object[][] {
+            { "|<xsl:value-of select=\"substring('asdf',2, 1)\"/>|", "<t>|s|</t>"},
+            { "|<xsl:value-of select=\"substring('asdf',2, 1 div 0)\"/>|", "<t>|sdf|</t>"},
+            { "|<xsl:value-of select=\"substring('asdf',2, -0 div 0)\"/>|", "<t>||</t>" },
+            { "|<xsl:value-of select=\"substring('asdf',2, 1 div 0)\"/>|", "<t>|sdf|</t>" },
+            // 8076290 bug test case
+            { "|<xsl:value-of select=\"substring('123', 0, 3)\"/>|", "<t>|12|</t>"},
+        };
+    }
+
+    @DataProvider(name = "SupplementaryCharactersTestData")
+    private Object[][] dataSupplementaryCharacters() {
+        return new Object[][] {
+            // 8074297 bug test cases
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 3)\"/>|",    "<t>|BC|</t>"},
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 3, 1)\"/>|", "<t>|B|</t>" },
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 2, 2)\"/>|", "<t>|AB|</t>"},
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 3, 2)\"/>|", "<t>|BC|</t>"},
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 3, 4)\"/>|", "<t>|BC|</t>"},
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 1, 1)\"/>|", "<t>|&#131083;|</t>"},
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 2, 1)\"/>|", "<t>|A|</t>"},
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 1, 1 div 0)\"/>|", "<t>|&#131083;ABC|</t>"},
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', -10, 1 div 0)\"/>|", "<t>|&#131083;ABC|</t>"},
+            // 8076290 bug test case
+            { "|<xsl:value-of select=\"substring('&#131083;ABC', 0, 2)\"/>|", "<t>|&#131083;|</t>"},
+        };
+    }
+
+    private String testTransform(String xsl) throws Exception {
+        //Prepare sources for transormation
+        Source src = new StreamSource(new StringReader(xml));
+        Source xslsrc = new StreamSource(new StringReader(xslPre + xsl + xslPost));
+        //Create factory, template and transformer
+        TransformerFactory tf = TransformerFactory.newInstance();
+        Templates tmpl = tf.newTemplates(xslsrc);
+        Transformer t = tmpl.newTransformer();
+        //Prepare output stream
+        StringWriter xmlResultString = new StringWriter();
+        StreamResult xmlResultStream = new StreamResult(xmlResultString);
+        //Transform
+        t.transform(src, xmlResultStream);
+        return xmlResultString.toString().trim();
+    }
+
+    @Test
+    public void test8062923() throws Exception {
+        assertEquals(testTransform("|<xsl:value-of select=\"substring('asdf',2,-1)\"/>|"),
+                // TODO(b/181173341): Rollback this patch
+                // Android-changed: Change the expected output until JDK-8062923 is fixed on Android
+                //  "<t>||</t>");
+                "<t>|</t>");
+    }
+
+    @Test
+    public void test8062924() throws Exception {
+        assertEquals(testTransform("|<xsl:value-of select=\"substring('asdf',2,-1 div 0)\"/>|"),
+                // TODO(b/181173341): Rollback this patch
+                // Android-changed: Change the expected output until JDK-8062924 is fixed on Android
+                // "<t>||</t>");
+                "<t>|sdf|</t>");
+    }
+
+    @Test(dataProvider = "GeneralTestsData")
+    public void testGeneralAll(String xsl, String result) throws Exception {
+        assertEquals(testTransform(xsl), result);
+    }
+
+    // TODO(b/181173341): Rollback this patch
+    // Android-removed: Disable the test until JDK-8074297 is fixed on Android.
+    /*
+    @Test(dataProvider = "SupplementaryCharactersTestData")
+    public void testSupplementCharacters(String xsl, String result) throws Exception {
+        assertEquals(testTransform(xsl), result);
+    }
+    */
+
+}
diff --git a/ojluni/src/test/javax/xml/jaxp/transform/tokenize.xml b/ojluni/src/test/javax/xml/jaxp/transform/tokenize.xml
new file mode 100644
index 0000000..1f97559
--- /dev/null
+++ b/ojluni/src/test/javax/xml/jaxp/transform/tokenize.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<html>
+<a>
+   <b>Is this EXSLT? No. no</b>
+   <c>Is this EXSLT? No. no</c>
+</a>
+</html>
diff --git a/ojluni/src/test/javax/xml/jaxp/transform/tokenize.xsl b/ojluni/src/test/javax/xml/jaxp/transform/tokenize.xsl
new file mode 100644
index 0000000..d090d3b
--- /dev/null
+++ b/ojluni/src/test/javax/xml/jaxp/transform/tokenize.xsl
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+                xmlns:str="http://exslt.org/strings"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                version="1.0">
+<xsl:template match="a">
+   <xsl:apply-templates />
+</xsl:template>
+<xsl:template match="//a/c">
+   <xsl:value-of select="." />
+ -
+      <xsl:value-of select="str:tokenize(string(.), ' ')" />
+   <xsl:value-of select="str:tokenize(string(.), '')" />
+   <xsl:for-each select="str:tokenize(string(.), ' ')">
+      <xsl:value-of select="." />
+   </xsl:for-each>
+   <xsl:apply-templates select="*" />
+</xsl:template>
+<xsl:template match="//a/b">
+   <xsl:value-of select="." />
+ -
+      <xsl:value-of select="xalan:tokenize(string(.), ' ')" />
+   <xsl:value-of select="xalan:tokenize(string(.), '')" />
+   <xsl:for-each select="xalan:tokenize(string(.), ' ')">
+      <xsl:value-of select="." />
+   </xsl:for-each>
+   <xsl:apply-templates select="*" />
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/openjdk_java_files.bp b/openjdk_java_files.bp
index 7ba860c..a9f1dee 100644
--- a/openjdk_java_files.bp
+++ b/openjdk_java_files.bp
@@ -259,6 +259,13 @@
         "ojluni/src/main/java/java/lang/invoke/VarHandle.java",
         "ojluni/src/main/java/java/lang/invoke/VolatileCallSite.java",
         "ojluni/src/main/java/java/lang/invoke/WrongMethodTypeException.java",
+        "ojluni/src/main/java/java/math/BigDecimal.java",
+        "ojluni/src/main/java/java/math/BigInteger.java",
+        "ojluni/src/main/java/java/math/BitSieve.java",
+        "ojluni/src/main/java/java/math/MathContext.java",
+        "ojluni/src/main/java/java/math/MutableBigInteger.java",
+        "ojluni/src/main/java/java/math/RoundingMode.java",
+        "ojluni/src/main/java/java/math/SignedMutableBigInteger.java",
         "ojluni/src/main/java/java/net/AbstractPlainDatagramSocketImpl.java",
         "ojluni/src/main/java/java/net/AbstractPlainSocketImpl.java",
         "ojluni/src/main/java/java/net/Authenticator.java",
@@ -1418,7 +1425,6 @@
         "ojluni/src/main/java/sun/security/util/DerEncoder.java",
         "ojluni/src/main/java/sun/security/util/ObjectIdentifier.java",
         "ojluni/src/main/java/sun/security/x509/AlgorithmId.java",
-        "ojluni/src/main/java/sun/util/locale/LanguageTag.java",
     ],
 }
 
@@ -1792,6 +1798,7 @@
         "ojluni/src/main/java/sun/util/locale/BaseLocale.java",
         "ojluni/src/main/java/sun/util/locale/Extension.java",
         "ojluni/src/main/java/sun/util/locale/InternalLocaleBuilder.java",
+        "ojluni/src/main/java/sun/util/locale/LanguageTag.java",
         "ojluni/src/main/java/sun/util/locale/LocaleEquivalentMaps.java",
         "ojluni/src/main/java/sun/util/locale/LocaleExtensions.java",
         "ojluni/src/main/java/sun/util/locale/LocaleMatcher.java",
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index 7521cf9..64392e4 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -608,6 +608,14 @@
             if (Security.getProvider("KnoxAndroidKeyStore") != null) {
                 provide("KeyStore", "KnoxAndroidKeyStore");
             }
+
+            // Elliptic curve Diffie-Hellman
+            provide("KeyAgreement", "XDH");
+            provide("KeyFactory", "XDH");
+            provide("KeyPairGenerator", "XDH");
+
+            // AES-CMAC Mac
+            provide("Mac", "AESCMAC");
         }
     }
 
diff --git a/support/src/test/java/org/apache/harmony/security/tests/support/TestCertUtils.java b/support/src/test/java/org/apache/harmony/security/tests/support/TestCertUtils.java
index c9b9c9a..62261db 100644
--- a/support/src/test/java/org/apache/harmony/security/tests/support/TestCertUtils.java
+++ b/support/src/test/java/org/apache/harmony/security/tests/support/TestCertUtils.java
@@ -151,7 +151,7 @@
         private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7,
                 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
 
-        private static final String serializedData = "Just a dummy string to be serialized instead of real data";
+        private static final String serializedData = "Just a fake string to be serialized instead of real data";
 
         private Certificate[] certs;
 
diff --git a/support/src/test/java/org/apache/harmony/security/tests/support/TestKeyStoreSpi.java b/support/src/test/java/org/apache/harmony/security/tests/support/TestKeyStoreSpi.java
index 86b54c4..4994b72 100644
--- a/support/src/test/java/org/apache/harmony/security/tests/support/TestKeyStoreSpi.java
+++ b/support/src/test/java/org/apache/harmony/security/tests/support/TestKeyStoreSpi.java
@@ -50,13 +50,13 @@
 
     };
 
-    public static final Object DUMMY = new Object();
+    public static final Object FAKE = new Object();
 
     public TestKeyStoreSpi() {
         aliases.put("certalias", CERT);
         aliases.put("chainalias", CERTCHAIN);
         aliases.put("keyalias", KEY);
-        aliases.put("unknownalias", DUMMY);
+        aliases.put("unknownalias", FAKE);
     }
 
     @Override
diff --git a/support/src/test/java/tests/support/Support_ProviderTrust.java b/support/src/test/java/tests/support/Support_ProviderTrust.java
index 9c47e10..dc36930 100644
--- a/support/src/test/java/tests/support/Support_ProviderTrust.java
+++ b/support/src/test/java/tests/support/Support_ProviderTrust.java
@@ -22,7 +22,7 @@
 import java.security.Provider;
 
 /**
- * This class implements a dummy provider.
+ * This class implements a fake provider.
  *
  */
 public class Support_ProviderTrust extends Provider {
@@ -38,7 +38,7 @@
             + " DSA key, parameter generation and signing; SHA-1 digest; SHA1PRNG SecureRandom";
 
     /**
-     * Constructs a new instance of the dummy provider.
+     * Constructs a new instance of the fake provider.
      *
      */
     public Support_ProviderTrust() {
diff --git a/support/src/test/java/tests/support/Support_TestProvider.java b/support/src/test/java/tests/support/Support_TestProvider.java
index 0b5d319..5c87b39 100644
--- a/support/src/test/java/tests/support/Support_TestProvider.java
+++ b/support/src/test/java/tests/support/Support_TestProvider.java
@@ -22,7 +22,7 @@
 import java.security.Provider;
 
 /**
- * This class implements a dummy provider.
+ * This class implements a fake provider.
  */
 public class Support_TestProvider extends Provider {
     private static final long serialVersionUID = 1L;
@@ -38,7 +38,7 @@
             + "SHA1PRNG SecureRandom; PKCS#12/Netscape KeyStore";
 
     /**
-     * Constructs a new instance of the dummy provider.
+     * Constructs a new instance of the fake provider.
      */
     public Support_TestProvider() {
         super(NAME, VERSION, INFO);
@@ -110,7 +110,7 @@
                 put("Alg.Alias.Signature.OID.1.3.14.3.2.27", "SHA1withDSA");
 
                 put("KeyStore.PKCS#12/Netscape",
-                        "tests.support.Support_DummyPKCS12Keystore");
+                        "tests.support.Support_FakePKCS12Keystore");
 
                 // Certificate
                 put("CertificateFactory.X509",
@@ -122,4 +122,4 @@
             }
         });
     }
-}
\ No newline at end of file
+}
diff --git a/test-rules/src/platform_compat/java/libcore/junit/util/CoreCompatChangeRule.java b/test-rules/src/platform_compat/java/libcore/junit/util/CoreCompatChangeRule.java
index b526707..4fd3208 100644
--- a/test-rules/src/platform_compat/java/libcore/junit/util/CoreCompatChangeRule.java
+++ b/test-rules/src/platform_compat/java/libcore/junit/util/CoreCompatChangeRule.java
@@ -17,7 +17,6 @@
 package libcore.junit.util.compat;
 
 import android.compat.Compatibility;
-import android.compat.Compatibility.Callbacks;
 import android.compat.Compatibility.ChangeConfig;
 
 import org.junit.rules.TestRule;
diff --git a/test-rules/src/test/java/android/compat/testing/DummyApi.java b/test-rules/src/test/java/android/compat/testing/FakeApi.java
similarity index 90%
rename from test-rules/src/test/java/android/compat/testing/DummyApi.java
rename to test-rules/src/test/java/android/compat/testing/FakeApi.java
index dfe40cc..95fc8a0 100644
--- a/test-rules/src/test/java/android/compat/testing/DummyApi.java
+++ b/test-rules/src/test/java/android/compat/testing/FakeApi.java
@@ -19,21 +19,21 @@
 import android.compat.Compatibility;
 
 /**
- * This is a dummy API to test gating
+ * This is a fake API to test gating
  *
  * @hide
  */
-public class DummyApi {
+public class FakeApi {
 
     public static final long CHANGE_ID = 666013;
     public static final long CHANGE_ID_1 = 666014;
     public static final long CHANGE_ID_2 = 666015;
 
     /**
-     * Dummy method
+     * Fake method
      * @return "A" if change is enabled, "B" otherwise.
      */
-    public static String dummyFunc() {
+    public static String fakeFunc() {
         if (Compatibility.isChangeEnabled(CHANGE_ID)) {
             return "A";
         }
@@ -41,13 +41,13 @@
     }
 
     /**
-     * Dummy combined method
+     * Fake combined method
      * @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled,
                "1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled,
                "2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled,
                "3" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is enabled.
      */
-    public static String dummyCombinedFunc() {
+    public static String fakeCombinedFunc() {
         if (!Compatibility.isChangeEnabled(CHANGE_ID_1)
                 && !Compatibility.isChangeEnabled(CHANGE_ID_2)) {
             return "0";
diff --git a/test-rules/src/test/java/libcore/junit/util/compat/CoreCompatChangeRuleTest.java b/test-rules/src/test/java/libcore/junit/util/compat/CoreCompatChangeRuleTest.java
index 87b9446..1df8888 100644
--- a/test-rules/src/test/java/libcore/junit/util/compat/CoreCompatChangeRuleTest.java
+++ b/test-rules/src/test/java/libcore/junit/util/compat/CoreCompatChangeRuleTest.java
@@ -17,7 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.compat.testing.DummyApi;
+import android.compat.testing.FakeApi;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -38,40 +38,40 @@
     public TestRule compatChangeRule = new CoreCompatChangeRule();
 
     @Test
-    @EnableCompatChanges({DummyApi.CHANGE_ID})
-    public void testDummyGatingPositive() {
-        assertThat(DummyApi.dummyFunc()).isEqualTo("A");
+    @EnableCompatChanges({FakeApi.CHANGE_ID})
+    public void testFakeGatingPositive() {
+        assertThat(FakeApi.fakeFunc()).isEqualTo("A");
     }
 
     @Test
-    @DisableCompatChanges({DummyApi.CHANGE_ID})
-    public void testDummyGatingNegative() {
-        assertThat(DummyApi.dummyFunc()).isEqualTo("B");
+    @DisableCompatChanges({FakeApi.CHANGE_ID})
+    public void testFakeGatingNegative() {
+        assertThat(FakeApi.fakeFunc()).isEqualTo("B");
     }
 
     @Test
-    @DisableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
-    public void testDummyGatingCombined0() {
-        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("0");
+    @DisableCompatChanges({FakeApi.CHANGE_ID_1, FakeApi.CHANGE_ID_2})
+    public void testFakeGatingCombined0() {
+        assertThat(FakeApi.fakeCombinedFunc()).isEqualTo("0");
     }
 
     @Test
-    @DisableCompatChanges({DummyApi.CHANGE_ID_1})
-    @EnableCompatChanges({DummyApi.CHANGE_ID_2})
-    public void testDummyGatingCombined1() {
-        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("1");
+    @DisableCompatChanges({FakeApi.CHANGE_ID_1})
+    @EnableCompatChanges({FakeApi.CHANGE_ID_2})
+    public void testFakeGatingCombined1() {
+        assertThat(FakeApi.fakeCombinedFunc()).isEqualTo("1");
     }
 
     @Test
-    @EnableCompatChanges({DummyApi.CHANGE_ID_1})
-    @DisableCompatChanges({DummyApi.CHANGE_ID_2})
-    public void testDummyGatingCombined2() {
-        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("2");
+    @EnableCompatChanges({FakeApi.CHANGE_ID_1})
+    @DisableCompatChanges({FakeApi.CHANGE_ID_2})
+    public void testFakeGatingCombined2() {
+        assertThat(FakeApi.fakeCombinedFunc()).isEqualTo("2");
     }
 
     @Test
-    @EnableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
-    public void testDummyGatingCombined3() {
-        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("3");
+    @EnableCompatChanges({FakeApi.CHANGE_ID_1, FakeApi.CHANGE_ID_2})
+    public void testFakeGatingCombined3() {
+        assertThat(FakeApi.fakeCombinedFunc()).isEqualTo("3");
     }
 }
\ No newline at end of file
diff --git a/tools/docs/crypto/src/java/libcore/java/security/ListProviders.java b/tools/docs/crypto/src/java/libcore/java/security/ListProviders.java
index 9b7e5f0..3369bcd 100644
--- a/tools/docs/crypto/src/java/libcore/java/security/ListProviders.java
+++ b/tools/docs/crypto/src/java/libcore/java/security/ListProviders.java
@@ -141,7 +141,7 @@
         // suites are disabled in that case, so check for both
         SSLContext pskContext = SSLContext.getInstance("TLS");
         pskContext.init(
-                new KeyManager[] {new DummyKeyManager()},
+                new KeyManager[] {new FakeKeyManager()},
                 new TrustManager[0],
                 null);
         for (SSLContext sslContext : new SSLContext[] {defaultContext, tls13Context, pskContext}) {
@@ -163,7 +163,7 @@
         System.out.println("END ALGORITHM LIST");
     }
 
-    private static class DummyKeyManager implements PSKKeyManager {
+    private static class FakeKeyManager implements PSKKeyManager {
         @Override public String chooseServerKeyIdentityHint(Socket socket) { return null; }
         @Override public String chooseServerKeyIdentityHint(SSLEngine engine) { return null; }
         @Override public String chooseClientKeyIdentity(String identityHint, Socket socket) { return null; }
diff --git a/tools/patch-style/libcore-patch-style.awk b/tools/patch-style/libcore-patch-style.awk
new file mode 100644
index 0000000..e138298
--- /dev/null
+++ b/tools/patch-style/libcore-patch-style.awk
@@ -0,0 +1,175 @@
+# Copyright 2020 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.
+
+# Simple libcore patch-style checking based on http://go/libcore-patch-style.
+#
+# See sample-input.java for examples of matches and failures.
+#
+# Usage:
+#  awk -f libcore-patch-style.awk <file0> [... fileN]
+#
+# This script ignores any files whose name does not end in the suffix ".java".
+#
+# To scan all source code in the libcore tree:
+#  $ cd libcore
+#  $ find . -type f | xargs awk -f tools/patch-style/libcore-patch-style.awk
+#
+# To find sources with the most issues:
+#  $ cd libcore
+#  $ find . -type f | xargs awk -f tools/patch-style/libcore-patch-style.awk \
+#                   | grep -F ./ | sed -e 's/:.*//' | uniq -c | sort -n -r | head
+
+BEGIN {
+  g_errors = 0                    # Number of errors accumulated.
+  g_expected_end = ""             # Expected END line.
+  g_max_length = 100              # Maximum line length for markers (0 == no checking).
+  g_stop_oneline_interleaving = 0 # Error one-line comments between BEGIN and END markers.
+}
+
+BEGINFILE {
+  # Skip files whose names do not have a .java suffix.
+  if (FILENAME !~ /\.java$/) {
+    nextfile
+  }
+
+  # Reset the line number for reporting errors back to zero.
+  NR = 0
+
+  # Clear expected end marker as processing a new file.
+  g_expected_end = ""
+}
+
+function error(message) {
+  print(FILENAME ":" NR ":", message "\n")
+  g_errors += 1;
+}
+
+function expectationError(reason, expected, actual) {
+  error(reason "\n  Expected: \"" expected "\"\n  Actual:   \"" actual "\"")
+}
+
+function inputError(reason, actual) {
+  error(reason "\n  Input:    \"" actual "\"")
+}
+
+function checkLineLength(line) {
+  if (g_max_length > 0 && length(line) > g_max_length) {
+    inputError("Line too long", line)
+  }
+}
+
+function leftTrim(message) {
+  return gensub(/^ */, "", 1, message)
+}
+
+function failIfEndExpected() {
+  if (g_expected_end != "") {
+    expectationError("Missing END marker.", g_expected_end, $0)
+    g_expected_end = ""
+  }
+}
+
+function expectEndFor(begin_line) {
+  g_expected_end = begin_line
+  sub("BEGIN", "END", g_expected_end)
+}
+
+function actualEndFor(actual_line) {
+  if (actual_line != g_expected_end) {
+    expectationError("Bad END marker.", g_expected_end, actual_line)
+  }
+  g_expected_end = ""
+}
+
+function processBeginMarker(line) {
+  failIfEndExpected()
+  expectEndFor(line)
+}
+
+function processEndMarker(line) {
+  actualEndFor(line)
+}
+
+# BEGIN marker ending in a period.
+/^ *\/\/ BEGIN Android-(added|changed|note|removed):.*\./ {
+  checkLineLength($0)
+  processBeginMarker($0)
+  next
+}
+
+# BEGIN marker ending in a period.
+/^ *\/\/ END Android-(added|changed|note|removed):.*\./ {
+  checkLineLength($0)
+  processEndMarker($0)
+  next
+}
+
+# BEGIN marker ending in a bug reference.
+/^ *\/\/ BEGIN Android-(added|changed|note|removed):.*[.](http:\/\/)?b\/[1-9][0-9]*/ {
+  checkLineLength($0)
+  processBeginMarker($0)
+  next
+}
+
+# BEGIN marker ending in anything else, oops!
+/^ *\/\/ BEGIN Android-(added|changed|note|removed)[^:].*/ {
+  inputError("BEGIN marker is missing colon or description.", $0)
+  next
+}
+
+# END marker, should be paired with last BEGIN marker.
+/^ *\/\/ END Android-(added|changed|note|removed):.*/ {
+  checkLineLength($0)
+  processEndMarker($0)
+  next
+}
+
+# One line change marker ending in a period.
+/^ *\/\/ Android-(added|changed|note|removed):.*[.]/ {
+  checkLineLength($0)
+  if (g_stop_oneline_interleaving) {
+    failIfEndExpected()
+  }
+  next
+}
+
+# One line change marker ending in a bug reference.
+/^ *\/\/ Android-(added|changed|note|removed):[.](http:\/\/)?b\/[1-9][0-9]*/ {
+  checkLineLength($0)
+  if (g_stop_oneline_interleaving) {
+    failIfEndExpected()
+  }
+  next
+}
+
+# One line change marker missing comment after colon.
+/^ *\/\/ BEGIN Android-(added|changed|note|removed).*/ {
+  inputError("Bad change marker: missing colon or description.", $0)
+  next
+}
+
+# Something that looks like a potential change marker.
+/^ *(\/\*|\/\/|\*) *(Android|ANDROID)-/ {
+  if (g_stop_oneline_interleaving) {
+    failIfEndExpected()
+  }
+  inputError("Bad change marker.", $0)
+  next
+}
+
+END {
+  failIfEndExpected()
+  printf("Found " g_errors " libcore patch style issues.\n")
+  exit g_errors == 0
+}
diff --git a/tools/patch-style/sample-input.java b/tools/patch-style/sample-input.java
new file mode 100644
index 0000000..8d55cfe
--- /dev/null
+++ b/tools/patch-style/sample-input.java
@@ -0,0 +1,32 @@
+# HAPPY PATHS
+    // BEGIN Android-added: something bot like.
+    // END Android-added: something bot like.
+
+    // BEGIN Android-changed: something bot like. http://b/12345
+    // END Android-changed: something bot like. http://b/12345
+
+    // BEGIN Android-removed: coffee stain. b/12345
+    // END Android-changed: something bot like. http://b/12345
+
+    // Android-added: hello world.
+    // Android-change: hello world. http://b/1100
+    // Android-remove: goodbye world. http://b/999912
+
+# UNHAPPY PATHS
+
+    //  BEGIN Android-added something bot like.
+    //  END Android-added something bot like.
+
+    // Android-note: blah
+    // Android-note: blah.
+    // Android-changed hello
+    // Android-added: hello
+
+    /* Android-note blah balh */
+    /* Android note: blah balh */
+    /* ANDROID android android android */
+    * ANDROID *
+
+Android-bar
+
+// BEGIN Android-removed: END from last line.
diff --git a/tools/testmapping/README b/tools/testmapping/README
new file mode 100644
index 0000000..0222018
--- /dev/null
+++ b/tools/testmapping/README
@@ -0,0 +1,36 @@
+These scripts help generate the libcore TEST_MAPPING smoke tests, i.e. a set of
+tests to run on every change, chosen to run as many as possible in less than
+some time limit.
+
+The process is as follows.
+1. Do `source build/envsetup.sh` and `lunch <whatever>` as normal.
+2. Run the CtsLibcoreTestCases tests to generate logs to extract timings from.
+   This can be done with `atest CtsLibcoreTestCases` as normal. Make sure to use
+   an appropriate device (virtual or physical).
+3. Do that two more times. We'll use best-of-three timings, since sometimes a
+   test takes an unusual amount of time (perhaps because of GC pause or other
+   jank) and it should not be excluded for that.
+4. Run the save_logs.py script to copy the logs from out/ to libcore/smoketest.
+   (Empty that directory if it exists already). This is interactive and allows
+   you to pick the runs you want. (If you prefer, you can run this after each
+   run, rather than once after all three runs.)
+5. Run the gen_smoke_tests.py script to generate libcore/TEST_MAPPING.
+6. Check stdout from the script looks okay (not too many warnings, sensible
+   numbers, etc.).
+7. Check the generated TEST_MAPPING looks okay.
+8. Do e.g. `time atest --test-mapping libcore` to check it runs okay.
+9. Delete libcore/smoketest/ once you're happy.
+10. Submit the new TEST_MAPPING.
+
+The scripts take no options. There are some constants at the start you can
+adjust. (These could be converted to command-line options if convenient.)
+
+See comments in the scripts for more on how they work.
+
+At time of writing, with the current configuration, this generates a sensible
+number of classes to exclude, so the TEST_MAPPING looks reasonable. If this list
+becomes too long, we'll have to find a way to simplify it, by rolling up to a
+higher granularity. Given the way that atest et al are configured, that will
+probably mean excluding more things.
+
+TODO(peteg): What about PTS?
diff --git a/tools/testmapping/gen_smoke_tests.py b/tools/testmapping/gen_smoke_tests.py
new file mode 100755
index 0000000..159be1b
--- /dev/null
+++ b/tools/testmapping/gen_smoke_tests.py
@@ -0,0 +1,474 @@
+#!/usr/bin/env python2
+#
+#   Copyright 2020 - 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.
+
+"""Tool to generate data for smoke tests."""
+
+from __future__ import print_function
+
+import collections
+import datetime
+import gzip
+import operator
+import os
+import re
+import tempfile
+
+import jinja2
+import util
+
+ANDROID_REPOSITORY_ROOT = util.android_repository_root()
+LIBCORE_DIR = os.path.join(ANDROID_REPOSITORY_ROOT, 'libcore')
+LOGS_DIR = os.path.join(LIBCORE_DIR, 'smoketest')
+REQUIRED_RUNS = 3
+CTS_LOG_LINE_PATTERN = (r'(\d+)-(\d+) +(\d+):(\d+):(\d+)\.(\d+) +\d+ +\d+ +'
+                        r'[^ ]+ (CtsTestRunListener|TestRunner) *: *(.+)')
+THIS_YEAR = datetime.datetime.now().year
+START_MATCHER = ('CtsTestRunListener', r'Now executing\s*:\s*(.+)')
+TESTS_RAN_MATCHER = ('TestRunner', r'started\s*:.*')
+END_MATCHER = ('CtsTestRunListener', r'Total memory\s*:.+')
+HARD_END_MATCHER = ('TestRunner', r'run finished\s*:.+')
+JAVA_CLASS_PATTERN = r'(?:([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\.)?[a-zA-Z_][a-zA-Z0-9_]*'
+MAX_RUN_TIME = datetime.timedelta(minutes=10)
+OUT_DIR = tempfile.mkdtemp()
+ROLL_UP_TEST_CLASSES_TO_PACKAGE = False
+CTS_MODULE_NAME = 'CtsLibcoreTestCases'
+
+TEST_MAPPING_TEMPLATE = jinja2.Template("""
+{
+  "presubmit": [
+    {
+      "name": "{{module}}",
+      "options": [{% for test in exclusions %}
+        {
+          "exclude-filter": "{{test}}"
+        }{% if not loop.last %},{% endif %}{% endfor %}
+      ]
+    }
+  ]
+}
+""".strip())
+
+
+def find_all_log_files():
+  """Returns a list of the log files to read."""
+  if not os.path.isdir(LOGS_DIR):
+    raise Exception('Could not find logs directory ' + LOGS_DIR)
+  filenames = os.listdir(LOGS_DIR)
+  if not filenames:
+    raise Exception('Found empty logs directory ' + LOGS_DIR)
+  if len(filenames) != REQUIRED_RUNS:
+    raise Exception('Expected to find exactly %d files in %s, found %d' %
+                    (REQUIRED_RUNS, LOGS_DIR, len(filenames)))
+  return map(lambda f: os.path.join(LOGS_DIR, f), filenames)
+
+
+def read_cts_logs(filename):
+  """Read CTS entries from a log file.
+
+  Args:
+    filename: The name of the file to read.
+
+  Yields:
+    Tuples of timestamps (as datetimes), log sources, and log messages.
+  """
+  print('Reading ' + util.printable_path(filename))
+  with gzip.open(filename, mode='rt') as log_file:
+    for line in log_file:
+      cts_match = re.match(CTS_LOG_LINE_PATTERN, line)
+      if cts_match:
+        assert len(cts_match.groups()) == 8
+        timestamp = datetime.datetime(
+            year=THIS_YEAR,
+            month=int(cts_match.group(1)),
+            day=int(cts_match.group(2)),
+            hour=int(cts_match.group(3)),
+            minute=int(cts_match.group(4)),
+            second=int(cts_match.group(5)),
+            microsecond=1000 * int(cts_match.group(6)))
+        source = cts_match.group(7)
+        message = cts_match.group(8)
+        yield (timestamp, source, message)
+
+
+def match(matcher, got_source, message):
+  """Check whether a log line matches requirements.
+
+  Args:
+    matcher: A pair of the required log source and a pattern to match messages.
+    got_source: The actual log source.
+    message: The actual message.
+
+  Returns:
+    A MatchObject from the pattern match against the message, or None.
+  """
+  (want_source, pattern) = matcher
+  if got_source == want_source:
+    return re.match(pattern, message)
+  else:
+    return None
+
+
+def parse_running_times(filename):
+  """Parse the running times from a log file.
+
+  Args:
+    filename: The name of the file to read.
+
+  Yields:
+    Pairs of test names and running times (as timedeltas). Also emits the
+    overall elapsed time from the start of the first test to the end of the last
+    test with the key 'OVERALL'.
+
+  Raises:
+    Exception: The log file entries didn't look like we expected.
+  """
+  executing = None
+  start_timestamp = None
+  ran_tests = False
+  overall_start_timestamp = None
+  overall_end_timestamp = None
+  for (timestamp, source, message) in read_cts_logs(filename):
+    start_match = match(START_MATCHER, source, message)
+    tests_ran_match = match(TESTS_RAN_MATCHER, source, message)
+    end_match = match(END_MATCHER, source, message)
+    hard_end_match = match(HARD_END_MATCHER, source, message)
+    if not executing:
+      if start_match:
+        assert len(start_match.groups()) == 1
+        executing = start_match.group(1)
+        start_timestamp = timestamp
+        if not overall_start_timestamp:
+          overall_start_timestamp = timestamp
+    else:
+      if start_match:
+        raise Exception(
+            'Found start for %s while waiting for end for %s at %s in %s' %
+            (start_match.group(1), executing, str(timestamp), filename))
+      if tests_ran_match:
+        ran_tests = True
+      if end_match or hard_end_match:
+        running_time = timestamp - start_timestamp
+        # We see two types of execution in the logs. One appears to be some kind
+        # of dry run which doesn't actually run any tests (and completes
+        # significantly faster). We want the one that actually runs tests.
+        if ran_tests:
+          yield (executing, running_time)
+        executing = None
+        start_timestamp = None
+        ran_tests = False
+        overall_end_timestamp = timestamp
+  if executing:
+    raise Exception('Reached EOF while waiting for end for %s in %s' %
+                    (executing, filename))
+  yield ('OVERALL', overall_end_timestamp - overall_start_timestamp)
+
+
+def collect_running_times(filenames):
+  """Collect running times from some log file.
+
+  Args:
+    filenames: The names of the files to read.
+
+  Returns:
+    A tuple containing (1) a dictionary mapping test names to sets of running
+    times (as timedeltas), and (2) a list of overall running times (i.e. elapsed
+    times from the start of the first test to the end of the last test).
+  """
+  times_by_test = collections.defaultdict(list)
+  output_path = os.path.join(OUT_DIR, 'test_times.txt')
+  overall_times = []
+  with open(output_path, 'w') as output:
+    for filename in filenames:
+      for (test_name, time) in parse_running_times(filename):
+        output.write('%s: %g ms\n' % (test_name, time.total_seconds() * 1000))
+        if test_name == 'OVERALL':
+          overall_times.append(time)
+        else:
+          times_by_test[test_name].append(time)
+  print('Wrote test times to ' + util.printable_path(output_path))
+  return (times_by_test, overall_times)
+
+
+def process_running_times(times_by_test):
+  """Processes the collected running times.
+
+  Args:
+    times_by_test: A dictionary mapping test names to sets of running times.
+
+  Returns:
+    A dictionary mapping test names to fastest running times.
+  """
+  for (test, times) in times_by_test.iteritems():
+    if len(times) != REQUIRED_RUNS:
+      print('Warning: Only found %d runs for %s' % (len(times), test))
+  return {test: min(times) for (test, times) in times_by_test.iteritems()}
+
+
+def calculate_overhead_ratio(overall_times, fastest_time_by_test):
+  """Calculates a ratio for the actual overall time to the sum of test times.
+
+   The actual overall times are the elapsed times from the start of the first
+   test to the end of the last test. The average of these is used. The ratio is
+   taken with the sume of the fastest time for each test (which is what will be
+   used for scoring).
+
+  Args:
+    overall_times: A list of overall running times.
+    fastest_time_by_test: A dictionary mapping test names to fastest running
+      times.
+
+  Returns:
+    The ratio.
+  """
+  average_overall_time = sum(overall_times,
+                             datetime.timedelta(0)) / len(overall_times)
+  total_time_by_test = sum(fastest_time_by_test.values(), datetime.timedelta(0))
+  ratio = (
+      average_overall_time.total_seconds() / total_time_by_test.total_seconds())
+  print(
+      'Average time for run is %g seconds, sum of fastest test times is %g, ratio is %g'
+      % (average_overall_time.total_seconds(),
+         total_time_by_test.total_seconds(), ratio))
+................................................................................
+  # N.B. Possible future enhancement: Currently, because# we take the fastest of
+  # three runs, a real run will always be slightly slower than we predict. We
+  # could multiply in another overhead factor to account for this, e.g. by
+  # looking at the ratio of the mean of the three to the fastest of the three.
+  # (This factor should be applied globally rather than individually to each
+  # test so as not to penalize tests which happened to have a slow run or two.)
+  # This is not a high priority since for now we can just set MAX_RUN_TIME a bit
+  # low to allow for this.
+  return ratio
+
+
+def get_parent(name):
+  """Returns the parent of a Java class or package name."""
+  class_match = re.match(JAVA_CLASS_PATTERN, name)
+  if not class_match:
+    raise Exception('Could not parse Java class name ' + name)
+  assert len(class_match.groups()) == 1
+  return class_match.group(1)
+
+
+def group_times_by_package(times_by_test):
+  """Groups the test classes by package name, summing the times.
+
+  Args:
+    times_by_test: A dictionary mapping test names to fastest running times.
+
+  Returns:
+    A dictionary mapping package names to total fastest running times.
+  """
+  time_by_package = collections.defaultdict(datetime.timedelta)
+  for (clazz, time) in times_by_test.iteritems():
+    package = get_parent(clazz)
+    if package:  # A few tests have no package. They're weird, let's skip them.
+      time_by_package[package] = time_by_package[package] + time
+  output_path = os.path.join(OUT_DIR, 'package_times.txt')
+  with open(output_path, 'w') as output:
+    for (package, time) in time_by_package.iteritems():
+      output.write('%s: %s ms\n' % (package, time.total_seconds() * 1000.0))
+  print('Wrote package times to ' + util.printable_path(output_path))
+  return time_by_package
+
+
+def find_tests_to_run(time_by_test, overhead_ratio):
+  """Finds the tests to actually run.
+
+  The tests chosen will be the fastest set such that their total time, when
+  multiplied by the overhead ratio, is less than the maximum.
+
+  Args:
+    time_by_test: A dictionary mapping test names to total fastest running
+      times. The names can be packages, classes, or a mixture of the two.
+    overhead_ratio: A ratio for the actual overall time to the sum of test
+      times.
+
+  Returns:
+    A list of test names whose running times are below the threshold.
+
+  Raises:
+    Exception: The total running time of all the tests is below the threhold.
+  """
+  test_inclusions = {test: False for test in time_by_test.keys()}
+  included = 0
+  total_time = datetime.timedelta()
+  output_path = os.path.join(OUT_DIR, 'included_tests.txt')
+  adjusted_max_run_time_seconds = MAX_RUN_TIME.total_seconds() / overhead_ratio
+  with open(output_path, 'w') as output:
+    for (test, time) in sorted(
+        time_by_test.iteritems(), key=operator.itemgetter(1)):
+      if (total_time + time).total_seconds() <= adjusted_max_run_time_seconds:
+        test_inclusions[test] = True
+        included += 1
+        total_time += time
+        output.write('%s: %g ms -> %g ms\n' %
+                     (test, time.total_seconds() * 1000.0,
+                      total_time.total_seconds() * 1000.0))
+      else:
+        print('Can run fastest %d of %d tests in %g * %g = %g seconds' %
+              (included, len(time_by_test), total_time.total_seconds(),
+               overhead_ratio, total_time.total_seconds() * overhead_ratio))
+        print('Wrote tests to include to ' + util.printable_path(output_path))
+        return test_inclusions
+  raise Exception('Apparently we can run all the tests? This smells wrong.')
+
+
+def build_test_tree(tests):
+  """Builds a tree of tests.
+
+  Args:
+    tests: The list of tests to build into a tree. These can be packages,
+      classes, or a mixture of the two.
+
+  Returns:
+    A dictionary mapping every test's ancestors to its children. The roots
+    appear as children of None.
+  """
+  tree = collections.defaultdict(set)
+  for test in tests:
+    while True:
+      parent = get_parent(test)
+      tree[parent].add(test)
+      if parent:
+        test = parent
+      else:
+        break
+  return tree
+
+
+def build_exclusion_list(test_inclusions):
+  """Builds a list of tests to exclude.
+
+  Args:
+    test_inclusions: A dictionary mapping test names to whether or not they
+      should be included in the smoke tests. The names can be packages, classes,
+      or a mixture of the two.
+
+  Returns:
+    A list of the exclusions. These could be individual tests, or packages or
+    some part of the package hierarchy if they are to be entirely excluded.
+  """
+  tree = build_test_tree(test_inclusions.keys())
+  exclusions = []
+
+  # We do a DFS of the tree, rolling up exclusions as far as possible, i.e. if
+  # an entire branch is excluded, we exclude that branch rather than all of its
+  # leaves.
+
+  def visit(test):
+    """Visitor for a DFS of the tree.
+
+    Args:
+      test: The test to visit (either a class or package name).
+
+    Returns:
+      Whether or not the parent should include this node in the tree.
+
+    Raises:
+      Exception: The tree had an unexpected structure.
+    """
+    if test in test_inclusions:
+      # We only expect to have inclusion status for the leaves of the tree.
+      # Check that this is the case.
+      if test in tree:
+        raise Exception('The name %s is used for a leaf and a branch!' % test)
+      # Return to the parent node whether this leaf is included.
+      return test_inclusions[test]
+    else:
+      # We expect to have inclusion status for all leaves of the tree. Check
+      # that this is the case.
+      if test not in tree:
+        raise Exception('Leaf %s has no inclusion status!' % test)
+      # Look at all the children of this node.
+      any_child_included = False
+      child_exclusions = []
+      for child in tree[test]:
+        child_included = visit(child)
+        if child_included:
+          any_child_included = True
+        else:
+          child_exclusions.append(child)
+      # If any children are included, we will count this node as included, so we
+      # have to add any excluded children to the exclusion list.
+      if any_child_included:
+        for child in child_exclusions:
+          exclusions.append(child)
+      # Now return whether this node should be counted as included, which means
+      # whether any children are included.
+      return any_child_included
+
+  # Start the DFS at each root of the tree.
+  for root in tree[None]:
+    root_included = visit(root)
+    # If any of the roots are counted as excluded, add them to the list.
+    if not root_included:
+      exclusions.append(root)
+
+  # Finally, sort and return the list of exclusions.
+  return sorted(exclusions)
+
+
+def self_test():
+  """Self-test for the build_include_exclude_list logic."""
+  test_inclusions = {
+      'a.x': True,
+      'a.y': True,
+      'b.x': True,
+      'b.y': False,
+      'c.x': False,
+      'c.y': False,
+      'd.x.m': True,
+      'd.x.n': True,
+      'd.y.m': True,
+      'd.y.n': False,
+      'd.z.m': False,
+      'd.z.n': False,
+  }
+  expected_exclusions = [
+      'b.y',
+      'c',
+      'd.y.n',
+      'd.z',
+  ]
+  actual_exclusions = build_exclusion_list(test_inclusions)
+  if actual_exclusions != expected_exclusions:
+    raise Exception('Self test failed! Expected %s but got %s' %
+                    (expected_exclusions, actual_exclusions))
+
+
+def main():
+  """The main method."""
+  self_test()
+  filenames = find_all_log_files()
+  (times_by_test, overall_times) = collect_running_times(filenames)
+  fastest_time_by_test = process_running_times(times_by_test)
+  overhead_ratio = calculate_overhead_ratio(overall_times, fastest_time_by_test)
+  if ROLL_UP_TEST_CLASSES_TO_PACKAGE:
+    time_by_package = group_times_by_package(fastest_time_by_test)
+    test_inclusions = find_tests_to_run(time_by_package, overhead_ratio)
+  else:
+    test_inclusions = find_tests_to_run(fastest_time_by_test, overhead_ratio)
+  exclusions = build_exclusion_list(test_inclusions)
+  output_path = os.path.join(LIBCORE_DIR, 'TEST_MAPPING')
+  with open(output_path, 'w') as output:
+    output.write(
+        TEST_MAPPING_TEMPLATE.render(
+            module=CTS_MODULE_NAME, exclusions=exclusions))
+  print('Wrote test mapping to ' + util.printable_path(output_path))
+
+
+main()
diff --git a/tools/testmapping/save_logs.py b/tools/testmapping/save_logs.py
new file mode 100755
index 0000000..8f737b3e
--- /dev/null
+++ b/tools/testmapping/save_logs.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python2
+#
+#   Copyright 2020 - 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.
+
+"""Tool to save logs files for use by gen_smoke_tests.py."""
+
+from __future__ import print_function
+
+import glob
+import os
+import shutil
+import sys
+
+import util
+
+ANDROID_REPOSITORY_ROOT = util.android_repository_root()
+LOGS_ROOT = os.path.join(ANDROID_REPOSITORY_ROOT, 'out', 'host', 'linux-x86',
+                         'cts', 'android-cts', 'logs')
+CTS_LOGS_PATTERN = os.path.join(LOGS_ROOT, '*', '*',
+                                'device_logcat_test_*.txt.gz')
+LIBCORE_DIR = os.path.join(ANDROID_REPOSITORY_ROOT, 'libcore')
+DESTINATION_DIR = os.path.join(LIBCORE_DIR, 'smoketest')
+
+
+def check_destination():
+  """Ensures the destination directory to copy the files to exists."""
+  if not os.path.isdir(LIBCORE_DIR):
+    raise Exception('Could not find directory ' + LIBCORE_DIR)
+  if not os.path.isdir(DESTINATION_DIR):
+    print('Making destination directory ' +
+          util.printable_path(DESTINATION_DIR))
+    os.mkdir(DESTINATION_DIR, 0o755)
+  else:
+    print('Found destination directory ' + util.printable_path(DESTINATION_DIR))
+
+
+def is_real_log_file(filename):
+  """Returns whether the filename is one we should use or not."""
+  return 'latest' not in filename
+
+
+def find_all_log_files():
+  """Finds all CTS log files in the expected directory.
+
+  Returns:
+    A list of filenames, sorted by mtime, most recent first.
+  Raises:
+    Exception: Not enough log files found.
+  """
+  print('Looking for logs in %s' % util.printable_path(LOGS_ROOT))
+  print('+++ ' + CTS_LOGS_PATTERN)
+  for f in glob.glob(CTS_LOGS_PATTERN):
+    print('*** ' + f)
+  sources = [f for f in glob.glob(CTS_LOGS_PATTERN) if is_real_log_file(f)]
+  if not sources:
+    raise Exception('Found no logs files!')
+  return sorted(sources, key=os.path.getmtime, reverse=True)
+
+
+def relative_source_name(source):
+  """Returns the name of a source file, relative to the logs root."""
+  return os.path.relpath(source, LOGS_ROOT)
+
+
+def flatten_name(name):
+  """Returns a flattened version of a path, using _ to separate."""
+  parts = []
+  while name:
+    (head, tail) = os.path.split(name)
+    parts.insert(0, tail)
+    name = head
+  return '_'.join(parts)
+
+
+def destination_path(name):
+  """Returns the destination path for a given name (which must be flattened)."""
+  assert not os.path.dirname(name)
+  return os.path.join(DESTINATION_DIR, name)
+
+
+def get_indexes_from_user(prompt, options):
+  """Gets a sequence of indexes between 1 and max from the user.
+
+  Args:
+    prompt: A prompt to show to the user.
+    options: The options to show to the user.
+
+  Yields:
+    The indexes.
+  """
+  for (index, option) in enumerate(options):
+    print('%d: %s' % (index + 1, option))
+  while True:
+    print(prompt)
+    instr = sys.stdin.readline().strip()
+    if instr.lower() == 'q':
+      break
+    try:
+      index = int(instr)
+    except ValueError:
+      print('Not a valid index! Please try again')
+      continue
+    if index < 1 or index > len(options):
+      print('Not a valid index! Please try again')
+      continue
+    yield index - 1
+
+
+def do_copy(source):
+  """Copies the given source into the destination directory."""
+  destination = destination_path(flatten_name(relative_source_name(source)))
+  if os.path.exists(destination):
+    print('File already exists: ' + util.printable_path(destination))
+  else:
+    print('Copying %s to %s' %
+          (util.printable_path(source), util.printable_path(destination)))
+    shutil.copyfile(source, destination)
+
+
+def main():
+  check_destination()
+  sources = find_all_log_files()
+  for index in get_indexes_from_user(
+      'Enter the number of the file to save, or Q to quit',
+      map(relative_source_name, sources)):
+    do_copy(sources[index])
+  print('Bye!')
+
+
+main()
diff --git a/tools/testmapping/util.py b/tools/testmapping/util.py
new file mode 100644
index 0000000..b1cf937
--- /dev/null
+++ b/tools/testmapping/util.py
@@ -0,0 +1,34 @@
+#
+#   Copyright 2020 - 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.
+
+"""Common functions shared between the scripts."""
+
+import os
+
+
+def android_repository_root():
+  """Returns the root of the tree."""
+  if 'ANDROID_BUILD_TOP' not in os.environ:
+    raise Exception('Environment variable ANDROID_BUILD_TOP not set')
+  return os.environ['ANDROID_BUILD_TOP']
+
+
+def printable_path(filename):
+  """Returns the filename relative to the current dir, if it's under it."""
+  relative_path = os.path.relpath(filename)
+  if not relative_path.startswith('../'):
+    return relative_path
+  else:
+    return filename
diff --git a/tools/upstream/Android.bp b/tools/upstream/Android.bp
index dd01c30..63a88e2 100644
--- a/tools/upstream/Android.bp
+++ b/tools/upstream/Android.bp
@@ -16,6 +16,14 @@
 
 //#############################################################
 
+package {
+    // http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // the below license kinds from "libcore_license":
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["libcore_license"],
+}
+
 java_library_host {
     name: "libcore-compare-upstreams",
     srcs: ["src/main/java/**/*.java"],